UNPKG

@openui5/sap.m

Version:

OpenUI5 UI Library sap.m

607 lines (546 loc) 18.1 kB
/*! * OpenUI5 * (c) Copyright 2009-2023 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ // Provides control sap.m.PDFViewer. sap.ui.define([ "./library", "sap/ui/core/Control", "sap/ui/Device", "sap/m/PDFViewerRenderManager", "sap/m/MessageBox", "sap/m/PDFViewerRenderer", "sap/base/Log", "sap/base/assert", "sap/ui/thirdparty/jquery", "./PDFViewerRenderer" ], function( library, Control, Device, PDFViewerRenderManager, MessageBox, PDFViewerRenderer, Log, assert, jQuery, PDFViewerRenderer1 ) { "use strict"; var PDFViewerDisplayType = library.PDFViewerDisplayType; /** * Definition of PDFViewer control * * @param {string} [sId] id for the new control, generated automatically if no id is given * @param {object} [mSettings] initial settings for the new control * * @class * <p>This control enables you to display PDF documents within your app. * It can be embedded in your user interface layout, or you can set it to open in a popup dialog.</p> * <p>Please note that the PDF Viewer control can be fully displayed on desktop devices only. On mobile * devices, only the toolbar with a download button is visible.</p> * @extends sap.ui.core.Control * * @author SAP SE * @version 1.117.4 * @since 1.48 * * @constructor * @public * @alias sap.m.PDFViewer * @see {@link topic:cd80a8bca4ac450b86547d78f0653330 PDF Viewer} */ var PDFViewer = Control.extend("sap.m.PDFViewer", /** @lends sap.m.PDFViewer.prototype */ { metadata: { library: "sap.m", properties: { /** * Defines the height of the PDF viewer control, respective to the height of * the parent container. Can be set to a percent, pixel, or em value. */ height: {type: "sap.ui.core.CSSSize", group: "Dimension", defaultValue: "100%"}, /** * Defines the width of the PDF viewer control, respective to the width of the * parent container. Can be set to a percent, pixel, or em value. */ width: {type: "sap.ui.core.CSSSize", group: "Dimension", defaultValue: "100%"}, /** * Specifies the path to the PDF file to display. Can be set to a relative or * an absolute path.<br> * Optionally, this property can also be set to a data URI path or a blob URL * in all major web browsers except Internet Explorer and Microsoft Edge, provided * that this data URI or blob URL is allowed in advance. For more information about * URL filtering, see {@link topic:91f3768f6f4d1014b6dd926db0e91070 URLList Validator Filtering}. */ source: {type: "sap.ui.core.URI", group: "Misc", defaultValue: null}, /** * A custom error message that is displayed when the PDF file cannot be loaded. * @deprecated As of version 1.50.0, replaced by {@link sap.m.PDFViewer#getErrorPlaceholderMessage}. */ errorMessage: {type: "string", group: "Misc", defaultValue: null, deprecated: true}, /** * A custom text that is displayed instead of the PDF file content when the PDF * file cannot be loaded. */ errorPlaceholderMessage: {type: "string", group: "Misc", defaultValue: null}, /** * A custom title for the PDF viewer popup dialog. Works only if the PDF viewer * is set to open in a popup dialog. * @deprecated As of version 1.50.0, replaced by {@link sap.m.PDFViewer#getTitle}. */ popupHeaderTitle: {type: "string", group: "Misc", defaultValue: null, deprecated: true}, /** * A custom title for the PDF viewer. */ title: {type: "string", group: "Misc", defaultValue: null}, /** * Shows or hides the download button. */ showDownloadButton: {type: "boolean", group: "Misc", defaultValue: true}, /** * Defines how the PDF viewer should be displayed. * <ul> * <li>If set to <code>Link</code>, the PDF viewer appears as a toolbar with a download * button that can be used to download the PDF file.<br> * When the {@link #open} method is called, the user can either open the PDF file in a * new tab or download it.</li> * <li>If set to <code>Embedded</code>, the PDF viewer appears embedded in the parent * container and displays either the PDF document or the message defined by the * <code>errorPlaceholderMessage</code> property.</li> * <li>If set to <code>Auto</code>, the appearance of the PDF viewer depends on the * device being used: * <ul> * <li>On mobile devices (phones, tablets), the PDF viewer appears as a toolbar with * a download button.</li> * <li>On desktop devices, the PDF viewer is embedded in its parent container.</li> * </ul> * </li> * </ul> */ displayType: {type: "sap.m.PDFViewerDisplayType", group: "Misc", defaultValue: PDFViewerDisplayType.Auto} }, aggregations: { /** * A custom control that can be used instead of the error message specified by the * errorPlaceholderMessage property. */ errorPlaceholder: {type: "sap.ui.core.Control", multiple: false}, /** * A multiple aggregation for buttons that can be added to the footer of the popup * dialog. Works only if the PDF viewer is set to open in a popup dialog. */ popupButtons: {type: "sap.m.Button", multiple: true, singularName: "popupButton"} }, events: { /** * This event is fired when a PDF file is loaded. If the PDF is loaded in smaller chunks, * this event is fired as often as defined by the browser's plugin. This may happen after * a couple chunks are processed. */ loaded: {}, /** * This event is fired when there is an error loading the PDF file. */ error: { parameters : { /** * The iframe element. */ target : {type: "any"} } }, /** * This event is fired when the PDF viewer control cannot check the loaded content. For * example, the default configuration of the Mozilla Firefox browser may not allow checking * the loaded content. This may also happen when the source PDF file is stored in a different * domain. * If you want no error message to be displayed when this event is fired, call the * preventDefault() method inside the event handler. */ sourceValidationFailed: {} } }, renderer: PDFViewerRenderer1 }); /** * Lifecycle method * * @private */ PDFViewer.prototype.init = function () { // helper object that holds the references of nested objects this._objectsRegister = {}; // state variable that shows the state of popup (rendering of pdf in popup requires it) this._bIsPopupOpen = false; this._initPopupControl(); this._initPopupDownloadButtonControl(); this._initPlaceholderMessagePageControl(); this._initToolbarDownloadButtonControl(); this._initOverflowToolbarControl(); this._initControlState(); }; /** * Setup state variables to default state * * @private */ PDFViewer.prototype._initControlState = function () { // state property that control if the embedded pdf should or should not rendered. this._bRenderPdfContent = true; }; PDFViewer.prototype.setWidth = function (sWidth) { this.setProperty("width", sWidth, true); var oDomRef = this.$(); if (oDomRef === null) { return this; } oDomRef.css("width", this._getRenderWidth()); return this; }; PDFViewer.prototype.setHeight = function (sHeight) { this.setProperty("height", sHeight, true); var oDomRef = this.$(); if (oDomRef === null) { return this; } oDomRef.css("height", this._getRenderHeight()); return this; }; PDFViewer.prototype.onBeforeRendering = function () { try { //unbind all iFrame events before rendering var oIframeElement = this._getIframeDOMElement(); oIframeElement.remove(); } catch (error) { Log.info(error); } }; /** * Lifecycle method * * @private */ PDFViewer.prototype.onAfterRendering = function () { var fnInitIframeElement = function () { // cant use attachBrowserEvent because it attach event to component root node (this.$()) // load event does not bubble so it has to be bind directly to iframe element var oIframeElement = this._getIframeDOMElement(); oIframeElement.on("load", this._onLoadListener.bind(this)); oIframeElement.on("error", this._onErrorListener.bind(this)); }.bind(this); try { this.setBusy(true); fnInitIframeElement(); } catch (error) { Log.error(error); this.setBusy(false); } }; /** * @private */ PDFViewer.prototype._fireErrorEvent = function (oEventTarget) { this._renderErrorState(); this.fireError({ target: oEventTarget || null }); }; /** * @private */ PDFViewer.prototype._renderErrorState = function () { var oDownloadButton = this._objectsRegister.getToolbarDownloadButtonControl(); oDownloadButton.setEnabled(false); var oDownloadButton = this._objectsRegister.getPopupDownloadButtonControl(); oDownloadButton.setEnabled(false); this.setBusy(false); this._bRenderPdfContent = false; // calls controls invalidate because the error state should be render. // It is controlled by the state variable called _bRenderPdfContent // The main invalidate set the state of the control to the default and tries to load and render pdf Control.prototype.invalidate.call(this); }; /** * @private */ PDFViewer.prototype._fireLoadedEvent = function () { this._bRenderPdfContent = true; this.setBusy(false); try { this._getIframeDOMElement().removeClass("sapMPDFViewerLoading"); } catch (err) { Log.fatal("Iframe not founded in loaded event"); Log.fatal(err); } this.fireEvent("loaded"); }; /** * @param oEvent * @private */ PDFViewer.prototype._onLoadListener = function (oEvent) { try { var oTarget = jQuery(oEvent.target), bContinue = true; // Firefox // https://bugzilla.mozilla.org/show_bug.cgi?id=911444 // because of the embedded pdf plugin in firefox it is not possible to check contentType of the iframe document // if the content is pdf. If the content is not a pdf and it is from the same origin, it can be accessed. // Other browsers allow access to the mimeType of the iframe's document if the content is from the same origin. var sCurrentContentType = "application/pdf"; try { // browsers render pdf in iframe as html page with embed tag var aEmbeds = oTarget[0].contentWindow.document.embeds; bContinue = !!aEmbeds && aEmbeds.length === 1; if (bContinue) { sCurrentContentType = aEmbeds[0].attributes.getNamedItem("type").value; } } catch (error) { // even though the sourceValidationFailed event is fired, the default behaviour is to continue. // when preventDefault is on event object is called, the rendering ends up with error if (!Device.browser.firefox && this.fireEvent("sourceValidationFailed", {}, true)) { this._fireLoadedEvent(); return; } } if (bContinue && PDFViewerRenderer._isSupportedMimeType(sCurrentContentType) && PDFViewerRenderer._isPdfPluginEnabled()) { this._fireLoadedEvent(); } else { this._fireErrorEvent(oEvent.target); } } catch (error) { Log.fatal(false, "Fatal error during the handling of load event happened."); Log.fatal(false, error.message); } }; /** * @private */ PDFViewer.prototype._onErrorListener = function () { this._fireErrorEvent(); }; /** * Downloads the PDF file. * * @public */ PDFViewer.prototype.downloadPDF = function () { var oWindow = window.open(this.getSource()); oWindow.opener = null; oWindow.focus(); }; /** * @param {string} oClickedButtonId * @private */ PDFViewer.prototype._onSourceValidationErrorMessageBoxCloseListener = function (oClickedButtonId) { if (oClickedButtonId === MessageBox.Action.CANCEL) { this._renderErrorState(); } else { this._fireLoadedEvent(); } }; /** * @param oEvent * @private */ PDFViewer.prototype._onAfterPopupClose = function (oEvent) { var oPopup = this._objectsRegister.getPopup(); // content has to be cleared from dom oPopup.removeAllContent(); this._bIsPopupOpen = false; }; /** * @returns {boolean} * @private */ PDFViewer.prototype._shouldRenderPdfContent = function () { return PDFViewerRenderer._isPdfPluginEnabled() && this._bRenderPdfContent && this._isSourceValidToDisplay(); }; /** * @returns {boolean} * @private */ PDFViewer.prototype._isSourceValidToDisplay = function () { var sSource = this.getSource(); return sSource !== null && sSource !== "" && typeof sSource !== "undefined"; }; /** * Triggers rerendering of this element and its children. * * @param {sap.ui.base.ManagedObject} [oOrigin] Child control for which the method was called * * @public */ PDFViewer.prototype.invalidate = function (oOrigin) { this._initControlState(); Control.prototype.invalidate.call(this, oOrigin); }; /** * Opens the PDF viewer in a popup dialog. * * @public */ PDFViewer.prototype.open = function () { if (!this._isSourceValidToDisplay()) { assert(false, "The PDF file cannot be opened with the given source. Given source: " + this.getSource()); return; } else if (!PDFViewerRenderer._isPdfPluginEnabled()) { Log.warning("The PDF plug-in is not available on this device."); } if (this._isEmbeddedModeAllowed()) { this._openOnDesktop(); } else { this._openOnMobile(); } }; /** * Handles opening on desktop devices * @private */ PDFViewer.prototype._openOnDesktop = function () { var oPopup = this._objectsRegister.getPopup(); if (this._bIsPopupOpen) { return; } this._initControlState(); this._preparePopup(oPopup); oPopup.addContent(this); this._bIsPopupOpen = true; oPopup.open(); }; /** * Handles opening on mobile/tablet devices * @private */ PDFViewer.prototype._openOnMobile = function () { var oWindow = window.open(this.getSource()); oWindow.opener = null; oWindow.focus(); }; /** * Gets the iframe element from rendered DOM * @returns {*} jQuery object of iframe * @private */ PDFViewer.prototype._getIframeDOMElement = function () { var oIframeElement = this.$("iframe"); if (oIframeElement.length === 0) { throw Error("Underlying iframe was not found in DOM."); } if (oIframeElement.length > 1) { Log.fatal("Initialization of iframe fails. Reason: the control somehow renders multiple iframes"); } return oIframeElement; }; /** * @private */ PDFViewer.prototype._isEmbeddedModeAllowed = function () { //Allow Embedding only if PDFViewer plugin is present return this._isDisplayTypeAuto() ? Device.system.desktop : this._isDisplayTypeEmbedded(); }; /** * @returns {boolean} * @private */ PDFViewer.prototype._isDisplayTypeAuto = function () { return this.getDisplayType() === PDFViewerDisplayType.Auto; }; /** * @returns {boolean} * @private */ PDFViewer.prototype._isDisplayTypeEmbedded = function () { return this.getDisplayType() === PDFViewerDisplayType.Embedded; }; /** * @returns {boolean} * @private */ PDFViewer.prototype._isDisplayTypeLink = function () { return this.getDisplayType() === PDFViewerDisplayType.Link; }; /** * @returns {boolean} * @private */ PDFViewer.prototype._isDisplayDownloadButton = function () { return this.getShowDownloadButton() || this._isDisplayTypeLink() || (this._isDisplayTypeAuto() && !this._isEmbeddedModeAllowed()); }; /** * @returns {module:sap/base/i18n/ResourceBundle} * @private */ PDFViewer.prototype._getLibraryResourceBundle = function () { return sap.ui.getCore().getLibraryResourceBundle("sap.m"); }; /** * @returns {string} * @private */ PDFViewer.prototype._getMessagePageErrorMessage = function () { return this.getErrorPlaceholderMessage() ? this.getErrorPlaceholderMessage() : this._getLibraryResourceBundle().getText("PDF_VIEWER_PLACEHOLDER_ERROR_TEXT"); }; /** * @returns {string} * @private */ PDFViewer.prototype._getRenderWidth = function () { return this._bIsPopupOpen ? '100%' : this.getWidth(); }; /** * @returns {string} * @private */ PDFViewer.prototype._getRenderHeight = function () { if (this._bIsPopupOpen) { return '100%'; } if (!this._isEmbeddedModeAllowed()) { return 'auto'; } return this.getHeight(); }; /** * @private */ PDFViewer.prototype._showMessageBox = function () { MessageBox.show(this._getLibraryResourceBundle().getText("PDF_VIEWER_SOURCE_VALIDATION_MESSAGE_TEXT"), { icon: MessageBox.Icon.WARNING, title: this._getLibraryResourceBundle().getText("PDF_VIEWER_SOURCE_VALIDATION_MESSAGE_HEADER"), actions: [MessageBox.Action.OK, MessageBox.Action.CANCEL], defaultAction: MessageBox.Action.CANCEL, id: this.getId() + "-validationErrorSourceMessageBox", styleClass: "sapUiSizeCompact", contentWidth: '100px', onClose: this._onSourceValidationErrorMessageBoxCloseListener.bind(this) }); }; /** * Lifecycle method * @private */ PDFViewer.prototype.exit = function () { jQuery.each(this._objectsRegister, function (iIndex, fnGetObject) { var oObject = fnGetObject(true); if (oObject) { oObject.destroy(); } }); try { //unbind all iFrame events before rendering var oIframeElement = this._getIframeDOMElement(); oIframeElement.off(); } catch (error) { Log.info(error); } }; PDFViewerRenderManager.extendPdfViewer(PDFViewer); return PDFViewer; });