diff --git a/.vscode/extensions.json b/.vscode/extensions.json index ab5da4d838..89167f14bb 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -10,6 +10,7 @@ "formulahendry.auto-complete-tag", "formulahendry.auto-rename-tag", "mrmlnc.vscode-duplicate", - "ms-python.python" + "ms-python.python", + "wix.vscode-import-cost" ] } diff --git a/packages/client/src/Shell.jsx b/packages/client/src/Shell.jsx index 9523d6901a..bbc0834a4a 100644 --- a/packages/client/src/Shell.jsx +++ b/packages/client/src/Shell.jsx @@ -88,6 +88,7 @@ const Shell = ({ items = [], page = "home", ...rest }) => { version, pagesWithoutNavMenu, isFrame, + barTypeInFrame, } = rest; const theme = useTheme(); @@ -457,7 +458,7 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
- {!isFrame && } + {barTypeInFrame !== "none" && }
@@ -565,6 +566,7 @@ const ShellWrapper = inject( version, pagesWithoutNavMenu, isFrame, + barTypeInFrame: frameConfig?.showHeaderBanner, }; }, )(observer(Shell)); diff --git a/packages/client/src/components/AccessSelector/index.tsx b/packages/client/src/components/AccessSelector/index.tsx index f68d953ca8..127938ec6b 100644 --- a/packages/client/src/components/AccessSelector/index.tsx +++ b/packages/client/src/components/AccessSelector/index.tsx @@ -146,7 +146,7 @@ const AccessSelector: React.FC = ({ directionX="right" directionY="top" fixedDirection - manualWidth="fit-content" + manualWidth="auto" isDefaultMode isAside={isMobileView} setIsOpenItemAccess={setIsOpenItemAccess} diff --git a/packages/client/src/components/MainBar/index.js b/packages/client/src/components/MainBar/index.js index 1c264ce909..f7d4f03337 100644 --- a/packages/client/src/components/MainBar/index.js +++ b/packages/client/src/components/MainBar/index.js @@ -63,7 +63,7 @@ const MainBar = ({ snackbarExist, setMaintenanceExist, isNotPaidPeriod, - isFrame, + barTypeInFrame, }) => { const { pathname } = useLocation(); @@ -72,7 +72,7 @@ const MainBar = ({ }, []); const isVisibleBar = - !isFrame && + barTypeInFrame !== "none" && !isNotPaidPeriod && !pathname.includes("error") && !pathname.includes("confirm") && @@ -95,7 +95,7 @@ export default inject( filesStore, currentTariffStatusStore, }) => { - const { checkedMaintenance, setMaintenanceExist, snackbarExist, isFrame } = + const { checkedMaintenance, setMaintenanceExist, snackbarExist, frameConfig } = settingsStore; const { isNotPaidPeriod } = currentTariffStatusStore; const { firstLoad } = clientLoadingStore; @@ -107,7 +107,7 @@ export default inject( snackbarExist, setMaintenanceExist, isNotPaidPeriod, - isFrame, + barTypeInFrame: frameConfig?.showHeaderBanner }; }, )(observer(MainBar)); diff --git a/packages/client/src/components/QuotaForm/index.js b/packages/client/src/components/QuotaForm/index.js index 1305ef9e62..b34856cce8 100644 --- a/packages/client/src/components/QuotaForm/index.js +++ b/packages/client/src/components/QuotaForm/index.js @@ -218,7 +218,7 @@ const QuotaForm = ({ size="content" onSelect={onSelectComboBox} showDisabledItems - manualWidth={"fit-content"} + manualWidth="auto" directionY="both" /> diff --git a/packages/client/src/components/SpaceQuota/index.js b/packages/client/src/components/SpaceQuota/index.js index 182b955112..34a6cd2663 100644 --- a/packages/client/src/components/SpaceQuota/index.js +++ b/packages/client/src/components/SpaceQuota/index.js @@ -196,7 +196,7 @@ const SpaceQuota = (props) => { size="content" modernView isLoading={isLoading} - manualWidth="fit-content" + manualWidth="auto" directionY="both" /> diff --git a/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/ThirdPartyStorage/ThirdPartyComboBox.js b/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/ThirdPartyStorage/ThirdPartyComboBox.js index 41f5e19848..96c6867649 100644 --- a/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/ThirdPartyStorage/ThirdPartyComboBox.js +++ b/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/ThirdPartyStorage/ThirdPartyComboBox.js @@ -239,7 +239,7 @@ const ThirdPartyComboBox = ({ scaled withBackdrop={isMobile} size="content" - manualWidth={"fit-content"} + manualWidth={"auto"} isMobileView={isMobileOnly} directionY="both" displaySelectedOption diff --git a/packages/client/src/components/dialogs/DataReassignmentDialog/sub-components/Body/AccountInfo.js b/packages/client/src/components/dialogs/DataReassignmentDialog/sub-components/Body/AccountInfo.js index 132b9aa0f8..be18dd934e 100644 --- a/packages/client/src/components/dialogs/DataReassignmentDialog/sub-components/Body/AccountInfo.js +++ b/packages/client/src/components/dialogs/DataReassignmentDialog/sub-components/Body/AccountInfo.js @@ -31,7 +31,7 @@ import { Avatar } from "@docspace/shared/components/avatar"; import CatalogSpamIcon from "PUBLIC_DIR/images/catalog.spam.react.svg"; import { commonIconsStyles } from "@docspace/shared/utils"; -import { capitalize } from "lodash"; +import capitalize from "lodash/capitalize"; const StyledCatalogSpamIcon = styled(CatalogSpamIcon)` ${commonIconsStyles} diff --git a/packages/client/src/components/dialogs/EditGroupMembersDialog/sub-components/GroupMember/index.tsx b/packages/client/src/components/dialogs/EditGroupMembersDialog/sub-components/GroupMember/index.tsx index 7925f15b08..52304481fe 100644 --- a/packages/client/src/components/dialogs/EditGroupMembersDialog/sub-components/GroupMember/index.tsx +++ b/packages/client/src/components/dialogs/EditGroupMembersDialog/sub-components/GroupMember/index.tsx @@ -174,7 +174,7 @@ const GroupMember = ({ member, infoPanelSelection }: GroupMemberProps) => { size="content" modernView title={t("Common:Role")} - manualWidth={"fit-content"} + manualWidth="auto" isMobileView={isMobileOnly} directionY="both" displaySelectedOption diff --git a/packages/client/src/helpers/people-helpers.js b/packages/client/src/helpers/people-helpers.js index 8e0534d72d..cf71235b6d 100644 --- a/packages/client/src/helpers/people-helpers.js +++ b/packages/client/src/helpers/people-helpers.js @@ -27,7 +27,8 @@ import React from "react"; import { Trans } from "react-i18next"; import MailReactSvgUrl from "PUBLIC_DIR/images/mail.react.svg?url"; -import { find, cloneDeep } from "lodash"; +import cloneDeep from "lodash/cloneDeep"; +import find from "lodash/find"; import { EmployeeActivationStatus, EmployeeStatus, diff --git a/packages/client/src/pages/Home/InfoPanel/Body/views/Accounts/index.js b/packages/client/src/pages/Home/InfoPanel/Body/views/Accounts/index.js index 9988810e25..48e7058160 100644 --- a/packages/client/src/pages/Home/InfoPanel/Body/views/Accounts/index.js +++ b/packages/client/src/pages/Home/InfoPanel/Body/views/Accounts/index.js @@ -176,7 +176,7 @@ const Accounts = (props) => { size="content" displaySelectedOption modernView - manualWidth={"fit-content"} + manualWidth="auto" isLoading={isLoading} /> ); diff --git a/packages/client/src/pages/Home/InfoPanel/Body/views/Members/User.js b/packages/client/src/pages/Home/InfoPanel/Body/views/Members/User.js index d54dcc0ea3..5ae7ecbc74 100644 --- a/packages/client/src/pages/Home/InfoPanel/Body/views/Members/User.js +++ b/packages/client/src/pages/Home/InfoPanel/Body/views/Members/User.js @@ -378,7 +378,7 @@ const User = ({ size="content" modernView title={t("Common:Role")} - manualWidth={"fit-content"} + manualWidth="auto" isLoading={isLoading} isMobileView={isMobileOnly} directionY="both" diff --git a/packages/client/src/pages/Home/Section/AccountsBody/InsideGroup/TableView/TableRow.js b/packages/client/src/pages/Home/Section/AccountsBody/InsideGroup/TableView/TableRow.js index bc198c6201..9c4ca6f5aa 100644 --- a/packages/client/src/pages/Home/Section/AccountsBody/InsideGroup/TableView/TableRow.js +++ b/packages/client/src/pages/Home/Section/AccountsBody/InsideGroup/TableView/TableRow.js @@ -399,7 +399,7 @@ const InsideGroupTableRow = (props) => { directionY="both" size="content" modernView - manualWidth={"fit-content"} + manualWidth="auto" isLoading={isLoading} optionStyle={{ maxWidth: "400px" }} textOverflow @@ -442,7 +442,7 @@ const InsideGroupTableRow = (props) => { size="content" displaySelectedOption modernView - manualWidth={"fit-content"} + manualWidth="auto" isLoading={isLoading} /> ); diff --git a/packages/client/src/pages/Home/Section/AccountsBody/People/TableView/TableRow.js b/packages/client/src/pages/Home/Section/AccountsBody/People/TableView/TableRow.js index 45c74f06fc..7352bb5612 100644 --- a/packages/client/src/pages/Home/Section/AccountsBody/People/TableView/TableRow.js +++ b/packages/client/src/pages/Home/Section/AccountsBody/People/TableView/TableRow.js @@ -438,7 +438,7 @@ const PeopleTableRow = (props) => { size="content" displaySelectedOption modernView - manualWidth={"fit-content"} + manualWidth={"auto"} isLoading={isLoading} /> ); diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/RowView/UsersTypeRowContent.js b/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/RowView/UsersTypeRowContent.js index 49978069f3..bec9750871 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/RowView/UsersTypeRowContent.js +++ b/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/RowView/UsersTypeRowContent.js @@ -131,7 +131,7 @@ const UsersRowContent = ({ size="content" displaySelectedOption modernView - manualWidth="fit-content" + manualWidth="auto" /> , diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/TableView/UsersTypeTableRow.js b/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/TableView/UsersTypeTableRow.js index 9cfb47cc25..4c59bdece7 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/TableView/UsersTypeTableRow.js +++ b/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/TableView/UsersTypeTableRow.js @@ -126,7 +126,7 @@ const UsersTypeTableRow = ({ size="content" displaySelectedOption modernView - manualWidth="fit-content" + manualWidth="auto" /> diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/index.js b/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/index.js index b84e8ce76a..e644d45ba3 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/index.js +++ b/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/index.js @@ -219,7 +219,7 @@ const GoogleWorkspace = ({ return clearCheckedAccounts; }, []); - if (isMobile || isMobileBreakpoint()) + if (isMobileBreakpoint()) return ( , diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/NextCloudWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/TableView/UsersTableRow.js b/packages/client/src/pages/PortalSettings/categories/data-import/NextCloudWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/TableView/UsersTableRow.js index 9cfb47cc25..4c59bdece7 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/NextCloudWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/TableView/UsersTableRow.js +++ b/packages/client/src/pages/PortalSettings/categories/data-import/NextCloudWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/TableView/UsersTableRow.js @@ -126,7 +126,7 @@ const UsersTypeTableRow = ({ size="content" displaySelectedOption modernView - manualWidth="fit-content" + manualWidth="auto" /> diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/NextCloudWorkspace/index.js b/packages/client/src/pages/PortalSettings/categories/data-import/NextCloudWorkspace/index.js index da1a8f2a9f..ddde6ae9e1 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/NextCloudWorkspace/index.js +++ b/packages/client/src/pages/PortalSettings/categories/data-import/NextCloudWorkspace/index.js @@ -133,7 +133,7 @@ const NextcloudWorkspace = (props) => { return clearCheckedAccounts; }, []); - if (isMobile || isMobileBreakpoint()) + if (isMobileBreakpoint()) return ( , diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/TableView/UsersTypeTableRow.js b/packages/client/src/pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/TableView/UsersTypeTableRow.js index 7382d83618..417e46d932 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/TableView/UsersTypeTableRow.js +++ b/packages/client/src/pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/Stepper/SelectUsersTypeStep/AccountsTable/TableView/UsersTypeTableRow.js @@ -126,7 +126,7 @@ const UsersTypeTableRow = ({ size="content" displaySelectedOption modernView - manualWidth="fit-content" + manualWidth="auto" /> diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/index.js b/packages/client/src/pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/index.js index e56e709566..732dd33386 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/index.js +++ b/packages/client/src/pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/index.js @@ -216,7 +216,7 @@ const OnlyofficeWorkspace = ({ return clearCheckedAccounts; }, []); - if (isMobile || isMobileBreakpoint()) { + if (isMobileBreakpoint()) { return ( props.theme.dropDown.border}; border-radius: ${(props) => props.theme.dropDown.borderRadius}; -moz-border-radius: ${(props) => props.theme.dropDown.borderRadius}; - -webkit-border-radius: ${(props) => props.theme.dropDown.borderRadius};dropDownMaxHeight + -webkit-border-radius: ${(props) => props.theme.dropDown.borderRadius}; box-shadow: ${(props) => props.theme.dropDown.boxShadow}; -moz-box-shadow: ${(props) => props.theme.dropDown.boxShadow}; -webkit-box-shadow: ${(props) => props.theme.dropDown.boxShadow}; diff --git a/packages/shared/components/link-with-dropdown/LinkWithDropdown.styled.tsx b/packages/shared/components/link-with-dropdown/LinkWithDropdown.styled.tsx index 186802cb30..adeaf4380f 100644 --- a/packages/shared/components/link-with-dropdown/LinkWithDropdown.styled.tsx +++ b/packages/shared/components/link-with-dropdown/LinkWithDropdown.styled.tsx @@ -24,7 +24,6 @@ // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode -import React from "react"; import styled, { css } from "styled-components"; import ExpanderDownReactSvg from "PUBLIC_DIR/images/expander-down.react.svg"; @@ -32,7 +31,6 @@ import { Base } from "../../themes"; import { Text } from "../text"; import { TextProps } from "../text/Text.types"; -// import { transform } from "lodash"; import { SimpleLinkWithDropdownProps, TDropdownType, diff --git a/packages/shared/components/media-viewer/sub-components/ImageViewer/index.tsx b/packages/shared/components/media-viewer/sub-components/ImageViewer/index.tsx index 388e668fae..1bbd458427 100644 --- a/packages/shared/components/media-viewer/sub-components/ImageViewer/index.tsx +++ b/packages/shared/components/media-viewer/sub-components/ImageViewer/index.tsx @@ -988,7 +988,7 @@ export const ImageViewer = ({ window.ClientConfig?.imageThumbnails && thumbnailSrc && !showOriginSrc - ? `${thumbnailSrc}&size=3840x2160` + ? `${thumbnailSrc}&size=3840x2160&view=true` : src } ref={imgRef} diff --git a/packages/shared/components/media-viewer/sub-components/ViewerPlayer/index.tsx b/packages/shared/components/media-viewer/sub-components/ViewerPlayer/index.tsx index 7fc8eb919f..e908d44628 100644 --- a/packages/shared/components/media-viewer/sub-components/ViewerPlayer/index.tsx +++ b/packages/shared/components/media-viewer/sub-components/ViewerPlayer/index.tsx @@ -24,7 +24,7 @@ // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode -import lodash from "lodash"; +import omit from "lodash/omit"; import { useGesture } from "@use-gesture/react"; import { useSpring, animated } from "@react-spring/web"; import { @@ -640,7 +640,7 @@ export const ViewerPlayer = ({ ref={videoRef} hidden={isAudio} preload="metadata" - style={lodash.omit(style, ["x", "y"])} + style={omit(style, ["x", "y"])} src={thumbnailSrc ? src : `${src}#t=0.001`} poster={posterUrl} onError={hadleError} diff --git a/packages/shared/components/selector/sub-components/AccessSelector.tsx b/packages/shared/components/selector/sub-components/AccessSelector.tsx index a7dd834da5..e501090f6e 100644 --- a/packages/shared/components/selector/sub-components/AccessSelector.tsx +++ b/packages/shared/components/selector/sub-components/AccessSelector.tsx @@ -63,7 +63,7 @@ const AccessSelector = (props: AccessSelectorProps) => { options={accessRights as TOption[]} size={ComboBoxSize.content} scaled={false} - manualWidth="fit-content" + manualWidth="auto" selectedOption={selectedAccessRight as TOption} showDisabledItems directionX="right" @@ -81,7 +81,7 @@ const AccessSelector = (props: AccessSelectorProps) => { directionX="right" directionY="top" fixedDirection={isMobileView} - manualWidth={isMobileView ? "fit-content" : `${width}px`} + manualWidth={isMobileView ? "auto" : `${width}px`} isAside={isMobileView} manualY={isMobileView ? "0px" : undefined} withoutBackground={isMobileView} diff --git a/packages/shared/components/share/sub-components/LinkRow.tsx b/packages/shared/components/share/sub-components/LinkRow.tsx index bf05ee7c76..a5e1cc4964 100644 --- a/packages/shared/components/share/sub-components/LinkRow.tsx +++ b/packages/shared/components/share/sub-components/LinkRow.tsx @@ -153,7 +153,7 @@ const LinkRow = ({ modernView type="onlyIcon" isDisabled={isExpiredLink || isLoaded} - manualWidth="fit-content" + manualWidth="auto" withBackdrop={false} /> diff --git a/packages/shared/constants/index.ts b/packages/shared/constants/index.ts index 568f8437bf..bf45a089ea 100644 --- a/packages/shared/constants/index.ts +++ b/packages/shared/constants/index.ts @@ -171,7 +171,8 @@ export const HTML_EXST = [".htm", ".mht", ".html"]; export const SYSTEM_THEME_KEY = "system_theme"; -const SDK_VERSION = "1.0.0"; +const SDK_VERSION = "1.0.1"; + export const SDK_SCRIPT_URL = typeof window !== "undefined" ? `${window.location.origin}/static/scripts/sdk/${SDK_VERSION}/api.js` diff --git a/public/scripts/sdk/1.0.1/api.js b/public/scripts/sdk/1.0.1/api.js new file mode 100644 index 0000000000..ae265191e0 --- /dev/null +++ b/public/scripts/sdk/1.0.1/api.js @@ -0,0 +1,1353 @@ +// (c) Copyright Ascensio System SIA 2009-2024 +// +// This program is a free software product. +// You can redistribute it and/or modify it under the terms +// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software +// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended +// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of +// any third-party rights. +// +// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see +// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html +// +// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021. +// +// The interactive user interfaces in modified source and object code versions of the Program must +// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3. +// +// Pursuant to Section 7(b) of the License you must retain the original Product logo when +// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under +// trademark law for use of our trademarks. +// +// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing +// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 +// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + +(function () { + const FRAME_NAME = "frameDocSpace"; + + const defaultConfig = { + src: new URL(document.currentScript.src).origin, + rootPath: "/rooms/shared/", + requestToken: null, + width: "100%", + height: "100%", + name: FRAME_NAME, + type: "desktop", // TODO: ["desktop", "mobile"] + frameId: "ds-frame", + mode: "manager", //TODO: ["manager", "editor", "viewer","room-selector", "file-selector", "system"] + id: null, + locale: null, + theme: "System", + editorType: "desktop", //TODO: ["desktop", "embedded"] + editorGoBack: true, + selectorType: "exceptPrivacyTrashArchiveFolders", //TODO: ["roomsOnly", "userFolderOnly", "exceptPrivacyTrashArchiveFolders", "exceptSortedByTagsFolders"] + showSelectorCancel: false, + showSelectorHeader: false, + showHeader: false, + showHeaderBanner: "none", //TODO: ["none", "info", "all"] + showTitle: true, + showMenu: false, + showFilter: false, + showSignOut: true, + destroyText: "", + viewAs: "row", //TODO: ["row", "table", "tile"] + viewTableColumns: "Name,Type,Tags", + checkCSP: true, + disableActionButton: false, + showSettings: false, + waiting: false, + withSearch: true, + withBreadCrumbs: true, + withSubtitle: true, + filterParam: "ALL", + buttonColor: "#5299E0", + infoPanelVisible: true, + downloadToEvent: false, + filter: { + // filterType: 0, + // type: 0, + count: 100, + page: 1, + sortorder: "descending", //TODO: ["descending", "ascending"] + sortby: "DateAndTime", //TODO: ["DateAndTime", "AZ", "Type", "Size", "DateAndTimeCreation", "Author"] + search: "", + withSubfolders: false, + }, + editorCustomization: {}, + keysForReload: [ + "src", + "rootPath", + "width", + "height", + "name", + "frameId", + "id", + "type", + "editorType", + "mode", + ], + events: { + onSelectCallback: null, + onCloseCallback: null, + onAppReady: null, + onAppError: (e) => console.log("onAppError", e), + onEditorCloseCallback: null, + onAuthSuccess: null, + onSignOut: null, + onDownload: null, + }, + }; + + const lt = //g; + const rgt = "&rt;"; + + const cspErrorText = + "The current domain is not set in the Content Security Policy (CSP) settings."; + + const validateCSP = async (targetSrc) => { + let currentSrc = window.location.origin; + + if (currentSrc.indexOf(targetSrc) !== -1) return; // skip check for the same domain + + const response = await fetch(`${targetSrc}/api/2.0/security/csp`); + const res = await response.json(); + + currentSrc = window.location.host || new URL(window.location.origin).host; // more flexible way to check + + const domains = [...res.response.domains].map((d) => { + try { + const domain = new URL(d.toLowerCase()); + const domainFull = + domain.host + (domain.pathname !== "/" ? domain.pathname : ""); + + return domainFull; + } catch { + return d; + } + }); + + const passed = domains.includes(currentSrc.toLowerCase()); + + if (!passed) throw new Error(cspErrorText); + + return; + }; + + const getConfigFromParams = () => { + const src = decodeURIComponent(document.currentScript.src); + + if (!src || !src.length) return null; + + const searchUrl = src.split("?")[1]; + let object = {}; + + if (searchUrl && searchUrl.length) { + object = JSON.parse( + `{"${searchUrl.replace(/&/g, '","').replace(/=/g, '":"')}"}`, + (k, v) => (v === "true" ? true : v === "false" ? false : v) + ); + + object.filter = defaultConfig.filter; + + for (prop in object) { + if (prop in defaultConfig.filter) { + object.filter[prop] = object[prop]; + delete object[prop]; + } + } + } + + return { ...defaultConfig, ...object }; + }; + + /** + * Represents the DocSpace class. + * @class + */ + class DocSpace { + #isConnected = false; + #frameOpacity = 0; + #callbacks = []; + #tasks = []; + #classNames = ""; + + constructor(config) { + this.config = config; + } + + /** + * Checks if any of the keys in the given array exist in the provided object. + * + * @param {Array} array - The array of keys to check. + * @param {Object} object - The object to check against. + * @returns {boolean} - Returns true if any of the keys exist in the object, otherwise false. + */ + #oneOfExistInObject = (array, object) => { + return Object.keys(object).some((k) => array.includes(k)); + }; + + /** + * Creates a loader element with the specified configuration. + * + * @param {Object} config - The configuration object for the loader. + * @param {string} config.width - The width of the loader container. + * @param {string} config.height - The height of the loader container. + * @param {string} config.src - The source path for the loader image. + * @param {string} config.theme - The theme of the loader (e.g., "Dark", "System"). + * @param {string} config.frameId - The ID of the loader frame. + * @returns {HTMLElement} The loader container element. + */ + #createLoader = (config) => { + const container = document.createElement("div"); + container.style.width = config.width; + container.style.height = config.height; + container.style.display = "flex"; + container.style.justifyContent = "center"; + container.style.alignItems = "center"; + + // const loader = document.createElement("img"); + // loader.setAttribute("src", `${config.src}/static/images/loader.svg`); + // loader.setAttribute("width", `64px`); + // loader.setAttribute("height", `64px`); + + // if ( + // config.theme === "Dark" || + // (config.theme === "System" && + // window.matchMedia("(prefers-color-scheme: dark)")) + // ) { + // container.style.backgroundColor = "#333333"; + // loader.style.filter = + // "invert(100%) sepia(100%) saturate(0%) hue-rotate(288deg) brightness(102%) contrast(102%)"; + // } + + const loader = document.createElement("div"); + const loaderClass = `${config.frameId}-loader__element`; + loader.setAttribute("class", loaderClass); + + container.appendChild(loader); + + const style = document.createElement("style"); + style.innerHTML = ` + @keyframes rotate { + 0%{ + transform: rotate(-45deg); + } + 15%{ + transform: rotate(45deg); + } + 30%{ + transform: rotate(135deg); + } + 45%{ + transform: rotate(225deg); + } + 60%, 100%{ + transform: rotate(315deg); + } + } + + .${loaderClass} { + width: 74px; + height: 74px; + + border: 4px solid rgba(51,51,51, 0.1); + border-top-color: #333333; + border-radius: 50%; + + transform: rotate(-45deg); + + position: relative; + + box-sizing: border-box; + + animation: 1s linear infinite rotate; + } + + @media (prefers-color-scheme: dark) { + .${loaderClass} { + border-color: rgba(204, 204, 204, 0.1); + border-top-color: #CCCCCC; + } + } + `; + + container.appendChild(style); + + container.setAttribute("id", config.frameId + "-loader"); + + return container; + }; + + /** + * Creates a button view based on the provided configuration. + * + * @param {Object} config - The configuration object for the button view. + * @param {string} config.buttonColor - The background color of the button. Defaults to "#5299E0". + * @param {boolean} config.buttonWithLogo - Determines whether the button should include a logo. Defaults to false. + * @param {string} config.buttonText - The text to display on the button. Defaults to "Select to DocSpace". + * @param {string} config.src - The source URL for the logo image. + * @param {string} config.frameId - The ID of the container element for the button. + * @param {Object} config.events - The event callbacks for the button. + * @param {Function} config.events.onSelectCallback - The callback function to be executed when an item is selected. + * @param {Function} config.events.onCloseCallback - The callback function to be executed when the button view is closed. + * @param {Function} config.events.onAppReady - The callback function to be executed when the DocSpace app is ready. + * @param {Function} config.events.onAppError - The callback function to be executed when an error occurs in the DocSpace app. + * @param {Function} config.events.onEditorCloseCallback - The callback function to be executed when the editor is closed. + * @param {Function} config.events.onAuthSuccess - The callback function to be executed when authentication is successful. + * @param {Function} config.events.onSignOut - The callback function to be executed when the user signs out. + * @returns {HTMLButtonElement} The created button element. + */ + #createButtonView = (config) => { + const button = document.createElement("button"); + button.style.backgroundColor = config?.buttonColor || "#5299E0"; + button.style.color = "#fff"; + button.style.padding = "0 28px"; + button.style.borderRadius = "3px"; + button.style.border = `1px solid #5299E0`; + button.style.height = "32px"; + button.style.fontWeight = "600"; + button.style.fontFamily = "Open Sans"; + button.style.cursor = "pointer"; + button.style.display = "flex"; + button.style.alignItems = "center"; + button.style.gap = "10px"; + button.style.userSelect = "none"; + button.style.borderColor = config?.buttonColor || "#5299E0"; + + const logoSrc = `${config.src}/static/images/light_small_logo.react.svg`; + + button.innerHTML = `${config?.buttonWithLogo ? `` : ""}${config?.buttonText || "Select to DocSpace"}`; + const url = new URL(document.currentScript.src); + const scriptUrl = `${url.origin}${url.pathname}`; + + const configStringify = JSON.stringify(config, function (key, val) { + return typeof val === "function" ? "" + val : val; + }) + .replace(lt, rlt) + .replace(gt, rgt); + + const windowHeight = 778, + windowWidth = 610; + + button.addEventListener("click", () => { + const winHtml = ` + + + + DocSpace + + + + +
+ + + `; + + const winUrl = URL.createObjectURL( + new Blob([winHtml], { type: "text/html" }) + ); + + window.open( + winUrl, + "_blank", + `width=${windowWidth},height=${windowHeight}` + ); + }); + + button.setAttribute("id", config.frameId + "-container"); + + return button; + }; + + /** + * Creates an iframe element based on the provided configuration. + * + * @param {Object} config - The configuration object for creating the iframe. + * @param {string} config.mode - The mode of the iframe. + * @param {Object} config.filter - The filter object for the iframe. + * @param {string} config.id - The ID of the iframe. + * @param {string} config.requestToken - The request token for the iframe. + * @param {boolean} config.withSubfolders - Indicates whether to include subfolders in the iframe. + * @param {string} config.rootPath - The root path for the iframe. + * @param {string} config.editorGoBack - The go back option for the editor iframe. + * @param {Object} config.editorCustomization - The customization object for the editor iframe. + * @param {string} config.theme - The theme for the editor iframe. + * @param {Function} config.events.onEditorCloseCallback - The callback function for the editor close event. + * @param {string} config.editorType - The type of the editor iframe. + * @param {string} config.action - The action for the viewer iframe. + * @param {string} config.src - The source URL for the iframe. + * @param {string} config.width - The width of the iframe. + * @param {string} config.height - The height of the iframe. + * @param {string} config.name - The name of the iframe. + * @param {string} config.frameId - The ID of the iframe. + * @param {string} config.type - The type of the iframe. + * @param {boolean} config.checkCSP - Indicates whether to check the Content Security Policy. + * @returns {HTMLIFrameElement} The created iframe element. + */ + #createIframe = (config) => { + const iframe = document.createElement("iframe"); + + let path = ""; + + switch (config.mode) { + case "manager": { + if (config.filter) { + if (config.id) config.filter.folder = config.id; + + const params = config.requestToken + ? { key: config.requestToken, ...config.filter } + : config.filter; + + if (!params.withSubfolders) { + delete params.withSubfolders; + } + + const urlParams = new URLSearchParams(params).toString(); + + path = `${config.rootPath}${ + config.requestToken + ? `?${urlParams}` + : `${config.id ? config.id + "/" : ""}filter?${urlParams}` + }`; + } + break; + } + + case "room-selector": { + path = `/sdk/room-selector`; + break; + } + + case "file-selector": { + path = `/sdk/file-selector?selectorType=${config.selectorType}`; + break; + } + + case "system": { + path = `/sdk/system`; + break; + } + + case "editor": { + let goBack = config.editorGoBack; + config.editorCustomization.uiTheme = config.theme; + + if (!config.id || config.id === "undefined" || config.id === "null") { + config.id = -1; //editor default wrong file id error + } + + const customization = JSON.stringify(config.editorCustomization); + + if ( + config.events.onEditorCloseCallback && + typeof config.events.onEditorCloseCallback === "function" + ) { + goBack = "event"; + } + + path = `/doceditor/?fileId=${config.id}&editorType=${config.editorType}&editorGoBack=${goBack}&customization=${customization}`; + + if (config.requestToken) { + path = `${path}&share=${config.requestToken}`; + } + + break; + } + + case "viewer": { + let goBack = config.editorGoBack; + config.editorCustomization.uiTheme = config.theme; + + if (!config.id || config.id === "undefined" || config.id === "null") { + config.id = -1; //editor default wrong file id error + } + + const customization = JSON.stringify(config.editorCustomization); + + if ( + config.events.onEditorCloseCallback && + typeof config.events.onEditorCloseCallback === "function" + ) { + goBack = "event"; + } + + path = `/doceditor/?fileId=${config.id}&editorType=${config.editorType}&action=view&editorGoBack=${goBack}&customization=${customization}`; + + if (config.requestToken) { + path = `${path}&share=${config.requestToken}`; + } + + break; + } + + default: + path = config.rootPath; + } + + iframe.src = config.src + path; + iframe.style.width = config.width; + iframe.style.height = config.height; + iframe.name = `${FRAME_NAME}__#${config.frameId}`; + iframe.id = config.frameId; + + iframe.frameBorder = 0; + iframe.allowFullscreen = true; + iframe.setAttribute("allow", "storage-access *"); + + if (config.type == "mobile") { + iframe.style.position = "fixed"; + iframe.style.overflow = "hidden"; + document.body.style.overscrollBehaviorY = "contain"; + } + + if (this.config.checkCSP) { + validateCSP(this.config.src).catch((e) => { + const html = ` + + +
+
+ +
+
+
+ +
+ + ${cspErrorText} Please add it via + + the Developer Tools section. + +
+
+ `; + iframe.srcdoc = html; + e.message && config.events.onAppError(e.message); + + this.setIsLoaded(); + }); + } + + return iframe; + }; + + /** + * Sends a message to the specified frame. + * @param {any} message - The message to be sent. + */ + #sendMessage = (message) => { + let mes = { + frameId: this.config.frameId, + type: "", + data: message, + }; + + const targetFrame = document.getElementById(this.config.frameId); + + if (targetFrame && !!targetFrame.contentWindow) { + targetFrame.contentWindow.postMessage( + JSON.stringify(mes, (key, value) => + typeof value === "function" ? value.toString() : value + ), + this.config.src + ); + } + }; + + /** + * Handles incoming messages from the server. + * + * @param {MessageEvent} e - The message event object. + */ + #onMessage = (e) => { + if (typeof e.data == "string") { + let data = {}; + + try { + data = JSON.parse(e.data); + } catch (err) { + data = {}; + } + + if (this.config.frameId !== data.frameId) { + return; + } + + switch (data.type) { + case "onMethodReturn": { + if (this.#callbacks.length > 0) { + const callback = this.#callbacks.shift(); + callback && callback(data.methodReturnData); + } + + if (this.#tasks.length > 0) { + this.#sendMessage(this.#tasks.shift()); + } + break; + } + case "onEventReturn": { + if (Object.keys(this.config).length === 0) return; + if ( + data?.eventReturnData?.event in this.config.events && + typeof this.config.events[data?.eventReturnData.event] === + "function" + ) { + this.config.events[data?.eventReturnData.event]( + data?.eventReturnData?.data + ); + } + break; + } + case "onCallCommand": { + this[data.commandName].call(this, data.commandData); + break; + } + default: + break; + } + } + }; + + /** + * Executes a method on the message bus. + * + * @param {string} methodName - The name of the method to execute. + * @param {any} params - The parameters to pass to the method. + * @param {Function} callback - The callback function to be called after the method execution. + * @returns {void} + */ + #executeMethod = (methodName, params, callback) => { + if (!this.#isConnected) { + this.config.events.onAppError( + "Message bus is not connected with frame" + ); + return; + } + + this.#callbacks.push(callback); + + const message = { + type: "method", + methodName, + data: params, + }; + + if (this.#callbacks.length !== 1) { + this.#tasks.push(message); + return; + } + + this.#sendMessage(message); + }; + + /** + * Initializes the button with the provided configuration. + * + * @param {Object} config - The configuration object for the button. + * @returns {HTMLElement} - The initialized button element. + */ + initButton(config) { + const configFull = { ...defaultConfig, ...config }; + this.config = { + ...this.config, + ...configFull, + events: { ...defaultConfig.events }, + }; + + const target = document.getElementById(this.config.frameId); + + let button = null; + + if (target) { + button = this.#createButtonView(this.config); + + this.#classNames = target.className; + + const isSelfReplace = target.parentNode.isEqualNode( + document.getElementById(this.config.frameId + "-container") + ); + + target && isSelfReplace + ? target.parentNode.replaceWith(button) + : target.replaceWith(button); + + window.addEventListener("message", this.#onMessage, false); + + this.#isConnected = true; + } + + window.DocSpace.SDK.frames = window.DocSpace.SDK.frames || []; + + window.DocSpace.SDK.frames[this.config.frameId] = this; + + return button; + } + + /** + * Initializes the frame with the provided configuration. + * + * @param {Object} config - The configuration object for the frame. + * @returns {HTMLIFrameElement} - The created iframe element. + */ + initFrame(config) { + const configFull = { ...defaultConfig, ...config }; + Object.entries(configFull).map(([key, value]) => { + if (typeof value === "string") + configFull[key] = value.replaceAll(rlt, "<").replaceAll(rgt, ">"); + }); + this.config = { ...this.config, ...configFull }; + + const target = document.getElementById(this.config.frameId); + + let iframe = null; + + if (target) { + iframe = this.#createIframe(this.config); + + iframe.style.opacity = this.#frameOpacity; + iframe.style.zIndex = 2; + iframe.style.position = "absolute"; + iframe.style.width = "100%"; + iframe.style.height = "100%"; + iframe.style.top = 0; + iframe.style.left = 0; + + const frameLoader = this.#createLoader(this.config); + + this.#classNames = target.className; + + const renderContainer = document.createElement("div"); + renderContainer.id = this.config.frameId + "-container"; + renderContainer.classList = ["frame-container"]; + renderContainer.style.position = "relative"; + renderContainer.style.width = "100%"; + renderContainer.style.height = "100%"; + + if (!this.config.waiting || this.config.mode === "system") { + renderContainer.appendChild(iframe); + } + + renderContainer.appendChild(frameLoader); + + const isSelfReplace = target.parentNode.isEqualNode( + document.getElementById(this.config.frameId + "-container") + ); + + target && isSelfReplace + ? target.parentNode.replaceWith(renderContainer) + : target.replaceWith(renderContainer); + + window.addEventListener("message", this.#onMessage, false); + + this.#isConnected = true; + } + + window.DocSpace.SDK.frames = window.DocSpace.SDK.frames || {}; + + window.DocSpace.SDK.frames[this.config.frameId] = this; + + return iframe; + } + + /** + * Initializes the manager mode. + * @param {Object} config - The configuration object. + * @returns {Promise} A promise that resolves when the frame is initialized. + */ + initManager(config = {}) { + config.mode = "manager"; + + return this.initFrame(config); + } + + /** + * Sets the loaded state of the target frame and removes the loader element. + */ + setIsLoaded() { + const targetFrame = document.getElementById(this.config.frameId); + const loader = document.getElementById(this.config.frameId + "-loader"); + + if (targetFrame) { + targetFrame.style.opacity = 1; + targetFrame.style.position = "relative"; + targetFrame.style.width = this.config.width; + targetFrame.style.height = this.config.height; + targetFrame.parentNode.style.height = "inherit"; + + if (loader) loader.remove(); + } + } + + /** + * Initializes the editor. + * @param {Object} config - The configuration object for the editor. + * @returns {Object} - The initialized frame object. + */ + initEditor(config = {}) { + config.mode = "editor"; + + return this.initFrame(config); + } + + /** + * Initializes the viewer mode. + * @param {Object} config - The configuration object. + * @returns {Promise} A promise that resolves when the viewer is initialized. + */ + initViewer(config = {}) { + config.mode = "viewer"; + + return this.initFrame(config); + } + + /** + * Initializes the room selector. + * + * @param {Object} config - The configuration object. + * @returns {Object} - The initialized frame. + */ + initRoomSelector(config = {}) { + config.mode = "room-selector"; + + return this.initFrame(config); + } + + /** + * Initializes the file selector mode. + * @param {Object} config - The configuration object. + * @returns {Object} - The initialized frame. + */ + initFileSelector(config = {}) { + config.mode = "file-selector"; + + return this.initFrame(config); + } + + /** + * Initializes the system with the given configuration. + * @param {Object} config - The configuration object. + * @returns {Promise} A promise that resolves when the system is initialized. + */ + initSystem(config = {}) { + config.mode = "system"; + + return this.initFrame(config); + } + + /** + * Destroys the frame and cleans up associated resources. + */ + destroyFrame() { + const target = document.createElement("div"); + + target.setAttribute("id", this.config.frameId); + target.innerHTML = this.config.destroyText; + target.className = this.#classNames; + + const targetFrame = document.getElementById( + this.config.frameId + "-container" + ); + + window.removeEventListener("message", this.#onMessage, false); + this.#isConnected = false; + + delete window.DocSpace.SDK.frames[this.config.frameId]; + + targetFrame?.parentNode?.replaceChild(target, targetFrame); + + this.config = {}; + } + + /** + * Retrieves a method promise. + * + * @param {string} methodName - The name of the method. + * @param {Object|null} params - The parameters for the method (optional). + * @param {boolean} withReload - Indicates whether to reload the configuration (optional). + * @returns {Promise} A promise that resolves with the method data. + */ + #getMethodPromise = (methodName, params = null, withReload = false) => { + return new Promise((resolve) => { + if (withReload) { + this.initFrame(this.config); + resolve(this.config); + } else { + this.#executeMethod(methodName, params, (data) => resolve(data)); + } + }); + }; + + /** + * Retrieves folder information. + * @returns {Promise} A promise that resolves with the folder information. + */ + getFolderInfo() { + return this.#getMethodPromise("getFolderInfo"); + } + + /** + * Retrieves the current selection. + * @returns {Promise} A promise that resolves with the current selection. + */ + getSelection() { + return this.#getMethodPromise("getSelection"); + } + + /** + * Retrieves the files from the server. + * @returns {Promise} A promise that resolves with the files. + */ + getFiles() { + return this.#getMethodPromise("getFiles"); + } + + /** + * Retrieves the folders from the server. + * @returns {Promise} A promise that resolves with the folders data. + */ + getFolders() { + return this.#getMethodPromise("getFolders"); + } + + /** + * Retrieves a list of items. + * @returns {Promise} A promise that resolves with the list of items. + */ + getList() { + return this.#getMethodPromise("getList"); + } + + /** + * Retrieves rooms based on the provided filter. + * + * @param {Object} filter - The filter object to apply when retrieving rooms. + * @returns {Promise} A promise that resolves with the retrieved rooms. + */ + getRooms(filter) { + return this.#getMethodPromise("getRooms", filter); + } + + /** + * Retrieves user information. + * @returns {Promise} A promise that resolves with the user information. + */ + getUserInfo() { + return this.#getMethodPromise("getUserInfo"); + } + + /** + * Retrieves the configuration object. + * @returns {Object} The configuration object. + */ + getConfig() { + return this.config; + } + + /** + * Retrieves the hash settings. + * @returns {Promise} A promise that resolves with the hash settings. + */ + getHashSettings() { + return this.#getMethodPromise("getHashSettings"); + } + + /** + * Sets the configuration for the API. + * + * @param {Object} newConfig - The new configuration object. + * @param {boolean} [reload=false] - Indicates whether to reload the API after setting the configuration. + * @returns {Promise} A promise that resolves when the configuration is set. + */ + setConfig(newConfig = {}, reload = false) { + if (this.#oneOfExistInObject(this.config.keysForReload, newConfig)) + reload = true; + + this.config = { ...this.config, ...newConfig }; + + return this.#getMethodPromise("setConfig", this.config, reload); + } + + /** + * Opens a modal with the specified type and options. + * + * @param {string} type - The type of the modal. + * @param {object} options - The options for the modal. + * @returns {Promise} A promise that resolves when the modal is opened. + */ + openModal(type, options) { + return this.#getMethodPromise("openModal", { type, options }); + } + + /** + * Creates a file with the specified parameters. + * + * @param {string} folderId - The ID of the folder where the file will be created. + * @param {string} title - The title of the file. + * @param {string} templateId - The ID of the template to be used for the file. + * @param {string} formId - The ID of the form associated with the file. + * @returns {Promise} A promise that resolves with the created file. + */ + createFile(folderId, title, templateId, formId) { + return this.#getMethodPromise("createFile", { + folderId, + title, + templateId, + formId, + }); + } + + /** + * Creates a new folder with the given parent folder ID and title. + * + * @param {string} parentFolderId - The ID of the parent folder. + * @param {string} title - The title of the new folder. + * @returns {Promise} A promise that resolves with the result of the createFolder operation. + */ + createFolder(parentFolderId, title) { + return this.#getMethodPromise("createFolder", { + parentFolderId, + title, + }); + } + + /** + * Creates a new room with the specified title and room type. + * @param {string} title - The title of the room. + * @param {string} roomType - The type of the room. + * @returns {Promise} A promise that resolves with the created room. + */ + createRoom(title, roomType) { + return this.#getMethodPromise("createRoom", { + title, + roomType, + }); + } + + /** + * Sets the view type for the list of items. + * + * @param {string} type - The type of view to set. + * @returns {Promise} - A promise that resolves when the view type is set. + */ + setListView(type) { + return this.#getMethodPromise("setItemsView", type); + } + + /** + * Creates a hash for the given password using the specified hash settings. + * + * @param {string} password - The password to be hashed. + * @param {object} hashSettings - The settings for the hash algorithm. + * @returns {Promise} A promise that resolves to the generated hash. + */ + createHash(password, hashSettings) { + return this.#getMethodPromise("createHash", { password, hashSettings }); + } + + /** + * Logs in a user with the provided email and password hash. + * @param {string} email - The user's email address. + * @param {string} passwordHash - The hashed password. + * @returns {Promise} A promise that resolves with the login response. + */ + login(email, passwordHash) { + return this.#getMethodPromise("login", { email, passwordHash }); + } + + /** + * Logs out the user. + * @returns {Promise} A promise that resolves when the user is logged out. + */ + logout() { + return this.#getMethodPromise("logout"); + } + + /** + * Creates a new tag with the given name. + * + * @param {string} name - The name of the tag. + * @returns {Promise} A promise that resolves when the tag is created. + */ + createTag(name) { + return this.#getMethodPromise("createTag", name); + } + + /** + * Adds tags to a room. + * + * @param {string} roomId - The ID of the room. + * @param {string[]} tags - An array of tags to add. + * @returns {Promise} A promise that resolves when the tags are added successfully. + */ + addTagsToRoom(roomId, tags) { + return this.#getMethodPromise("addTagsToRoom", { roomId, tags }); + } + + /** + * Removes tags from a room. + * + * @param {string} roomId - The ID of the room. + * @param {string[]} tags - An array of tags to be removed. + * @returns {Promise} A promise that resolves when the tags are successfully removed. + */ + removeTagsFromRoom(roomId, tags) { + return this.#getMethodPromise("removeTagsFromRoom", { roomId, tags }); + } + } + + /** + * Represents the DocSpace SDK. + * @class + */ + class DocSpaceSDK { + frames = {}; + instances = []; + + /** + * Initializes a new instance of the DocSpace class and initializes the frame. + * @param {Object} config - The configuration object for initializing the DocSpace instance. + * @returns {DocSpace} The initialized DocSpace instance. + */ + initFrame = (config) => { + const existInstance = this.instances.find( + (i) => i.config.frameId === config.frameId + ); + + if (existInstance) { + existInstance.initFrame(config); + return existInstance; + } + + const instance = new DocSpace(config); + + instance.initFrame(config); + + this.instances.push(instance); + + return instance; + }; + + /** + * Initializes a button with the provided configuration. + * @param {Object} config - The configuration object for the button. + * @returns {DocSpace} - An instance of the DocSpace class. + */ + initButton = (config) => { + const existInstance = this.instances.find( + (i) => i.config.frameId === config.frameId + ); + + if (existInstance) { + existInstance.initButton(config); + return existInstance; + } + + const instance = new DocSpace(config); + + instance.initButton(config); + + this.instances.push(instance); + + return instance; + }; + + /** + * Initializes the editor. + * + * @param {Object} config - The configuration object for the editor. + * @returns {DocSpace} The initialized DocSpace instance. + */ + initEditor = (config = {}) => { + const existInstance = this.instances.find( + (i) => i.config.frameId === config.frameId + ); + + if (existInstance) { + existInstance.initEditor(config); + return existInstance; + } + + const instance = new DocSpace(config); + + instance.initEditor(config); + + this.instances.push(instance); + + return instance; + }; + + /** + * Initializes the viewer. + * @param {Object} config - The configuration object for the viewer. + * @returns {DocSpace} - The initialized DocSpace instance. + */ + initViewer = (config = {}) => { + const existInstance = this.instances.find( + (i) => i.config.frameId === config.frameId + ); + + if (existInstance) { + existInstance.initViewer(config); + return existInstance; + } + + const instance = new DocSpace(config); + + instance.initViewer(config); + + this.instances.push(instance); + + return instance; + }; + + /** + * Initializes the room selector. + * + * @param {Object} config - The configuration object. + * @returns {DocSpace} The instance of the DocSpace class. + */ + initRoomSelector = (config = {}) => { + const existInstance = this.instances.find( + (i) => i.config.frameId === config.frameId + ); + + if (existInstance) { + existInstance.initRoomSelector(config); + return existInstance; + } + + const instance = new DocSpace(config); + + instance.initRoomSelector(config); + + this.instances.push(instance); + + return instance; + }; + + /** + * Initializes the file selector. + * + * @param {Object} config - The configuration object. + * @returns {DocSpace} The initialized DocSpace instance. + */ + initFileSelector = (config = {}) => { + const existInstance = this.instances.find( + (i) => i.config.frameId === config.frameId + ); + + if (existInstance) { + existInstance.initFileSelector(config); + return existInstance; + } + + const instance = new DocSpace(config); + + instance.initFileSelector(config); + + this.instances.push(instance); + + return instance; + }; + + /** + * Initializes the manager for DocSpace. + * @param {Object} config - The configuration object for the manager. + * @returns {DocSpace} The initialized DocSpace instance. + */ + initManager = (config = {}) => { + const existInstance = this.instances.find( + (i) => i.config.frameId === config.frameId + ); + + if (existInstance) { + existInstance.initManager(config); + return existInstance; + } + + const instance = new DocSpace(config); + + instance.initManager(config); + + this.instances.push(instance); + + return instance; + }; + + /** + * Initializes the DocSpace system. + * + * @param {Object} config - The configuration object for initializing the system. + * @returns {DocSpace} - The initialized DocSpace instance. + */ + initSystem = (config = {}) => { + const existInstance = this.instances.find( + (i) => i.config.frameId === config.frameId + ); + + if (existInstance) { + existInstance.initSystem(config); + return existInstance; + } + + const instance = new DocSpace(config); + + instance.initSystem(config); + + this.instances.push(instance); + + return instance; + }; + } + + window.DocSpace = window.DocSpace || {}; + + const config = getConfigFromParams(); + + window.DocSpace.SDK = window.DocSpace.SDK || new DocSpaceSDK(); + + if (config.init) { + config?.isButtonMode + ? window.DocSpace.SDK.initButton(config) + : window.DocSpace.SDK.initFrame(config); + } +})();