diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ccbe46 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..70b40e9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,28 @@ +# Contributing + +## Important notes +Please don't edit files in the `dist` subdirectory as they are generated via Grunt. You'll find source code in the `src` subdirectory! + +### Code style +Regarding code style like indentation and whitespace, **follow the conventions you see used in the source already.** + +## Modifying the code +First, ensure that you have the latest [Node.js](http://nodejs.org/) and [npm](http://npmjs.org/) installed. + +Test that Grunt's CLI is installed by running `grunt --version`. If the command isn't found, run `npm install -g grunt-cli`. For more information about installing Grunt, see the [getting started guide](http://gruntjs.com/getting-started). + +1. Fork and clone the repo. +1. Run `npm install` to install all dependencies (including Grunt). +1. Run `grunt` to grunt this project. + +Assuming that you don't see any red, you're ready to go. Just be sure to run `grunt` after making any changes, to ensure that nothing is broken. + +## Submitting pull requests + +1. Create a new branch, please don't work in your `master` branch directly. +1. Add failing tests for the change you want to make. Run `grunt` to see the tests fail. +1. Fix stuff. +1. Run `grunt` to see if the tests pass. Repeat steps 2-4 until done. +1. Open `test/*.html` unit test file(s) in actual browser to ensure tests pass everywhere. +1. Update the documentation to reflect any changes. +1. Push to your fork and submit a pull request. diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..8cd79b6 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,43 @@ +'use strict'; + +module.exports = function(grunt) { + + // Project configuration. + grunt.initConfig({ + // Metadata. + pkg: grunt.file.readJSON('package.json'), + banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + + '<%= grunt.template.today("yyyy-mm-dd") %>\n' + + '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' + + '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + + ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n', + clean: { + files: ['dist'] + }, + coffee: { + compile: { + files: { + 'dist/videojs.ga.js': 'src/videojs.ga.coffee', + } + } + }, + uglify: { + options: { + banner: '<%= banner %>' + }, + dist: { + src: 'dist/videojs.ga.js', + dest: 'dist/videojs.ga.min.js' + }, + }, + }); + + // These plugins provide necessary tasks. + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-coffee'); + + // Default task. + grunt.registerTask('default', ['clean', 'coffee', 'uglify']); + +}; diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..1cf89a6 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,22 @@ +Copyright (c) 2013 Michael Bensoussan + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..244ea07 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Ga + +Google Analytics plugin for video.js + +## Getting Started +Download [videojs](http://www.videojs.com/) + +In your web page: + +```html + + + + +``` + +## Documentation +_(Coming soon)_ + +## Examples +_(Coming soon)_ + +## Release History +_(Nothing yet)_ diff --git a/dist/videojs.ga.js b/dist/videojs.ga.js new file mode 100644 index 0000000..dbb8e93 --- /dev/null +++ b/dist/videojs.ga.js @@ -0,0 +1,122 @@ +(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; }; + + vjs.plugin('ga', function(options) { + var dataSetupOptions, deafultsEventsToTrack, end, error, eventCategory, eventLabel, eventsToTrack, fullscreen, loaded, parsedOptions, pause, percentsAlreadyTracked, percentsPlayedInterval, play, resize, seekEnd, seekStart, seeking, timeupdate, volumeChange; + dataSetupOptions = {}; + if (this.options()["data-setup"]) { + parsedOptions = JSON.parse(this.options()["data-setup"]); + if (parsedOptions.ga) { + dataSetupOptions = parsedOptions.ga; + } + } + deafultsEventsToTrack = ['loaded', 'percentsPlayed', 'start', 'end', 'seek', 'play', 'pause', 'resize', 'volumeChange', 'error', 'fullscreen']; + eventsToTrack = options.eventsToTrack || dataSetupOptions.eventsToTrack || deafultsEventsToTrack; + percentsPlayedInterval = options.percentsPlayedInterval || dataSetupOptions.percentsPlayedInterval || 10; + eventCategory = options.eventCategory || dataSetupOptions.eventCategory || 'Video'; + eventLabel = options.eventLabel || dataSetupOptions.eventLabel; + percentsAlreadyTracked = []; + seekStart = seekEnd = 0; + seeking = false; + loaded = function() { + if (!eventLabel) { + eventLabel = this.currentSrc().split("/").slice(-1)[0].replace(/\.(\w{3,4})(\?.*)?$/i, ''); + } + if (__indexOf.call(eventsToTrack, "loadedmetadata") >= 0) { + _gaq.push(['_trackEvent', eventCategory, 'loadedmetadata', eventLabel]); + } + }; + 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) { + _gaq.push(['_trackEvent', eventCategory, "start", eventLabel]); + } else if (__indexOf.call(eventsToTrack, "percentsPlayed") >= 0 && percentPlayed !== 0) { + _gaq.push(['_trackEvent', eventCategory, "" + percent + "%", eventLabel]); + } + if (percentPlayed > 0) { + percentsAlreadyTracked.push(percent); + } + } + } + if (__indexOf.call(eventsToTrack, "seek") >= 0) { + seekStart = seekEnd; + seekEnd = currentTime; + if (Math.abs(seekStart - seekEnd) > 1) { + seeking = true; + _gaq.push(['_trackEvent', eventCategory, 'seek start', eventLabel, seekStart]); + _gaq.push(['_trackEvent', eventCategory, 'seek end', eventLabel, seekEnd]); + } + } + }; + end = function() { + _gaq.push(['_trackEvent', eventCategory, "end", eventLabel]); + }; + play = function() { + var currentTime; + currentTime = Math.round(this.currentTime()); + if (currentTime > 0 && !seeking) { + _gaq.push(['_trackEvent', eventCategory, 'play', eventLabel, currentTime]); + } + seeking = true; + }; + pause = function() { + var currentTime, duration; + currentTime = Math.round(this.currentTime()); + duration = Math.round(this.duration()); + if (currentTime !== duration && !seeking) { + _gaq.push(['_trackEvent', eventCategory, 'pause', eventLabel, currentTime]); + } + }; + volumeChange = function() { + var volume; + volume = this.muted() === true ? 0 : this.volume(); + _gaq.push(['_trackEvent', eventCategory, 'volumeChange', eventLabel, volume]); + }; + resize = function() { + _gaq.push(['_trackEvent', eventCategory, 'resize', eventLabel, "" + this.width + "*" + this.height]); + }; + error = function() { + var currentTime; + currentTime = Math.round(this.currentTime()); + _gaq.push(['_trackEvent', eventCategory, 'error', eventLabel, currentTime]); + }; + fullscreen = function() { + var currentTime; + currentTime = Math.round(this.currentTime()); + if (this.isFullScreen) { + _gaq.push(['_trackEvent', eventCategory, 'enter fullscreen', eventLabel, currentTime]); + } else { + _gaq.push(['_trackEvent', eventCategory, 'exit fullscreen', eventLabel, currentTime]); + } + }; + this.on("loadedmetadata", loaded); + this.on("timeupdate", timeupdate); + if (__indexOf.call(eventsToTrack, "end") >= 0) { + this.on("ended", end); + } + if (__indexOf.call(eventsToTrack, "play") >= 0) { + this.on("play", play); + } + 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); + } + }); + +}).call(this); diff --git a/dist/videojs.ga.min.js b/dist/videojs.ga.min.js new file mode 100644 index 0000000..d4809c3 --- /dev/null +++ b/dist/videojs.ga.min.js @@ -0,0 +1,3 @@ +/*! videojs-ga - v0.1.0 - 2013-09-07 +* Copyright (c) 2013 Michael Bensoussan; Licensed */ +(function(){var a=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};vjs.plugin("ga",function(b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v;c={},this.options()["data-setup"]&&(l=JSON.parse(this.options()["data-setup"]),l.ga&&(c=l.ga)),d=["loaded","percentsPlayed","start","end","seek","play","pause","resize","volumeChange","error","fullscreen"],i=b.eventsToTrack||c.eventsToTrack||d,o=b.percentsPlayedInterval||c.percentsPlayedInterval||10,g=b.eventCategory||c.eventCategory||"Video",h=b.eventLabel||c.eventLabel,n=[],s=r=0,t=!1,k=function(){h||(h=this.currentSrc().split("/").slice(-1)[0].replace(/\.(\w{3,4})(\?.*)?$/i,"")),a.call(i,"loadedmetadata")>=0&&_gaq.push(["_trackEvent",g,"loadedmetadata",h])},u=function(){var b,c,d,e,f;for(b=Math.round(this.currentTime()),c=Math.round(this.duration()),e=Math.round(100*(b/c)),d=f=0;99>=f;d=f+=o)e>=d&&a.call(n,d)<0&&(a.call(i,"start")>=0&&0===d&&e>0?_gaq.push(["_trackEvent",g,"start",h]):a.call(i,"percentsPlayed")>=0&&0!==e&&_gaq.push(["_trackEvent",g,""+d+"%",h]),e>0&&n.push(d));a.call(i,"seek")>=0&&(s=r,r=b,Math.abs(s-r)>1&&(t=!0,_gaq.push(["_trackEvent",g,"seek start",h,s]),_gaq.push(["_trackEvent",g,"seek end",h,r])))},e=function(){_gaq.push(["_trackEvent",g,"end",h])},p=function(){var a;a=Math.round(this.currentTime()),a>0&&!t&&_gaq.push(["_trackEvent",g,"play",h,a]),t=!0},m=function(){var a,b;a=Math.round(this.currentTime()),b=Math.round(this.duration()),a===b||t||_gaq.push(["_trackEvent",g,"pause",h,a])},v=function(){var a;a=this.muted()===!0?0:this.volume(),_gaq.push(["_trackEvent",g,"volumeChange",h,a])},q=function(){_gaq.push(["_trackEvent",g,"resize",h,""+this.width+"*"+this.height])},f=function(){var a;a=Math.round(this.currentTime()),_gaq.push(["_trackEvent",g,"error",h,a])},j=function(){var a;a=Math.round(this.currentTime()),this.isFullScreen?_gaq.push(["_trackEvent",g,"enter fullscreen",h,a]):_gaq.push(["_trackEvent",g,"exit fullscreen",h,a])},this.on("loadedmetadata",k),this.on("timeupdate",u),a.call(i,"end")>=0&&this.on("ended",e),a.call(i,"play")>=0&&this.on("play",p),a.call(i,"pause")>=0&&this.on("pause",m),a.call(i,"volumeChange")>=0&&this.on("volumechange",v),a.call(i,"resize")>=0&&this.on("resize",q),a.call(i,"error")>=0&&this.on("error",f),a.call(i,"fullscreen")>=0&&this.on("fullscreenchange",j)})}).call(this); \ No newline at end of file diff --git a/libs/video-js-4.1.0/demo.captions.vtt b/libs/video-js-4.1.0/demo.captions.vtt new file mode 100644 index 0000000..e598be1 --- /dev/null +++ b/libs/video-js-4.1.0/demo.captions.vtt @@ -0,0 +1,41 @@ +WEBVTT + +00:00.700 --> 00:04.110 +Captions describe all relevant audio for the hearing impaired. +[ Heroic music playing for a seagull ] + +00:04.500 --> 00:05.000 +[ Splash!!! ] + +00:05.100 --> 00:06.000 +[ Sploosh!!! ] + +00:08.000 --> 00:09.225 +[ Splash...splash...splash splash splash ] + +00:10.525 --> 00:11.255 +[ Splash, Sploosh again ] + +00:13.500 --> 00:14.984 +Dolphin: eeeEEEEEeeee! + +00:14.984 --> 00:16.984 +Dolphin: Squawk! eeeEEE? + +00:25.000 --> 00:28.284 +[ A whole ton of splashes ] + +00:29.500 --> 00:31.000 +Mine. Mine. Mine. + +00:34.300 --> 00:36.000 +Shark: Chomp + +00:36.800 --> 00:37.900 +Shark: CHOMP!!! + +00:37.861 --> 00:41.193 +EEEEEEOOOOOOOOOOWHALENOISE + +00:42.593 --> 00:45.611 +[ BIG SPLASH ] \ No newline at end of file diff --git a/libs/video-js-4.1.0/demo.html b/libs/video-js-4.1.0/demo.html new file mode 100644 index 0000000..fb26a08 --- /dev/null +++ b/libs/video-js-4.1.0/demo.html @@ -0,0 +1,30 @@ + + + + Video.js | HTML5 Video Player + + + + + + + + + + + + + + + + + diff --git a/libs/video-js-4.1.0/font/vjs.eot b/libs/video-js-4.1.0/font/vjs.eot new file mode 100644 index 0000000..1b8202a Binary files /dev/null and b/libs/video-js-4.1.0/font/vjs.eot differ diff --git a/libs/video-js-4.1.0/font/vjs.svg b/libs/video-js-4.1.0/font/vjs.svg new file mode 100644 index 0000000..2059a1f --- /dev/null +++ b/libs/video-js-4.1.0/font/vjs.svg @@ -0,0 +1,40 @@ + + + + +This is a custom SVG font generated by IcoMoon. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/video-js-4.1.0/font/vjs.ttf b/libs/video-js-4.1.0/font/vjs.ttf new file mode 100644 index 0000000..a5ad468 Binary files /dev/null and b/libs/video-js-4.1.0/font/vjs.ttf differ diff --git a/libs/video-js-4.1.0/font/vjs.woff b/libs/video-js-4.1.0/font/vjs.woff new file mode 100644 index 0000000..375510e Binary files /dev/null and b/libs/video-js-4.1.0/font/vjs.woff differ diff --git a/libs/video-js-4.1.0/video-js.css b/libs/video-js-4.1.0/video-js.css new file mode 100644 index 0000000..3058edb --- /dev/null +++ b/libs/video-js-4.1.0/video-js.css @@ -0,0 +1,726 @@ +/*! +Video.js Default Styles (http://videojs.com) +Version 4.1.0 +*/ + +/* +REQUIRED STYLES (be careful overriding) +================================================================================ */ +/* When loading the player, the video tag is replaced with a DIV, + that will hold the video tag or object tag for other playback methods. + The div contains the video playback element (Flash or HTML5) and controls, and sets the width and height of the video. + + ** If you want to add some kind of border/padding (e.g. a frame), or special positioning, use another containing element. + Otherwise you risk messing up control positioning and full window mode. ** +*/ +.video-js { + background-color: #000; + position: relative; + padding: 0; + /* Start with 10px for base font size so other dimensions can be em based and easily calculable. */ + font-size: 10px; + /* Allow poster to be vertially aligned. */ + vertical-align: middle; + /* display: table-cell; */ /*This works in Safari but not Firefox.*/ + + /* Turn off user selection (text highlighting) by default. + The majority of player components will not be text blocks. + Text areas will need to turn user selection back on. */ + -webkit-user-select: none; /* Chrome all / Safari all */ + -moz-user-select: none; /* Firefox all */ + -ms-user-select: none; /* IE 10+ */ + -o-user-select: none; + user-select: none; +} + +/* Playback technology elements expand to the width/height of the containing div. +