import { Utilities } from './utilities' ; import { CustomError } from '../errors/custom.error' ; interface DialogResult { parse : boolean ; value : any ; } /**
 * Custom error type to handle API specific errors.
 */ export class DialogError extends CustomError { constructor param message param state constructor ( message , public innerError ? ) { super ( 'DialogError' , message , innerError ) ; } } /**
 * An optimized size object computed based on Screen Height & Screen Width
 */ export interface IDialogSize { width : number ; width$ : number ; height : number ; height$ : number ; } export class Dialog < T > { constructor param url param width param height constructor ( public url = location . origin , width = 1024 , height = 768 , public useTeamsDialog = false ) { if ( ! ( /^https/ . test ( url ) ) ) { throw new DialogError ( 'URL has to be loaded over HTTPS.' ) ; } this . size = this . _optimizeSize ( width , height ) ; } private readonly _windowFeatures = ',menubar=no,toolbar=no,location=no,resizable=yes,scrollbars=yes,status=no' ; private static readonly key = 'VGVtcG9yYXJ5S2V5Rm9yT0pIQXV0aA==' ; private _result : Promise < T > ; get result ( ) : Promise < T > { if ( this . _result == null ) { if ( this . useTeamsDialog ) { this . _result = this . _teamsDialog ( ) ; } else if ( Utilities . isAddin ) { this . _result = this . _addinDialog ( ) ; } else { this . _result = this . _webDialog ( ) ; } } return this . _result ; } size : IDialogSize ; private _addinDialog < T > ( ) { return new Promise ( ( resolve , reject ) => { Office . context . ui . displayDialogAsync ( this . url , { width : this . size . width$ , height : this . size . height$ } , ( result ) => { if ( result . status === Office . AsyncResultStatus . Failed ) { reject ( new DialogError ( result . error . message , result . error ) ) ; } else { let dialog = result . value as Office . DialogHandler ; dialog . addEventHandler ( Office . EventType . DialogMessageReceived , args => { let result = this . _safeParse ( args . message ) as T ; resolve ( result ) ; dialog . close ( ) ; } ) ; dialog . addEventHandler ( Office . EventType . DialogEventReceived , args => { reject ( new DialogError ( args . message , args . error ) ) ; dialog . close ( ) ; } ) ; } } ) ; } ) ; } private _teamsDialog ( ) { return new Promise ( ( resolve , reject ) => { microsoftTeams . initialize ( ) ; microsoftTeams . authentication . authenticate ( { url : this . url , width : this . size . width , height : this . size . height , failureCallback : exception => reject ( new DialogError ( 'Error while launching dialog' , exception as any ) ) , successCallback : message => resolve ( this . _safeParse ( message ) ) } ) ; } ) ; } private _webDialog ( ) { return new Promise ( ( resolve , reject ) => { try { const options = 'width=' + this . size . width + ',height=' + this . size . height + this . _windowFeatures ; window . open ( this . url , this . url , options ) ; if ( Utilities . isIEOrEdge ) { this . _pollLocalStorageForToken ( resolve , reject ) ; } else { const handler = event => { if ( event . origin === location . origin ) { window . removeEventListener ( 'message' , handler , false ) ; resolve ( this . _safeParse ( event . data ) ) ; } } ; window . addEventListener ( 'message' , handler ) ; } } catch ( exception ) { return reject ( new DialogError ( 'Unexpected error occurred while creating popup' , exception ) ) ; } } ) ; } private _pollLocalStorageForToken ( resolve , reject ) { localStorage . removeItem ( Dialog . key ) ; const POLL_INTERVAL = 400 ; let interval = setInterval ( ( ) => { try { const data = localStorage . getItem ( Dialog . key ) ; if ( ! ( data == null ) ) { clearInterval ( interval ) ; localStorage . removeItem ( Dialog . key ) ; return resolve ( this . _safeParse ( data ) ) ; } } catch ( exception ) { clearInterval ( interval ) ; localStorage . removeItem ( Dialog . key ) ; return reject ( new DialogError ( 'Unexpected error occurred in the dialog' , exception ) ) ; } } , POLL_INTERVAL ) ; } /**
     * Close any open dialog by providing an optional message.
     * If more than one dialogs are attempted to be opened
     * an exception will be created.
     */ static close ( message ? , useTeamsDialog = false ) { let parse = false ; let value = message ; if ( typeof message === 'function' ) { throw new DialogError ( 'Invalid message. Cannot pass functions as arguments' ) ; } else if ( ( ! ( value == null ) ) && typeof value === 'object' ) { parse = true ; value = JSON . stringify ( value ) ; } try { if ( useTeamsDialog ) { microsoftTeams . initialize ( ) ; microsoftTeams . authentication . notifySuccess ( JSON . stringify ( < DialogResult > { parse , value } ) ) ; } else if ( Utilities . isAddin ) { Office . context . ui . messageParent ( JSON . stringify ( < DialogResult > { parse , value } ) ) ; } else { if ( Utilities . isIEOrEdge ) { localStorage . setItem ( Dialog . key , JSON . stringify ( < DialogResult > { parse , value } ) ) ; } else if ( window . opener ) { window . opener . postMessage ( JSON . stringify ( < DialogResult > { parse , value } ) , location . origin ) ; } window . close ( ) ; } } catch ( error ) { throw new DialogError ( 'Cannot close dialog' , error ) ; } } private _optimizeSize ( desiredWidth , desiredHeight ) { const { width : screenWidth , height : screenHeight } = window . screen ; const width = this . _maxSize ( desiredWidth , screenWidth ) ; const height = this . _maxSize ( desiredHeight , screenHeight ) ; const width$ = this . _percentage ( width , screenWidth ) ; const height$ = this . _percentage ( height , screenHeight ) ; return { width$ , height$ , width , height } ; } private _maxSize ( value , max ) { return value < ( max - 30 ) ? value : max - 30 ; } private _percentage ( value , max ) { return ( value * 100 / max ) ; } private _safeParse ( data ) { try { let result = JSON . parse ( data ) as DialogResult ; if ( result . parse === true ) { return this . _safeParse ( result . value ) ; } return result . value ; } catch ( _e ) { return data ; } } }