Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mickey/videojs-ga
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: neuron-digital/videojs-ga
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Able to merge. These branches can be automatically merged.
  • 17 commits
  • 5 files changed
  • 4 contributors

Commits on Jul 23, 2016

  1. issue #28 fixed

    Dmitriy Lunkin committed Jul 23, 2016

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    1683026 View commit details

Commits on Nov 2, 2016

  1. Track played time on streams (videos with infinite duration), trigger…

    … 'gaEvent' instead of invoking ga directly
    Mikhail Khazov committed Nov 2, 2016
    Copy the full SHA
    f386630 View commit details
  2. Add autoLabel option

    Mikhail Khazov committed Nov 2, 2016
    Copy the full SHA
    6c5af22 View commit details
  3. Add sendGaEventDirectly option

    Mikhail Khazov committed Nov 2, 2016
    Copy the full SHA
    10228b1 View commit details
  4. Trigger 'start' event instead of 'firstplay'

    Mikhail Khazov committed Nov 2, 2016
    Copy the full SHA
    75504da View commit details
  5. * add secondsPlayed to eventsToTrack

    * add default values for `percentsPlayedInterval`, `secondsPlayedInterval`, `secondsPlayedMoments` options
    * do not emit `play` event on play with currentTime==0, if `start` event is tracked
    * update README
    Mikhail Khazov committed Nov 2, 2016
    Copy the full SHA
    2926f0e View commit details
  6. * add trackReplaySeconds option

    Mikhail Khazov committed Nov 2, 2016
    Copy the full SHA
    1d7bbe3 View commit details
  7. Merge branch 'gaEvent'

    Mikhail Khazov committed Nov 2, 2016
    Copy the full SHA
    8ce583c View commit details
  8. update readme

    Mikhail Khazov committed Nov 2, 2016
    Copy the full SHA
    a3fde59 View commit details
  9. add ads events

    Mikhail Khazov committed Nov 2, 2016
    Copy the full SHA
    d5ad02d View commit details

Commits on Nov 3, 2016

  1. single quotes

    Mikhail Khazov committed Nov 3, 2016
    Copy the full SHA
    003094d View commit details

Commits on Apr 21, 2017

  1. Copy the full SHA
    69281ed View commit details

Commits on Apr 24, 2017

  1. add percentsPlayedMoments option; refactor timeupdate handler; use no…

    …t rounded values for accurate calculation of percent values
    mkhazov committed Apr 24, 2017
    Copy the full SHA
    d7bb61a View commit details
  2. update README.md

    mkhazov committed Apr 24, 2017
    Copy the full SHA
    1b528c8 View commit details
  3. 0.6.0

    mkhazov committed Apr 24, 2017
    Copy the full SHA
    d436f92 View commit details
  4. Update README.md

    mkhazov authored Apr 24, 2017
    Copy the full SHA
    1b9c8fe View commit details

Commits on Apr 25, 2017

  1. Copy the full SHA
    2ff4015 View commit details
Showing with 445 additions and 142 deletions.
  1. +53 −18 README.md
  2. +207 −61 dist/videojs.ga.js
  3. +3 −3 dist/videojs.ga.min.js
  4. +7 −1 package.json
  5. +175 −59 src/videojs.ga.coffee
71 changes: 53 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
# videojs-ga

LOOKING FOR A MAINTAINER: [I am looking for a maintainer for this project](https://github.com/mickey/videojs-ga/issues/30)

Google Analytics plugin for video.js
Google Analytics plugin for [video.js](https://github.com/videojs/video.js)

## Getting Started
Download [videojs](http://www.videojs.com/) and [videojs.ga](https://github.com/mickey/videojs-ga)

In your web page:
```html
@@ -40,39 +37,77 @@ The plugin will take in priority options provided in the javascript, followed by

The following options are supported:

####eventCategory
### eventCategory

This is the `eventCategory` sent to GA. If you don't know what it is please check [GA's doc](https://developers.google.com/analytics/devguides/collection/analyticsjs/events)
**default:** `'Video'`

This is the ```category``` sent to GA. If you don't know what it is please check [GA's doc](https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide)
**default:** ```'Video'```
### eventLabel

This is the `eventLabel` sent to GA. If you don't know what it is please check [GA's doc](https://developers.google.com/analytics/devguides/collection/analyticsjs/events)
If not set, label will be generated automatically, unless `autoLabel` option is set to false.

####eventLabel
### autoLabel

This is the ```label``` sent to GA. If you don't know what it is please check [GA's doc](https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide)
**default:** basename of the video path so if the path is ```http://s3.amazonaws.com/pouet.mp4``` the label would be ```pouet```
If set to true, `eventLabel` will be generated automatically using basename of the video path.
E.g. if the path is `http://s3.amazonaws.com/pouet.mp4` the label would be `pouet`
**default:** `true`

####eventsToTrack
### eventsToTrack

The events you want to track. Most of this events are videojs events. Some of them might reflects my needs.
I'm open to add some more if you care to provide a good use case or a pull request.
**default:** every events
```[ 'loaded', 'percentsPlayed', 'start', 'end', 'seek', 'play', 'pause', 'resize', 'volumeChange', 'error', 'fullscreen']```
```[ 'loaded', 'percentsPlayed', 'secondsPlayed', 'start', 'end', 'seek', 'play', 'pause', 'resize', 'volumeChange', 'error', 'fullscreen']```

Most of the events are selft explanatory, here's the ones that may need more details:

- ```percentsPlayed```: will send an event every X percents. X being defined by the option ```percentsPlayedInterval```.
- `percentsPlayed`: will send an event every X percents. X being defined by the option `percentsPlayedInterval`.

####percentsPlayedInterval
### percentsPlayedInterval

This options goes with the ```percentsPlayed``` event. Every ```percentsPlayedInterval``` percents an event will be sent to GA.
This option goes with the `percentsPlayed` event. Every `percentsPlayedInterval` percents an event will be sent to GA.
Set this options to `false` if you want to use `percentsPlayedMoments` option and don't want to track every 10 percent of playback.
**default:** 10

####debug
### percentsPlayedMoments

This option goes with the `percentsPlayed` event. After each value of `percentsPlayedMoments` percents of the actual playback an event will be sent.
**default:** `[]`

### secondsPlayedInterval

This option goes with the `secondsPlayed` event. Every `secondsPlayedInterval` seconds of the actual playback an event will be sent to GA.
**default:** 60

### secondsPlayedMoments

This option goes with the `secondsPlayed` event. After each value of `secondsPlayedMoments` seconds of the actual playback an event will be sent to GA.
**default:** `[]`

### trackFiniteSeconds

If set to true and at least one of `secondsPlayedInterval`, `secondsPlayedMoments` options is set, `seconds played` events will be triggered.

### trackReplaySeconds
If set to true, `start` and `secondsPlayed` events will be emitted even after video has ended and then has been started again.
**default:** `false`

### sendGaEventDirectly
If set to true, plugin will send events to GA directly. Otherwise you need to listen to `gaEvent` on the player instance and then send events to GA manually, e.g.
```javascript
player.on('gaEvent', function(e, gaEvent) {
ga('send', gaEvent);
});
```
**default:** `false`

### debug

If set to false, console logs will be ommited
**default:** ```false```
**default:** `false`

#### ga.js and analytics.js
## ga.js and analytics.js

This plugin supports the ga.js and the newer analytics.js Google Analytics libraries. It autodetects the library you use.

268 changes: 207 additions & 61 deletions dist/videojs.ga.js
Original file line number Diff line number Diff line change
@@ -1,79 +1,161 @@
/*
* videojs-ga - v0.4.2 - 2015-02-06
* Copyright (c) 2015 Michael Bensoussan
* videojs-ga - v0.6.1 - 2017-04-25
* Copyright (c) 2017 Michael Bensoussan
* Licensed MIT
*/
(function() {
var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

videojs.plugin('ga', function(options) {
var dataSetupOptions, defaultsEventsToTrack, end, error, eventCategory, eventLabel, eventsToTrack, fullscreen, loaded, parsedOptions, pause, percentsAlreadyTracked, percentsPlayedInterval, play, resize, seekEnd, seekStart, seeking, sendbeacon, timeupdate, volumeChange;
var adend, adpause, adserror, adskip, adstart, adtimeout, autoLabel, dataSetupOptions, defaultsEventsToTrack, end, ended, error, eventCategory, eventLabel, eventsToTrack, firstplay, fullscreen, getCurrentTime, getCurrentValue, init, interval, isFinite, loaded, parsedOptions, pause, percentsPlayedInterval, percentsPlayedMoments, percentsTracked, play, playing, resize, secondsPlayed, secondsPlayedInterval, secondsPlayedMoments, seekEnd, seekStart, seeking, sendbeacon, startTimeTracking, stopTimeTracking, timeupdate, trackPercent, trackReplaySeconds, trackSeconds, trackSeek, trackingTime, volumeChange,
_this = this;
if (options == null) {
options = {};
}
dataSetupOptions = {};
if (this.options()["data-setup"]) {
parsedOptions = JSON.parse(this.options()["data-setup"]);
if (this.options_['data-setup']) {
parsedOptions = JSON.parse(this.options_['data-setup']);
if (parsedOptions.ga) {
dataSetupOptions = parsedOptions.ga;
}
}
defaultsEventsToTrack = ['loaded', 'percentsPlayed', 'start', 'end', 'seek', 'play', 'pause', 'resize', 'volumeChange', 'error', 'fullscreen'];
defaultsEventsToTrack = ['loaded', 'percentsPlayed', 'secondsPlayed', 'start', 'end', 'seek', 'play', 'pause', 'resize', 'volumeChange', 'error', 'fullscreen', 'adstart', 'adpause', 'adend', 'adskip', 'adtimeout', 'adserror'];
eventsToTrack = options.eventsToTrack || dataSetupOptions.eventsToTrack || defaultsEventsToTrack;
percentsPlayedInterval = options.percentsPlayedInterval || dataSetupOptions.percentsPlayedInterval || 10;
eventCategory = options.eventCategory || dataSetupOptions.eventCategory || 'Video';
autoLabel = options.autoLabel != null ? options.autoLabel : true;
eventLabel = options.eventLabel || dataSetupOptions.eventLabel;
options.debug = options.debug || false;
percentsAlreadyTracked = [];
percentsPlayedInterval = options.percentsPlayedInterval || dataSetupOptions.percentsPlayedInterval || 10;
percentsPlayedMoments = options.percentsPlayedMoments || dataSetupOptions.percentsPlayedMoments || [];
secondsPlayedInterval = options.secondsPlayedInterval || dataSetupOptions.secondsPlayedInterval || 60;
secondsPlayedMoments = options.secondsPlayedMoments || dataSetupOptions.secondsPlayedMoments || [];
trackReplaySeconds = options.trackReplaySeconds;
percentsTracked = [];
seekStart = seekEnd = 0;
seeking = false;
loaded = function() {
if (!eventLabel) {
eventLabel = this.currentSrc().split("/").slice(-1)[0].replace(/\.(\w{3,4})(\?.*)?$/i, '');
ended = false;
trackingTime = false;
secondsPlayed = 0;
isFinite = void 0;
trackSeconds = void 0;
interval = void 0;
options.debug = options.debug || false;
init = function() {
isFinite = Number.isFinite(_this.duration());
trackSeconds = __indexOf.call(eventsToTrack, 'secondsPlayed') >= 0 && (!isFinite || options.trackFiniteSeconds);
if (!eventLabel && autoLabel) {
eventLabel = _this.currentSrc().split('/').slice(-1)[0].replace(/\.(\w{3,4})(\?.*)?$/i, '');
}
if (!isFinite && !(options.eventCategory || dataSetupOptions.eventCategory)) {
eventCategory = 'Stream';
}
if (__indexOf.call(eventsToTrack, "loadedmetadata") >= 0) {
return startTimeTracking();
};
loaded = function() {
init();
if (__indexOf.call(eventsToTrack, 'loadedmetadata') >= 0) {
sendbeacon('loadedmetadata', true);
}
};
timeupdate = function() {
var currentTime, duration, percent, percentPlayed, _i;
currentTime = Math.round(this.currentTime());
duration = Math.round(this.duration());
percentPlayed = Math.round(currentTime / duration * 100);
for (percent = _i = 0; _i <= 99; percent = _i += percentsPlayedInterval) {
if (percentPlayed >= percent && __indexOf.call(percentsAlreadyTracked, percent) < 0) {
if (__indexOf.call(eventsToTrack, "start") >= 0 && percent === 0 && percentPlayed > 0) {
sendbeacon('start', true);
} else if (__indexOf.call(eventsToTrack, "percentsPlayed") >= 0 && percentPlayed !== 0) {
sendbeacon('percent played', true, percent);
}
if (percentPlayed > 0) {
percentsAlreadyTracked.push(percent);
}
if (!isFinite) {
return;
}
if (__indexOf.call(eventsToTrack, 'percentsPlayed') >= 0) {
trackPercent();
}
if (__indexOf.call(eventsToTrack, 'seek') >= 0) {
trackSeek();
}
};
trackPercent = function() {
var currentTime, duration, percent, percentToTrack, percentsPlayed, _i, _len;
currentTime = _this.currentTime();
duration = _this.duration();
percentsPlayed = Math.round(currentTime / duration * 100);
percentToTrack = void 0;
if (!percentsPlayed || __indexOf.call(percentsTracked, percentsPlayed) >= 0) {
return;
}
for (_i = 0, _len = percentsPlayedMoments.length; _i < _len; _i++) {
percent = percentsPlayedMoments[_i];
if (percent === percentsPlayed) {
percentToTrack = percentsPlayed;
}
}
if (__indexOf.call(eventsToTrack, "seek") >= 0) {
seekStart = seekEnd;
seekEnd = currentTime;
if (Math.abs(seekStart - seekEnd) > 1) {
seeking = true;
sendbeacon('seek start', false, seekStart);
sendbeacon('seek end', false, seekEnd);
if (percentsPlayedInterval && !(percentsPlayed % percentsPlayedInterval)) {
percentToTrack = percent = percentsPlayed;
}
if (percentToTrack) {
sendbeacon('percent played', true, percentsPlayed);
return percentsTracked.push(percentsPlayed);
}
};
trackSeek = function() {
seekStart = seekEnd;
seekEnd = getCurrentValue();
if (Math.abs(seekStart - seekEnd) > 1) {
seeking = true;
sendbeacon('seek start', false, seekStart);
return sendbeacon('seek end', false, seekEnd);
}
};
startTimeTracking = function() {
var currentTime;
if (!trackSeconds || trackingTime) {
return;
}
trackingTime = true;
currentTime = getCurrentTime();
return interval = setInterval(function() {
if (!(getCurrentTime() > currentTime)) {
return;
}
secondsPlayed++;
if (__indexOf.call(secondsPlayedMoments, secondsPlayed) >= 0 || !(secondsPlayed % secondsPlayedInterval)) {
sendbeacon('seconds played', true, secondsPlayed);
}
}, 1000);
};
stopTimeTracking = function() {
clearInterval(interval);
return trackingTime = false;
};
firstplay = function() {
startTimeTracking();
if (__indexOf.call(eventsToTrack, 'start') >= 0) {
return sendbeacon('start', true);
}
};
end = function() {
ended = true;
stopTimeTracking();
if (trackReplaySeconds) {
secondsPlayed = 0;
} else {
trackSeconds = false;
}
sendbeacon('end', true);
};
play = function() {
var currentTime;
currentTime = Math.round(this.currentTime());
sendbeacon('play', true, currentTime);
startTimeTracking();
currentTime = getCurrentValue();
if (currentTime > 0 || __indexOf.call(eventsToTrack, 'start') < 0) {
sendbeacon('play', true, currentTime);
}
if (ended && currentTime === 0 && trackReplaySeconds) {
sendbeacon('start', true);
}
seeking = false;
};
playing = function() {
startTimeTracking();
seeking = false;
};
pause = function() {
var currentTime, duration;
currentTime = Math.round(this.currentTime());
stopTimeTracking();
currentTime = getCurrentValue();
duration = Math.round(this.duration());
if (currentTime !== duration && !seeking) {
sendbeacon('pause', false, currentTime);
@@ -85,60 +167,124 @@
sendbeacon('volume change', false, volume);
};
resize = function() {
sendbeacon('resize - ' + this.width() + "*" + this.height(), true);
sendbeacon('resize - ' + this.width() + '*' + this.height(), true);
};
error = function() {
var currentTime;
currentTime = Math.round(this.currentTime());
currentTime = getCurrentValue();
sendbeacon('error', true, currentTime);
};
fullscreen = function() {
var currentTime;
currentTime = Math.round(this.currentTime());
currentTime = getCurrentValue();
if ((typeof this.isFullscreen === "function" ? this.isFullscreen() : void 0) || (typeof this.isFullScreen === "function" ? this.isFullScreen() : void 0)) {
sendbeacon('enter fullscreen', false, currentTime);
} else {
sendbeacon('exit fullscreen', false, currentTime);
}
};
adstart = function() {
stopTimeTracking();
if (__indexOf.call(eventsToTrack, 'adstart') >= 0) {
return sendbeacon('adstart', false, getCurrentValue());
}
};
adpause = function() {
return sendbeacon('adpause', false);
};
adend = function() {
startTimeTracking();
return sendbeacon('adend', true);
};
adskip = function() {
return sendbeacon('adskip', false);
};
adtimeout = function() {
return sendbeacon('adtimeout', true);
};
adserror = function(data) {
return sendbeacon('adserror', true, data != null ? data.AdError : void 0);
};
getCurrentValue = function() {
if (isFinite) {
return getCurrentTime();
} else {
return secondsPlayed;
}
};
getCurrentTime = function() {
return Math.round(_this.currentTime());
};
sendbeacon = function(action, nonInteraction, value) {
if (window.ga) {
var eventFields;
eventFields = {
eventCategory: eventCategory,
eventAction: action,
nonInteraction: nonInteraction
};
if (eventLabel != null) {
eventFields.eventLabel = eventLabel;
}
if (value != null) {
eventFields.eventValue = value;
}
_this.trigger('gaEvent', eventFields);
if (options.sendGaEventDirectly && window.ga) {
ga('send', 'event', {
'eventCategory': eventCategory,
'eventAction': action,
'eventLabel': eventLabel,
'eventValue': value,
'nonInteraction': nonInteraction
});
} else if (window._gaq) {
_gaq.push(['_trackEvent', eventCategory, action, eventLabel, value, nonInteraction]);
} else if (options.debug) {
console.log("Google Analytics not detected");
}
if (options.debug) {
console.log(eventFields);
}
};
this.ready(function() {
this.on("loadedmetadata", loaded);
this.on("timeupdate", timeupdate);
if (__indexOf.call(eventsToTrack, "end") >= 0) {
this.on("ended", end);
this.on('loadedmetadata', loaded);
this.on('timeupdate', timeupdate);
this.one('firstplay', firstplay);
if (__indexOf.call(eventsToTrack, 'end') >= 0) {
this.on('ended', end);
}
if (__indexOf.call(eventsToTrack, 'play') >= 0) {
this.on('play', play);
}
this.on('playing', playing);
if (__indexOf.call(eventsToTrack, 'pause') >= 0) {
this.on('pause', pause);
}
if (__indexOf.call(eventsToTrack, 'volumeChange') >= 0) {
this.on('volumechange', volumeChange);
}
if (__indexOf.call(eventsToTrack, 'resize') >= 0) {
this.on('resize', resize);
}
if (__indexOf.call(eventsToTrack, 'error') >= 0) {
this.on('error', error);
}
if (__indexOf.call(eventsToTrack, 'fullscreen') >= 0) {
this.on('fullscreenchange', fullscreen);
}
if (__indexOf.call(eventsToTrack, "play") >= 0) {
this.on("play", play);
if (__indexOf.call(eventsToTrack, 'adstart') >= 0) {
this.on('adstart', adstart);
}
if (__indexOf.call(eventsToTrack, "pause") >= 0) {
this.on("pause", pause);
if (__indexOf.call(eventsToTrack, 'adpause') >= 0) {
this.on('adpause', adpause);
}
if (__indexOf.call(eventsToTrack, "volumeChange") >= 0) {
this.on("volumechange", volumeChange);
if (__indexOf.call(eventsToTrack, 'adend') >= 0) {
this.on('adend', adend);
}
if (__indexOf.call(eventsToTrack, "resize") >= 0) {
this.on("resize", resize);
if (__indexOf.call(eventsToTrack, 'adskip') >= 0) {
this.on('adskip', adskip);
}
if (__indexOf.call(eventsToTrack, "error") >= 0) {
this.on("error", error);
if (__indexOf.call(eventsToTrack, 'adtimeout') >= 0) {
this.on('adtimeout', adtimeout);
}
if (__indexOf.call(eventsToTrack, "fullscreen") >= 0) {
return this.on("fullscreenchange", fullscreen);
if (__indexOf.call(eventsToTrack, 'adserror') >= 0) {
return this.on('adserror', adserror);
}
});
return {
6 changes: 3 additions & 3 deletions dist/videojs.ga.min.js
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
{
"name": "videojs-ga",
"version": "0.4.2",
"version": "0.6.1",
"license": "MIT",
"author": {
"name": "Michael Bensoussan",
"email": "mbensoussan.is@gmail.com"
},
"contributors": [
{
"name": "Mikhail Khazov",
"email": "mkhazov.work@gmail.com"
}
],
"engines": {
"node": ">= 0.8.0"
},
234 changes: 175 additions & 59 deletions src/videojs.ga.coffee
Original file line number Diff line number Diff line change
@@ -9,78 +9,152 @@
videojs.plugin 'ga', (options = {}) ->
# this loads options from the data-setup attribute of the video tag
dataSetupOptions = {}
if @options()["data-setup"]
parsedOptions = JSON.parse(@options()["data-setup"])
if @options_['data-setup']
parsedOptions = JSON.parse(@options_['data-setup'])
dataSetupOptions = parsedOptions.ga if parsedOptions.ga

defaultsEventsToTrack = [
'loaded', 'percentsPlayed', 'start',
'loaded', 'percentsPlayed', 'secondsPlayed', 'start',
'end', 'seek', 'play', 'pause', 'resize',
'volumeChange', 'error', 'fullscreen'
'volumeChange', 'error', 'fullscreen',
'adstart', 'adpause', 'adend', 'adskip', 'adtimeout', 'adserror'
]
eventsToTrack = options.eventsToTrack || dataSetupOptions.eventsToTrack || defaultsEventsToTrack
percentsPlayedInterval = options.percentsPlayedInterval || dataSetupOptions.percentsPlayedInterval || 10

eventCategory = options.eventCategory || dataSetupOptions.eventCategory || 'Video'
# if you didn't specify a name, it will be 'guessed' from the video src after metadatas are loaded
autoLabel = if options.autoLabel? then options.autoLabel else true
eventLabel = options.eventLabel || dataSetupOptions.eventLabel

# if debug isn't specified
options.debug = options.debug || false
percentsPlayedInterval = options.percentsPlayedInterval || dataSetupOptions.percentsPlayedInterval || 10
percentsPlayedMoments = options.percentsPlayedMoments || dataSetupOptions.percentsPlayedMoments || []
secondsPlayedInterval = options.secondsPlayedInterval || dataSetupOptions.secondsPlayedInterval || 60
secondsPlayedMoments = options.secondsPlayedMoments || dataSetupOptions.secondsPlayedMoments || []
trackReplaySeconds = options.trackReplaySeconds

# init a few variables
percentsAlreadyTracked = []
percentsTracked = []
seekStart = seekEnd = 0
seeking = false
ended = false
trackingTime = false
secondsPlayed = 0
isFinite = undefined
trackSeconds = undefined
interval = undefined

# if debug isn't specified
options.debug = options.debug || false

init = =>
isFinite = Number.isFinite(@duration())

trackSeconds = 'secondsPlayed' in eventsToTrack && (!isFinite || options.trackFiniteSeconds)

if !eventLabel && autoLabel
eventLabel = @currentSrc().split('/').slice(-1)[0].replace(/\.(\w{3,4})(\?.*)?$/i,'')

if !isFinite && !(options.eventCategory || dataSetupOptions.eventCategory)
eventCategory = 'Stream'

startTimeTracking()

loaded = ->
unless eventLabel
eventLabel = @currentSrc().split("/").slice(-1)[0].replace(/\.(\w{3,4})(\?.*)?$/i,'')
init()

if "loadedmetadata" in eventsToTrack
if 'loadedmetadata' in eventsToTrack
sendbeacon( 'loadedmetadata', true )

return

timeupdate = ->
currentTime = Math.round(@currentTime())
duration = Math.round(@duration())
percentPlayed = Math.round(currentTime/duration*100)
return unless isFinite

if 'percentsPlayed' in eventsToTrack
trackPercent()

for percent in [0..99] by percentsPlayedInterval
if percentPlayed >= percent && percent not in percentsAlreadyTracked
if 'seek' in eventsToTrack
trackSeek()

if "start" in eventsToTrack && percent == 0 && percentPlayed > 0
sendbeacon( 'start', true )
else if "percentsPlayed" in eventsToTrack && percentPlayed != 0
sendbeacon( 'percent played', true, percent )
return

if percentPlayed > 0
percentsAlreadyTracked.push(percent)
trackPercent = =>
currentTime = @currentTime()
duration = @duration()
percentsPlayed = Math.round(currentTime / duration * 100)
percentToTrack = undefined

if "seek" in eventsToTrack
seekStart = seekEnd
seekEnd = currentTime
# if the difference between the start and the end are greater than 1 it's a seek.
if Math.abs(seekStart - seekEnd) > 1
seeking = true
sendbeacon( 'seek start', false, seekStart )
sendbeacon( 'seek end', false, seekEnd )
return if !percentsPlayed || percentsPlayed in percentsTracked

return
# handle `percentsPlayedMoments`
for percent in percentsPlayedMoments
if percent is percentsPlayed
percentToTrack = percentsPlayed

# handle `percentsPlayedInterval`
if percentsPlayedInterval && !(percentsPlayed % percentsPlayedInterval)
percentToTrack = percent = percentsPlayed

if percentToTrack
sendbeacon( 'percent played', true, percentsPlayed )
percentsTracked.push(percentsPlayed)

trackSeek = ->
seekStart = seekEnd
seekEnd = getCurrentValue()
# if the difference between the start and the end are greater than 1 it's a seek.
if Math.abs(seekStart - seekEnd) > 1
seeking = true
sendbeacon( 'seek start', false, seekStart )
sendbeacon( 'seek end', false, seekEnd )

startTimeTracking = =>
return if !trackSeconds || trackingTime

trackingTime = true
currentTime = getCurrentTime()
interval = setInterval(() =>
return unless getCurrentTime() > currentTime
secondsPlayed++

if secondsPlayed in secondsPlayedMoments ||
!(secondsPlayed % secondsPlayedInterval)
sendbeacon( 'seconds played', true, secondsPlayed )

return
, 1000)

stopTimeTracking = ->
clearInterval(interval)
trackingTime = false

firstplay = ->
startTimeTracking()
sendbeacon( 'start', true ) if 'start' in eventsToTrack

end = ->
ended = true
stopTimeTracking()
if trackReplaySeconds then secondsPlayed = 0 else trackSeconds = false
sendbeacon( 'end', true )
return

play = ->
currentTime = Math.round(@currentTime())
sendbeacon( 'play', true, currentTime )
startTimeTracking()
currentTime = getCurrentValue()
if (currentTime > 0 || 'start' not in eventsToTrack)
sendbeacon( 'play', true, currentTime )
if ended && currentTime == 0 && trackReplaySeconds
sendbeacon( 'start', true )
seeking = false
return

playing = ->
startTimeTracking()
seeking = false
return

pause = ->
currentTime = Math.round(@currentTime())
stopTimeTracking()
currentTime = getCurrentValue()
duration = Math.round(@duration())
if currentTime != duration && !seeking
sendbeacon( 'pause', false, currentTime )
@@ -93,47 +167,89 @@ videojs.plugin 'ga', (options = {}) ->
return

resize = ->
sendbeacon( 'resize - ' + @width() + "*" + @height(), true )
sendbeacon( 'resize - ' + @width() + '*' + @height(), true )
return

error = ->
currentTime = Math.round(@currentTime())
currentTime = getCurrentValue()
# XXX: Is there some informations about the error somewhere ?
sendbeacon( 'error', true, currentTime )
return

fullscreen = ->
currentTime = Math.round(@currentTime())
currentTime = getCurrentValue()
if @isFullscreen?() || @isFullScreen?()
sendbeacon( 'enter fullscreen', false, currentTime )
else
sendbeacon( 'exit fullscreen', false, currentTime )
return

sendbeacon = ( action, nonInteraction, value ) ->
# console.log action, " ", nonInteraction, " ", value
if window.ga
adstart = ->
stopTimeTracking()
sendbeacon( 'adstart', false, getCurrentValue() ) if 'adstart' in eventsToTrack

adpause = ->
sendbeacon( 'adpause', false )

adend = ->
startTimeTracking()
sendbeacon( 'adend', true )

adskip = ->
sendbeacon( 'adskip', false )

adtimeout = ->
sendbeacon( 'adtimeout', true )

adserror = (data) ->
sendbeacon( 'adserror', true, data?.AdError )

getCurrentValue = ->
return if isFinite then getCurrentTime() else secondsPlayed

getCurrentTime = =>
Math.round(@currentTime())

sendbeacon = ( action, nonInteraction, value ) =>
eventFields =
eventCategory: eventCategory,
eventAction: action,
nonInteraction: nonInteraction

eventFields.eventLabel = eventLabel if eventLabel?
eventFields.eventValue = value if value?

@trigger('gaEvent', eventFields)

if options.sendGaEventDirectly && window.ga
ga 'send', 'event',
'eventCategory' : eventCategory
'eventAction' : action
'eventLabel' : eventLabel
'eventCategory' : eventCategory
'eventAction' : action
'eventLabel' : eventLabel
'eventValue' : value
'nonInteraction' : nonInteraction
else if window._gaq
_gaq.push(['_trackEvent', eventCategory, action, eventLabel, value, nonInteraction])
else if options.debug
console.log("Google Analytics not detected")
'nonInteraction' : nonInteraction

if options.debug
console.log(eventFields)
return

@ready ->
@on("loadedmetadata", loaded)
@on("timeupdate", timeupdate)
@on("ended", end) if "end" in eventsToTrack
@on("play", play) if "play" in eventsToTrack
@on("pause", pause) if "pause" in eventsToTrack
@on("volumechange", volumeChange) if "volumeChange" in eventsToTrack
@on("resize", resize) if "resize" in eventsToTrack
@on("error", error) if "error" in eventsToTrack
@on("fullscreenchange", fullscreen) if "fullscreen" in eventsToTrack
@on('loadedmetadata', loaded)
@on('timeupdate', timeupdate)
@one('firstplay', firstplay)
@on('ended', end) if 'end' in eventsToTrack
@on('play', play) if 'play' in eventsToTrack
@on('playing', playing)
@on('pause', pause) if 'pause' in eventsToTrack
@on('volumechange', volumeChange) if 'volumeChange' in eventsToTrack
@on('resize', resize) if 'resize' in eventsToTrack
@on('error', error) if 'error' in eventsToTrack
@on('fullscreenchange', fullscreen) if 'fullscreen' in eventsToTrack
@on('adstart', adstart) if 'adstart' in eventsToTrack
@on('adpause', adpause) if 'adpause' in eventsToTrack
@on('adend', adend) if 'adend' in eventsToTrack
@on('adskip', adskip) if 'adskip' in eventsToTrack
@on('adtimeout', adtimeout) if 'adtimeout' in eventsToTrack
@on('adserror', adserror) if 'adserror' in eventsToTrack

return 'sendbeacon': sendbeacon