4

I'm trying to take a screenshot of earth.nullschool.net with PhantomJS and Node.js.

This is the relevant code:

var phantom = require("phantom");
var url = "http://earth.nullschool.net/#current/wind/isobaric/10hPa/orthographic=0.00,0.00,350";
var timeout = 15000;

phantom.create(function(ph) {
    ph.createPage(function(page) {
        page.open(url, function(status) {
            setTimeout(function() {
                page.render("earth_0-0-350_#current.png", function(finished) {
                    ph.exit();
                });
            }, timeout);
        });
    });
});

I originally set the timeout variable to 5 seconds. This returned a blank image that hasn't rendered the map.

earth not rendered

I figured I needed a longer timeout to allow the page to fully load. So I have progressively made the timeout longer up to a full minute. But the output is the same. The map never renders.

After registering the event handlers (onConsoleMessage, onError, onResourceError, onResourceTimeout) I found the following error:

ERROR: TypeError: 'undefined' is not a function (evaluating 'i.bind(null,t,n)')
TRACE:
 -> http://earth.nullschool.net/js/bundle.min.js?v20150923: 84
 -> http://earth.nullschool.net/js/bundle.min.js?v20150923: 85
 -> http://earth.nullschool.net/js/bundle.min.js?v20150923: 85
 -> http://earth.nullschool.net/js/bundle.min.js?v20150923: 7 (in function "a")
 -> http://earth.nullschool.net/js/bundle.min.js?v20150923: 7
 -> http://earth.nullschool.net/js/bundle.min.js?v20150923: 84 (in function "n")
 -> http://earth.nullschool.net/js/bundle.min.js?v20150923: 64
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Sean Lynch
  • 2,852
  • 4
  • 32
  • 46

1 Answers1

3

PhantomJS 1.x has a pretty old engine and doesn't support some essential web standards. Among those is Function.prototype.bind which is not supported. This shim which works for almost all cases.

Since you generally want to enable this as soon as possible, you have to run the shim from the page.onInitialized event listener. The invocation in phantomjs-node is a little different than in plain PhantomJS:

phantom.create(function (ph) {
    ph.createPage(function (page) {
        page.set('onInitialized', function(success) {
            page.evaluate(function(){
                var isFunction = function(o) {
                  return typeof o == 'function';
                };

                var bind,
                  slice = [].slice,
                  proto = Function.prototype,
                  featureMap;

                featureMap = {
                  'function-bind': 'bind'
                };

                function has(feature) {
                  var prop = featureMap[feature];
                  return isFunction(proto[prop]);
                }

                // check for missing features
                if (!has('function-bind')) {
                  // adapted from Mozilla Developer Network example at
                  // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
                  bind = function bind(obj) {
                    var args = slice.call(arguments, 1),
                      self = this,
                      nop = function() {
                      },
                      bound = function() {
                        return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments)));
                      };
                    nop.prototype = this.prototype || {}; // Firefox cries sometimes if prototype is undefined
                    bound.prototype = new nop();
                    return bound;
                  };
                  proto.bind = bind;
                }
            });
        });

        /* remaining script */
        setTimeout(function() {
            page.render("earth_0-0-350_#current.png", function(finished) {
                ph.exit();
            });
        }, timeout);
    });
});

This is adjusted from my answer here.

Community
  • 1
  • 1
Artjom B.
  • 61,146
  • 24
  • 125
  • 222