سؤال

Overview

So I've written a Video.js plugin that reports back to google analytics and our custom reporting.

Each JSONP request we send back to our page is encoded using encodeURIComponent using this method. The first few fire off correctly. It then begins to error with TypeError: '[object Object]' is not a function (evaluating 'encodeURIComponent(p)'). This only happens in Safari. (I'm on safari 7.0.1 on OSX Mavericks)

I've even tried doing the whole url string using encodeURI but the samething happens to that function as well.

I've created a [JS FIDDLE][2] to demonstrate the problem. I've been unsuccessful at re-creating it with just some sample code so i've included all the relevant files in the external resources. If it does not do it, re-run the page its happening for me about 85% of the time.

Stepping through the functions

I first add the events to track

this.on('play',onPlay);
this.on('pause',onPause);

When an event fires it is caught by these functions

function onPlay( e ) {
    videoData = getVideoData();
    doTracking({
        'category': videoData.cid,
        'action': videoData.vid,
        'label': 'Play',
        'value': null
    });
}

function onPause( e ) {
    videoData = getVideoData();
    doTracking({
        'category': videoData.cid,
        'action': videoData.vid,
        'label': 'Pause',
        'value': getTime()
    });
}

Which gets video data from

function getVideoData() {
    var src = videojsRef.player().currentSrc();
    var srcSplit = src.split('/');

    var filename = srcSplit[srcSplit.length-1];
    var filenameSplit = filename.split('.');

    var cid = filenameSplit[0];
    var vid = filenameSplit[1];
    var type = filenameSplit[2];

    var returnObj = {
        'cid': cid,
        'vid': vid,
        'filename': filename
    };

    return returnObj;
}

And then calls "doTracking" which is just a helper function that calls both tracking functions.

function doTracking( opt ) {

    if ( gaType && bvReady ) { // Are both tracking types initialized
        // Send to google
        googleTrack( opt );

        // Send to BetterVideo
        bvTrack( opt );
    } else {
        queue.push( opt );
    }
}

Which calls bvTrack( opt )

function bvTrack( opt ) {
    var args = {
        pid: playerid,
        cid: opt.category,
        vcd: opt.action,
        a: opt.label,
        callback: '{callback}'          
    };

    if ( opt.value !== null ) {
        args.val = opt.value;
    }

    // Heres where the trouble starts
    new videojs.JSONP('http://jsfiddle.net/echo/jsonp/?'+serializeToQuery(args), function( response ) {
        console.log('[BV Reporting] Tracking Response: ', arguments );
    })
}

The data gets serialized in here

function serializeToQuery( obj ) {
    var str = [];
    console.log( "serializeToQuery", obj );
    for(var p in obj) {

        if ( obj.hasOwnProperty(p) ) {
            console.log( '    property', p, obj[p]);
            console.log( '        encodeURIComponent', typeof encodeURIComponent == 'function' ? 'function' : encodeURIComponent );
            console.log( '        encoded property', encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
            str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); 
        }
    }   
    return str.join("&");
}

and then passed to the d3.js inspired JSONP (which i believe i found here on SO

videojs.JSONP = function (url, callback) {
    var docHead = document.getElementsByTagName('head')[0];
    function rand() {
        var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
        c = '', i = -1;
        while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
        return c;
    }

    function create(url) {
        var e = url.match(/callback=jsonp.(\w+)/),
        c = e ? e[1] : rand();
        videojs.JSONP[c] = function(data) {
            callback(data);
            delete videojs.JSONP[c];
            docHead.removeChild(script);
        };
        return 'videojs.JSONP.' + c;
    }

    var cb = create(url),
        script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = url.replace(/(\{|%7B)callback(\}|%7D)/, cb);
        docHead.appendChild(script)
};

Output

serializeToQuery Object { 
                            a: "Pause"
                            callback: "{callback}"
                            cid: "oceans"
                            pid: "885FA551-A873-4BB9-891A-ABC08CD47D36"
                            val: 6
                            vcd: "mp4"
                        }
    property pid 885FA551-A873-4BB9-891A-ABC08CD47D36
        encodeURIComponent function
        encoded property pid=885FA551-A873-4BB9-891A-ABC08CD47D36
    property cid oceans
        encodeURIComponent function
        encoded property cid=oceans
    property vcd mp4
        encodeURIComponent function
        encoded property vcd=mp4
    property a Pause
        encodeURIComponent function
        encoded property a=Pause
    property callback {callback}
        encodeURIComponent function
        encoded property callback=%7Bcallback%7D
    property val 6
        encodeURIComponent function
        encoded property val=6

But after 2 or 3 JSONP calls, it outputs this:

serializeToQuery Object {
                            a: "Play"
                            callback: "{callback}"
                            cid: "oceans"
                            pid: "885FA551-A873-4BB9-891A-ABC08CD47D36"
                            vcd: "mp4"
                        }

    property pid 885FA551-A873-4BB9-891A-ABC08CD47D36 
        encodeURIComponent  Object {
                                        cid: "oceans"
                                        filename: "oceans.mp4"
                                        vid: "mp4"
                                    }

[Error] TypeError: '[object Object]' is not a function (evaluating 'encodeURIComponent(p)')
    serializeToQuery (videojs.bvReporting.js, line 531)
    bvTrack (videojs.bvReporting.js, line 481)
    doTracking (videojs.bvReporting.js, line 329)
    onPlay (videojs.bvReporting.js, line 113)
    ret (video.dev.js, line 769)
    dispatcher (video.dev.js, line 295)
    trigger (video.dev.js, line 529)
    trigger (video.dev.js, line 1868)
    eventHandler (video.dev.js, line 5376)
    ret (video.dev.js, line 769)
    dispatcher (video.dev.js, line 295)

As you can see encodeURIComponent is now the last object it was called with.

Any ideas?

هل كانت مفيدة؟

المحلول

In each of the tracking event listeners there is / was videoData = getVideoData() once I added var to videoData the issue ceased. Does anyone see why that may be cause? I understand it was being set to a global variable but how does encodeURIComponent get set to the object?

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top