0

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.

Screeshot of BaseURL.ts

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]();
    }
  }
}
David H.
  • 47
  • 1
  • 5
  • Can you try to provide any small sample code which we can try to test with IE 11 and other browser to check the result? It can help us to get the idea about the issue. Image of the code will not help us to find the issue. – Deepak-MSFT Jul 12 '19 at 13:46
  • I added a sample code. I hope it helps. – David H. Jul 12 '19 at 14:24
  • 1
    IE does not well support URL.createObjectURL(). It is better to use window.navigator.msSaveOrOpenBlob, for non-ie, we can use URL.createObjectURL() Ref: https://stackoverflow.com/questions/24007073/open-links-made-by-createobjecturl-in-ie11 and https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL – Deepak-MSFT Jul 15 '19 at 06:46
  • Thanks you very much for this tipp. Do you know which file I have to modify? It's difficult to find details on that. – David H. Jul 15 '19 at 07:59
  • You need to find all the occurrences where you are using URL.createObjectURL() and you need to use window.navigator.msSaveOrOpenBlob there. You can try to identify the browser using JS code and try to execute the specific code for specific browser. – Deepak-MSFT Jul 15 '19 at 08:03
  • I have tried to modify the file that contains it. The problem is that the file "dist/webpartname.js.map" is created automatically; so with every change in the code the file gets replaced. I thought there would be a way to change it in a file which will not be overwritten. – David H. Jul 15 '19 at 08:13
  • For that you need to check how the file generated automatically and if there is any way to keep the changes in it or just create it like the file with this changes by default. – Deepak-MSFT Jul 15 '19 at 08:18

2 Answers2

0

I have not found any solution for this issue. Even other panorama viewers which were working as a standalone solution in the IE11, did not work in the combination of IE11, SharePoint 2016 and SPFx Webpart.

Every time it worked in the Sharepoint with iframe. After deploying it onto SharePoint, it stopped working because of exceptions. Most exceptions had to do with security.

The most promising approach was the Photo Sphere Viewer as iframe. The issue there was that the image had to be into the CDN directory with the other js- and HTML-files. Otherwise there was an exception because of security and CORS (Cross-Origin Resource Sharing).

David H.
  • 47
  • 1
  • 5
0

IE does not support URLSearchParams . Add the below snippet in your code(Polyfill), preferably in constructor():

(function (w) {

    w.URLSearchParams = w.URLSearchParams || function (searchString) {
        var self = this;
        self.searchString = searchString;
        self.get = function (name) {
            var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(self.searchString);
            if (results == null) {
                return null;
            }
            else {
                return decodeURI(results[1]) || 0;
            }
        };
        self.has = function (name) {
            var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
            if (results == null) {
                return false;
            }
            else {
                return true;
            }
        }
    }

})(window);
Harsha Vardhini
  • 692
  • 6
  • 10