6

I'm doing software development for SharePoint 2013. Part of this involves overriding SharePoint's file previewer (filepreview.debug.js becomes myfilepreview.debug.js). We've run into problems, however, with IE8. Everything works just fine in IE9.

The error thrown in IE8 causes an error on any site you visit within the site collection that our custom feature is activated on: "Object does not support this property or method"

After doing some research on this error, it would appear that IE8 simply does not support Object.create. This Mozilla Developer post seems to support this theory. I was more pressed to believe this when the issue was solved by adding this polyfill code before the line that was throwing the error:

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        function F() { }
        F.prototype = o;
        return new F();
    };
}

I guess that would make sense that it works because it mimics the functionality of Object.create which supposedly isn't supported in IE8.

However, this was confusing because SharePoint's file previewer javascript works just fine. Their javascript also uses Object.create. Even weirder, the section of code that was throwing the error in our javascript wasn't even our code -- it's SharePoint's. The entire javascript code up to then is actually the same as SharePoint's as well, except for a few lines.

Here's the default, up to the line in question:

function $_global_filepreview() {
RegisterSod("mediaplayer.js", "_layouts/15/mediaplayer.js");
RegisterSod("sp.publishing.resources.resx", "/_layouts/15/ScriptResx.ashx?name=sp.publishing.resources&culture=" + STSHtmlEncode(Strings.STS.L_CurrentUICulture_Name));
RegisterSodDep("mediaplayer.js", "sp.publishing.resources.resx");
previewBase = (function() {
ULS7RK:
    ;
    var filePreviewUniqueId = 0;

    return {
        init: function(ctxT, listItem, extension) {
            this.fpId = ++filePreviewUniqueId;
            this.fpDomId = "FilePreviewID-" + String(this.fpId);
            this.fpCtx = ctxT;
            this.fpExtension = extension;
            this.fpListItem = listItem;
        },
        getHtml: function() {
        ULS7RK:
            ;
            return ['<div class="js-filePreview-containingElement" id=', StAttrQuote(this.fpDomId), '>', this.getInnerHtml(), '</div>'].join("");
        },
        getDomId: function() {
        ULS7RK:
            ;
            return this.fpDomId;
        },
        getContainingElement: function() {
        ULS7RK:
            ;
            var containingElement = document.getElementById(this.fpDomId);

            Sys.Debug.assert(m$.isElement(containingElement), "Containing element has not been rendered yet.");
            return containingElement;
        },
        canRender: function() {
        ULS7RK:
            ;
            return true;
        },
        getLoadingIndicatorHtml: function(customStyle) {
            if (m$.isUndefined(customStyle)) {
                customStyle = "";
            }
            return ['<div class="js-filePreview-loading-image" style="width:', this.getWidth(), 'px; height:', this.getHeight(), 'px; line-height:', this.getHeight(), 'px; text-align:center; vertical-align:middle; display: inline-block; ' + customStyle + '">', '<img src="', "/_layouts/15/images/gears_anv4.gif", '" />', '</div>'].join("");
        },

        hideLoadingIndicator: function() {
        ULS7RK:
            ;
            var containingElement = document.getElementById(this.fpDomId);

            ((m$(containingElement)).find("div.js-filePreview-loading-image")).css({
                display: "none"
            });
        },

        getInnerHtml: function() {
        ULS7RK:
            ;
            return "";
        },
        getWidth: function() {
        ULS7RK:
            ;
            return null;
        },
        getHeight: function() {
        ULS7RK:
            ;
            return null;
        },
        onPostRender: function() {
        },
        onVisible: function() {
        },
        onHidden: function() {
        }
    };
})();
//**This is where the "fix" was added originally**
blankPreview = Object.create(previewBase); <--- error is thrown here

The only difference between the SharePoint javscript (above) and ours (up to this point) is we've removed these following two lines from the beginning, though adding them back still doesn't solve the issue:

RegisterSod("sp.publishing.resources.resx", "/_layouts/15/ScriptResx.ashx?name=sp.publishing.resources&culture=" + STSHtmlEncode(Strings.STS.L_CurrentUICulture_Name));
RegisterSodDep("mediaplayer.js", "sp.publishing.resources.resx");

So my question is: Why am I getting this error that Object.create isn't supported in IE8 while other uses of the same exact function in the default javascript work without issue?

EDIT: A user on another forum suggested I try registering my script via sod. I added this to my code without effect:

RegisterSod("MyFilepreview.debug.js", "_layouts/15/MyFilepreview.debug.js");
Pavlo
  • 43,301
  • 14
  • 77
  • 113
tnw
  • 13,521
  • 15
  • 70
  • 111
  • 1
    Well the Sherlock Holmes thing to do would be to conclude that one of those two lines is causing a similar shim to be imported into the page. Of course, if that were true, failure after adding the lines back (resulting in you having *exactly* the same code) would make no sense. Thus, one might question *that* assertion: are those really the *only* differences? – Pointy Jan 21 '13 at 14:21
  • @Pointy A very valid question indeed. I've double and triple checked and it it really is the only difference up to that point. – tnw Jan 21 '13 at 14:35
  • The sharepoint server may do browser sniffing, and thus return different JS file for different browsers. Did you save this javascript code with IE8 or with a different browser (or did you find it on the server side)? – molnarg Jan 23 '13 at 16:20
  • @molnarg Interesting suggestion, though I've confirmed that this code is exactly the same for both IE8 and IE9. I found it using F12 debugging. Additionally, I've pulled up the file in the 15 hive on the server and confirmed it is also the same file there. – tnw Jan 23 '13 at 16:25
  • @tnw try substituting `Object.create` with a simple `new previewBase()`. Does that throw the same error? – Jibi Abraham Jan 30 '13 at 12:12
  • @JibiAbraham you can't do that, the first argument to `Object.create` is not a constructor function that can be called with `new`, but an object (usually plain) that becomes the prototype of the created object. – Esailija Jan 30 '13 at 13:40
  • Try replacing `Object.create(obj)` with `(function(){var F = function(){};F.prototype=obj;new F();}())` – xavierm02 Jan 30 '13 at 16:08

2 Answers2

3

Try debugging in IE and put breakpoints in SharePoint's default JavaScript file. Are you sure the instances of Object.create in the default JavaScript are actually being hit?

wjhguitarman
  • 1,063
  • 1
  • 9
  • 27
3

They aren't using Object.create with proper arguments.

They use Object.create({name:value}) when it should be Object.create({name:{value:value}}).

So they probably defined their own Object.create and their code is used after yours so you already have your Object.create set when their code runs and they probably test for existence just like you do and assume it's their version that is there when in fact it's yours.

So check for a definition of Object.create in their code and check the order in which scripts are executed.

xavierm02
  • 8,457
  • 1
  • 20
  • 24
  • Thanks for your comment. After exploring around a bit, I found that there is indeed a SharePoint native javascript file that does the exact same polyfill that I do. Using F12 debugging, I can see that it IS loaded alongside mine -- so now I guess the question is: why is my javascript not able to pick up on this while theirs is? – tnw Jan 28 '13 at 14:46
  • Did you check what `Object.create` contains when you polyfill it? You know it's not a function. Try printing it. Or at least its type. – xavierm02 Jan 28 '13 at 17:10
  • 1
    It is the second argument that is a property descriptor, not the first one. The first one is the prototype object for the created object, it's perfectly fine as `Object.create({name:value})` – Esailija Jan 30 '13 at 13:42