diff --git a/apps/common/mobile/lib/controller/ContextMenu.jsx b/apps/common/mobile/lib/controller/ContextMenu.jsx
index 61ddbd04f..9b05db8a8 100644
--- a/apps/common/mobile/lib/controller/ContextMenu.jsx
+++ b/apps/common/mobile/lib/controller/ContextMenu.jsx
@@ -103,7 +103,7 @@ class ContextMenuController extends Component {
}
onApiOpenContextMenu(x, y) {
- if ( !this.state.opened ) {
+ if ( !this.state.opened && $$('.dialog.modal-in').length < 1) {
this.setState({
items: this.initMenuItems(),
extraItems: this.initExtraItems()
diff --git a/apps/common/mobile/lib/controller/Plugins.jsx b/apps/common/mobile/lib/controller/Plugins.jsx
new file mode 100644
index 000000000..d01ac617f
--- /dev/null
+++ b/apps/common/mobile/lib/controller/Plugins.jsx
@@ -0,0 +1,283 @@
+import React, { useEffect, useState } from 'react';
+import { inject, observer } from 'mobx-react';
+import { f7 } from 'framework7-react';
+import { Device } from '../../utils/device';
+
+const PluginsController = inject('storeAppOptions')(observer(props => {
+ const { storeAppOptions } = props;
+ let configPlugins = {autostart:[]},
+ serverPlugins = {autostart:[]},
+ modal,
+ iframe;
+
+ useEffect(() => {
+ const onDocumentReady = () => {
+ Common.Notifications.on('engineCreated', api => {
+ api.asc_registerCallback("asc_onPluginShow", showPluginModal);
+ api.asc_registerCallback("asc_onPluginClose", pluginClose);
+ api.asc_registerCallback("asc_onPluginResize", pluginResize);
+ api.asc_registerCallback('asc_onPluginsInit', registerPlugins);
+
+ if(!storeAppOptions.customization || storeAppOptions.plugins !== false) {
+ loadPlugins();
+ }
+ });
+
+ Common.Gateway.on('init', loadConfig);
+ };
+
+ onDocumentReady();
+
+ return () => {
+ const api = Common.EditorApi.get();
+
+ api.asc_unregisterCallback("asc_onPluginShow", showPluginModal);
+ api.asc_unregisterCallback("asc_onPluginClose", pluginClose);
+ api.asc_unregisterCallback("asc_onPluginResize", pluginResize);
+ api.asc_unregisterCallback('asc_onPluginsInit', registerPlugins);
+
+ Common.Gateway.off('init', loadConfig);
+ };
+ });
+
+ const onDlgBtnClick = e => {
+ const api = Common.EditorApi.get();
+ let index = $$(e.currentTarget).index();
+ api.asc_pluginButtonClick(index);
+ };
+
+
+ const showPluginModal = (plugin, variationIndex, frameId, urlAddition) => {
+ let isAndroid = Device.android;
+ let variation = plugin.get_Variations()[variationIndex];
+
+ if (variation.get_Visual()) {
+ let url = variation.get_Url();
+ url = ((plugin.get_BaseUrl().length == 0) ? url : plugin.get_BaseUrl()) + url;
+
+ if (urlAddition)
+ url += urlAddition;
+
+ let isCustomWindow = variation.get_CustomWindow(),
+ arrBtns = variation.get_Buttons(),
+ newBtns = [],
+ size = variation.get_Size();
+
+ if (arrBtns.length) {
+ arrBtns.forEach((b, index) => {
+ if ((storeAppOptions.isEdit || b.isViewer !== false)) {
+ newBtns[index] = {
+ text: b.text,
+ attributes: {result: index}
+ };
+ }
+ });
+ }
+
+ f7.popover.close('.document-menu.modal-in', false);
+
+ modal = f7.dialog.create({
+ title: '',
+ text: '',
+ content: '
'+'
',
+ buttons : isCustomWindow ? undefined : newBtns
+ }).open();
+
+ iframe = document.createElement("iframe");
+
+ iframe.id = frameId;
+ iframe.name = 'pluginFrameEditor';
+ iframe.width = '100%';
+ iframe.height = '100%';
+ iframe.align = "top";
+ iframe.frameBorder = 0;
+ iframe.scrolling = "no";
+ iframe.src = url;
+
+ $$('#plugin-frame').append(iframe);
+
+ modal.$el.find('.dialog-button').on('click', onDlgBtnClick);
+
+ modal.$el.css({
+ margin: '0',
+ width: '90%',
+ left: '5%',
+ height: 'auto'
+ });
+
+ modal.$el.find('.dialog-inner').css({padding: '0'});
+
+ if (Device.phone) {
+ let height = Math.min(size[1], 240);
+ modal.$el.find('#plugin-frame').css({height: height + 'px'});
+ } else {
+ let height = Math.min(size[1], 500);
+ modal.$el.find('#plugin-frame').css({height: height + 'px'});
+ }
+
+ if (isAndroid) {
+ $$('.view.collaboration-root-view.navbar-through').removeClass('navbar-through').addClass('navbar-fixed');
+ $$('.view.collaboration-root-view .navbar').prependTo('.view.collaboration-root-view > .pages > .page');
+ }
+ }
+ };
+
+ const pluginClose = plugin => {
+ if (iframe) {
+ iframe = null;
+ }
+ };
+
+ const pluginResize = size => {
+ if (Device.phone) {
+ let height = Math.min(size[1], 240);
+ modal.$el.find('#plugin-frame').css({height: height + 'px'});
+ } else {
+ let height = Math.min(size[1], 500);
+ modal.$el.find('#plugin-frame').css({height: height + 'px'});
+ }
+ };
+
+ const getPlugins = (pluginsData, fetchFunction) => {
+ if (!pluginsData || pluginsData.length < 1)
+ return Promise.resolve([]);
+
+ fetchFunction = fetchFunction || function (url) {
+ return fetch(url)
+ .then(function(response) {
+ if (response.ok) return response.json();
+ else return Promise.reject(url);
+ }).then(function(json) {
+ json.baseUrl = url.substring(0, url.lastIndexOf("config.json"));
+ return json;
+ });
+ };
+
+ let loaded = [];
+
+ return pluginsData.map(fetchFunction).reduce(function (previousPromise, currentPromise) {
+ return previousPromise
+ .then(function()
+ {
+ return currentPromise;
+ })
+ .then(function(item)
+ {
+ loaded.push(item);
+ return Promise.resolve(item);
+ })
+ .catch(function(item)
+ {
+ return Promise.resolve(item);
+ });
+
+ }, Promise.resolve())
+ .then(function ()
+ {
+ return Promise.resolve(loaded);
+ });
+ };
+
+ const loadConfig = data => {
+ configPlugins.config = data.config.plugins;
+ };
+
+ const registerPlugins = plugins => {
+ let arr = [];
+
+ plugins.forEach(item => {
+ let plugin = new Asc.CPlugin();
+
+ plugin.set_Name(item['name']);
+ plugin.set_Guid(item['guid']);
+ plugin.set_BaseUrl(item['baseUrl']);
+
+ let variations = item['variations'],
+ variationsArr = [];
+
+ variations.forEach(itemVar => {
+ let variation = new Asc.CPluginVariation();
+
+ variation.set_Description(itemVar['description']);
+ variation.set_Url(itemVar['url']);
+ variation.set_Icons(itemVar['icons']);
+ variation.set_Visual(itemVar['isVisual']);
+ variation.set_CustomWindow(itemVar['isCustomWindow']);
+ variation.set_System(itemVar['isSystem']);
+ variation.set_Viewer(itemVar['isViewer']);
+ variation.set_EditorsSupport(itemVar['EditorsSupport']);
+ variation.set_Modal(itemVar['isModal']);
+ variation.set_InsideMode(itemVar['isInsideMode']);
+ variation.set_InitDataType(itemVar['initDataType']);
+ variation.set_InitData(itemVar['initData']);
+ variation.set_UpdateOleOnResize(itemVar['isUpdateOleOnResize']);
+ variation.set_Buttons(itemVar['buttons']);
+ variation.set_Size(itemVar['size']);
+ variation.set_InitOnSelectionChanged(itemVar['initOnSelectionChanged']);
+ variation.set_Events(itemVar['events']);
+
+ variationsArr.push(variation);
+ });
+
+ plugin["set_Variations"](variationsArr);
+ arr.push(plugin);
+ });
+
+ const api = Common.EditorApi.get();
+ api.asc_pluginsRegister('', arr);
+ };
+
+ const mergePlugins = () => {
+ if (serverPlugins.plugins !== undefined && configPlugins.plugins !== undefined) {
+ let arr = [],
+ plugins = configPlugins;
+
+ if (plugins.plugins && plugins.plugins.length > 0) {
+ arr = plugins.plugins;
+ }
+
+ plugins = serverPlugins;
+
+ if (plugins.plugins && plugins.plugins.length > 0) {
+ arr = arr.concat(plugins.plugins);
+ }
+
+ registerPlugins(arr);
+ }
+ };
+
+ const loadPlugins = () => {
+ if (configPlugins.config) {
+ getPlugins(configPlugins.config.pluginsData)
+ .then(function(loaded)
+ {
+ configPlugins.plugins = loaded;
+ mergePlugins();
+ });
+ } else {
+ configPlugins.plugins = false;
+ }
+
+ let server_plugins_url = '../../../../plugins.json';
+
+ Common.Utils.loadConfig(server_plugins_url, function (obj) {
+ if (obj != 'error') {
+ serverPlugins.config = obj;
+ getPlugins(serverPlugins.config.pluginsData)
+ .then(function(loaded)
+ {
+ serverPlugins.plugins = loaded;
+ mergePlugins();
+ });
+ } else
+ serverPlugins.plugins = false;
+ });
+ };
+
+ return <>>
+}));
+
+export default PluginsController;
+
+
+
diff --git a/apps/common/mobile/lib/view/Search.jsx b/apps/common/mobile/lib/view/Search.jsx
index ae026d894..72f69836c 100644
--- a/apps/common/mobile/lib/view/Search.jsx
+++ b/apps/common/mobile/lib/view/Search.jsx
@@ -9,7 +9,8 @@ import { observable, runInAction } from "mobx";
import { observer } from "mobx-react";
const searchOptions = observable({
- usereplace: false
+ usereplace: false,
+ isReplaceAll: false
});
const popoverStyle = {
@@ -31,15 +32,20 @@ class SearchSettingsView extends Component {
searchBy: 1,
lookIn: 1,
isMatchCase: false,
- isMatchCell: false
+ isMatchCell: false,
+ isReplaceAll: false
};
}
onFindReplaceClick(action) {
- runInAction(() => searchOptions.usereplace = action == 'replace');
+ runInAction(() => {
+ searchOptions.usereplace = action == 'replace';
+ searchOptions.isReplaceAll = action == 'replace-all';
+ });
this.setState({
- useReplace: searchOptions.usereplace
+ useReplace: searchOptions.usereplace,
+ isReplaceAll: searchOptions.isReplaceAll
});
if (this.onReplaceChecked) {}
@@ -251,14 +257,15 @@ class SearchView extends Component {
render() {
const usereplace = searchOptions.usereplace;
+ const isReplaceAll = searchOptions.isReplaceAll;
const hidden = {display: "none"};
const searchQuery = this.state.searchQuery;
- const replaceQuery = this.state.replaceQuery;
+ // const replaceQuery = this.state.replaceQuery;
const isIos = Device.ios;
const { _t } = this.props;
if(this.searchbar && this.searchbar.enabled) {
- usereplace ? this.searchbar.el.classList.add('replace') : this.searchbar.el.classList.remove('replace');
+ usereplace || isReplaceAll ? this.searchbar.el.classList.add('replace') : this.searchbar.el.classList.remove('replace');
}
return (
@@ -272,22 +279,30 @@ class SearchView extends Component {
this.onSearchClick(SEARCH_BACKWARD)}>
diff --git a/apps/common/mobile/lib/view/collaboration/Collaboration.jsx b/apps/common/mobile/lib/view/collaboration/Collaboration.jsx
index 8511f9c85..f87e36b16 100644
--- a/apps/common/mobile/lib/view/collaboration/Collaboration.jsx
+++ b/apps/common/mobile/lib/view/collaboration/Collaboration.jsx
@@ -134,7 +134,7 @@ class CollaborationView extends Component {
const show_popover = this.props.usePopover;
return (
show_popover ?
- this.props.onclosed()}>
+ this.props.onclosed()} closeByOutsideClick={false}>
:
this.props.onclosed()}>
diff --git a/apps/common/mobile/lib/view/collaboration/Comments.jsx b/apps/common/mobile/lib/view/collaboration/Comments.jsx
index 2fd0dbdfa..dd1c5d031 100644
--- a/apps/common/mobile/lib/view/collaboration/Comments.jsx
+++ b/apps/common/mobile/lib/view/collaboration/Comments.jsx
@@ -292,6 +292,12 @@ const EditCommentDialog = inject("storeComments")(observer(({storeComments, comm
done.classList.remove('disabled');
}
});
+ },
+ open: () => {
+ $$('.dialog-backdrop.backdrop-in')[0].classList.add('over-popover');
+ },
+ closed: () => {
+ $$('.dialog-backdrop.backdrop-in')[0].classList.remove('over-popover');
}
}
}).open();
@@ -411,6 +417,12 @@ const AddReplyDialog = inject("storeComments")(observer(({storeComments, userInf
}
});
done.classList.add('disabled');
+ },
+ open: () => {
+ $$('.dialog-backdrop.backdrop-in')[0].classList.add('over-popover');
+ },
+ closed: () => {
+ $$('.dialog-backdrop.backdrop-in')[0].classList.remove('over-popover');
}
}
}).open();
@@ -536,6 +548,12 @@ const EditReplyDialog = inject("storeComments")(observer(({storeComments, commen
done.classList.remove('disabled');
}
});
+ },
+ open: () => {
+ $$('.dialog-backdrop.backdrop-in')[0].classList.add('over-popover');
+ },
+ closed: () => {
+ $$('.dialog-backdrop.backdrop-in')[0].classList.remove('over-popover');
}
}
}).open();
@@ -825,7 +843,7 @@ const ViewCommentPopover = ({onCommentMenuClick, onResolveComment}) => {
f7.popover.open('#view-comment-popover', '#btn-coauth');
});
return (
-