Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 27 additions & 47 deletions js/src/launchAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ let isLoading: boolean = false;
/**
* Launch the Immersive Reader within an iframe.
* @param token The authentication token.
* @param subdomain The Immersive Reader Cognitive Service subdomain. This is a required parameter for Azure AD authentication in this and future versions of this SDK. Use of the Cognitive Services issueToken endpoint-based authentication tokens is deprecated and no longer supported.
* @param subdomain The Immersive Reader Cognitive Service subdomain.
* @param content The content that should be shown in the Immersive Reader.
* @param options Options for configuring the look and feel of the Immersive Reader.
* @return A promise that resolves with a LaunchResponse when the Immersive Reader is launched.
Expand Down Expand Up @@ -87,15 +87,14 @@ export function launchAsync(token: string, subdomain: string, content: Content,
const startTime = Date.now();
options = {
uiZIndex: 1000,
timeout: 15000, // Default to 15 seconds
timeout: 15000,
useWebview: false,
allowFullscreen: true,
hideExitButton: false,
cookiePolicy: CookiePolicy.Disable,
...options
};

// Ensure that we were given a number for the UI z-index
if (!options.uiZIndex || typeof options.uiZIndex !== 'number') {
options.uiZIndex = 1000;
}
Expand All @@ -119,17 +118,11 @@ export function launchAsync(token: string, subdomain: string, content: Content,
const parent = options.parent && document.contains(options.parent) ? options.parent : document.body;

const reset = (): void => {
// Remove container along with the iframe inside of it
if (parent.contains(iframeContainer)) {
parent.removeChild(iframeContainer);
}

window.removeEventListener('message', messageHandler);

// Clear the timeout timer
resetTimeout();

// Re-enable scrolling
if (noscroll.parentNode) {
noscroll.parentNode.removeChild(noscroll);
}
Expand All @@ -138,26 +131,20 @@ export function launchAsync(token: string, subdomain: string, content: Content,
const exit = (): void => {
reset();
isLoading = false;

// Execute exit callback if we have one
if (options.onExit) {
try {
options.onExit();
} catch {
// No-op
}
} catch { /* No-op */ }
}
};

// Reset variables
reset();

const messageHandler = (e: any): void => {
// Don't process the message if the data is not a string
if (!e || !e.data || typeof e.data !== 'string') { return; }

if (e.data === 'ImmersiveReader-ReadyForContent') {
resetTimeout(); // Reset the timeout once the reader page loads successfully. The Reader page will report further errors through PostMessage if there is an issue obtaining the ContentModel from the server
resetTimeout();
const message: Message = {
cogSvcsAccessToken: token,
cogSvcsSubdomain: subdomain,
Expand All @@ -179,21 +166,33 @@ export function launchAsync(token: string, subdomain: string, content: Content,
} else if (e.data.startsWith(PostMessageLaunchResponse)) {
let launchResponse: LaunchResponse = null;
let error: Error = null;

let response: LaunchResponseMessage = null;
try {
response = JSON.parse(e.data.substring(PostMessageLaunchResponse.length));
} catch {
// No-op
}
} catch { /* No-op */ }

if (response && response.success) {

launchResponse = {
container: iframeContainer,
sessionId: response.sessionId,
charactersProcessed: response.meteredContentSize
};

/**
* WORKAROUND: Force a layout recalculation of the iframe.
* This prevents the "Cannot read properties of undefined (reading 'height')" error
* in the virtualized content pane when reading long HTML chunks.
*/
setTimeout(() => {
if (iframe) {
const originalHeight = iframe.style.height;
iframe.style.height = '99.9%';
setTimeout(() => {
iframe.style.height = originalHeight;
}, 50);
}
}, 500);

} else if (response && !response.success) {
error = {
code: response.errorCode,
Expand All @@ -219,28 +218,23 @@ export function launchAsync(token: string, subdomain: string, content: Content,
if (options.onPreferencesChanged && typeof options.onPreferencesChanged === 'function') {
try {
options.onPreferencesChanged(e.data.substring(PostMessagePreferences.length));
} catch {
// No-op
}
} catch { /* No-op */ }
}
}
};
window.addEventListener('message', messageHandler);

// Reject the promise if the Immersive Reader page fails to load.
timeoutId = window.setTimeout((): void => {
reset();
isLoading = false;
reject({ code: ErrorCode.Timeout, message: `Page failed to load after timeout (${options.timeout} ms)` });
}, options.timeout);

// Create and style iframe
if (options.allowFullscreen) {
iframe.setAttribute('allowfullscreen', '');
}
iframe.style.cssText = options.parent ? 'position: static; width: 100%; height: 100%; left: 0; top: 0; border-width: 0' : 'position: static; width: 100vw; height: 100vh; left: 0; top: 0; border-width: 0';

// Send an initial message to the webview so it has a reference to this parent window
if (options.useWebview) {
iframe.addEventListener('loadstop', () => {
iframe.contentWindow.postMessage(JSON.stringify({ messageType: 'WebviewHost' }), '*');
Expand All @@ -249,25 +243,16 @@ export function launchAsync(token: string, subdomain: string, content: Content,

const domain = options.customDomain ? options.customDomain : `https://${subdomain}.cognitiveservices.azure.com/immersivereader/webapp/v1.0/`;
let src = domain + 'reader?exitCallback=ImmersiveReader-Exit&sdkPlatform=' + sdkPlatform + '&sdkVersion=' + sdkVersion;

src += '&cookiePolicy=' + ((options.cookiePolicy === CookiePolicy.Enable) ? 'enable' : 'disable');

if (options.hideExitButton) {
src += '&hideExitButton=true';
}

if (options.uiLang) {
src += '&omkt=' + options.uiLang;
}
if (options.hideExitButton) { src += '&hideExitButton=true'; }
if (options.uiLang) { src += '&omkt=' + options.uiLang; }

iframe.src = src;

iframeContainer.style.cssText = options.parent ? `position: relative; width: 100%; height: 100%; border-width: 0; -webkit-perspective: 1px; z-index: ${options.uiZIndex}; background: white; overflow: hidden` : `position: fixed; width: 100vw; height: 100vh; left: 0; top: 0; border-width: 0; -webkit-perspective: 1px; z-index: ${options.uiZIndex}; background: white; overflow: hidden`;

iframeContainer.appendChild(iframe);
parent.appendChild(iframeContainer);

// Disable body scrolling
document.head.appendChild(noscroll);
});
}
Expand All @@ -276,15 +261,10 @@ export function close(): void {
window.postMessage('ImmersiveReader-Exit', '*');
}

// The subdomain must be alphanumeric, and may contain '-',
// as long as the '-' does not start or end the subdomain.
export function isValidSubdomain(subdomain: string): boolean {
if (!subdomain) {
return false;
}

if (!subdomain) { return false; }
const validRegex = '^([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]\.privatelink)$';
const regExp = new RegExp(validRegex);

return regExp.test(subdomain);
}
}