I need some expert knowledge about SPFx web parts in combination with SharePoint 2016 and InternetExplorer 11.
The idea is to create a web part that uses Pannellum to display images. In Firefox it's already working. With IE11, however, the error messages appear in the console:SCRIPT5022: TypeMismatchError
BaseURL.ts (16,7)
SCRIPT5007: Die Eigenschaft "toString" eines undefinierten oder Nullverweises kann nicht abgerufen werden.
Message in English: The "toString" property of an undefined or null reference cannot be retrieved.
LogEvent.js (26,1)
In the developer tools I can see that the image has been downloaded. But as the code of the webpart tries to call createObjectURL
with the image as blob object it crashes.
The webpart has been created with Yeoman (3.1.0) and tested with Gulp. The execute the script for Pannellum in the webpart I use the executeScript function from this repo: https://github.com/SharePoint/sp-dev-fx-webparts/blob/dev/samples/react-script-editor/src/webparts/scriptEditor/ScriptEditorWebPart.ts
I guess my problem has to to with this: IE + XMLHttp + CreateObjectURL Error.
Has someone experience with this?
Here some example code:
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import { escape } from '@microsoft/sp-lodash-subset';
import { SPComponentLoader } from '@microsoft/sp-loader';
import styles from './TestWebPart.module.scss';
import * as strings from 'TestWebPartStrings';
require('../../../node_modules/pannellum/build/pannellum.css');
require('../../../node_modules/pannellum/build/pannellum.js');
export default class TestWebPart extends BaseClientSideWebPart<ITestWebPartProps> {
public render(): void {
let innerHTML: string = `
<script src="http://localhost:4321/node_modules/pannellum/build/pannellum.js"></script>
<script>
pannellum.viewer('panorama', {
"type": "equirectangular",
"panorama": "test2.jpg",
"autoLoad": true
});
</script>
<style>
#panorama {
width: 600px;
height: 400px;
}
</style>
<div id="panorama"></div>
`;
this.domElement.innerHTML = innerHTML;
this.executeScript(this.domElement);
}
private evalScript(elem) {
const data = (elem.text || elem.textContent || elem.innerHTML || '');
const headTag = document.getElementsByTagName('head')[0] || document.documentElement;
const scriptTag = document.createElement('script');
scriptTag.type = 'text/javascript';
if (elem.src && elem.src.length > 0) {
return;
}
if (elem.onload && elem.onload.length > 0) {
scriptTag.onload = elem.onload;
}
try {
// doesn't work on ie...
scriptTag.appendChild(document.createTextNode(data));
} catch (e) {
// IE has funky script nodes
scriptTag.text = data;
}
headTag.insertBefore(scriptTag, headTag.firstChild);
headTag.removeChild(scriptTag);
}
private nodeName(elem, name) {
return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
}
// Finds and executes scripts in a newly added element's body.
// Needed since innerHTML does not run scripts.
//
// Argument element is an element in the dom.
private async executeScript(element: HTMLElement) {
// Define global name to tack scripts on in case script to be loaded is not AMD/UMD
if (!window['_spPageContextInfo']) {
window['_spPageContextInfo'] = this.context.pageContext.legacyPageContext;
}
(<any>window).ScriptGlobal = {};
// main section of function
const scripts = [];
const childrenNodes = element.childNodes;
for (let i: number = 0; childrenNodes[i]; i++) {
const child: any = childrenNodes[i];
if (this.nodeName(child, 'script') &&
(!child.type || child.type.toLowerCase() === 'text/javascript')) {
scripts.push(child);
}
}
const urls = [];
const onLoads = [];
for (let i: number = 0; scripts[i]; i++) {
const scriptTag = scripts[i];
if (scriptTag.src && scriptTag.src.length > 0) {
urls.push(scriptTag.src);
}
if (scriptTag.onload && scriptTag.onload.length > 0) {
onLoads.push(scriptTag.onload);
}
}
let oldamd = undefined;
if (window['define'] && window['define'].amd) {
oldamd = window['define'].amd;
window['define'].amd = undefined;
}
for (let i: number = 0; i < urls.length; i++) {
try {
let scriptUrl = urls[i];
const prefix = scriptUrl.indexOf('?') === -1 ? '?' : '&';
scriptUrl += prefix + 'cow=' + new Date().getTime();
await SPComponentLoader.loadScript(scriptUrl, { globalExportsName: 'ScriptGlobal' });
} catch (error) {
if (console.error) {
console.error(error);
}
}
}
if (oldamd) {
window['define'].amd = oldamd;
}
for (let i: number = 0; scripts[i]; i++) {
const scriptTag = scripts[i];
if (scriptTag.parentNode) { scriptTag.parentNode.removeChild(scriptTag); }
this.evalScript(scripts[i]);
}
// execute any onload people have added
for (let i: number = 0; onLoads[i]; i++) {
onLoads[i]();
}
}
}