├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bower.json ├── index.html ├── package.json ├── scripts ├── banner.ejs ├── build-test.js ├── postversion.js ├── server.js └── version.js ├── src ├── plugin.js └── plugin.scss └── test ├── index.html ├── karma.conf.js └── plugin.test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 2 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | Thumbs.db 3 | ehthumbs.db 4 | Desktop.ini 5 | .DS_Store 6 | ._* 7 | 8 | # Editors 9 | *~ 10 | *.swp 11 | *.tmproj 12 | *.tmproject 13 | *.sublime-* 14 | .idea/ 15 | .project/ 16 | .settings/ 17 | .vscode/ 18 | 19 | # Logs 20 | logs 21 | *.log 22 | npm-debug.log* 23 | 24 | # Dependency directories 25 | bower_components/ 26 | node_modules/ 27 | 28 | # Yeoman meta-data 29 | .yo-rc.json 30 | 31 | # Build-related directories 32 | dist/ 33 | docs/api/ 34 | es5/ 35 | test/dist/ 36 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Intentionally left blank, so that npm does not ignore anything by default, 2 | # but relies on the package.json "files" array to explicitly define what ends 3 | # up in the package. 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | dist: trusty 3 | language: node_js 4 | node_js: 5 | - 'node' 6 | - 'lts/argon' 7 | 8 | before_script: 9 | # Check if the current version is equal to the major version for the env. 10 | - 'export IS_INSTALLED="$(npm list video.js | grep "video.js@$VJS")"' 11 | # We have to add semicolons to the end of each line in the if as Travis runs 12 | # this all on one line. 13 | - 'if [ -z "$IS_INSTALLED" ]; then 14 | echo "INSTALLING video.js@>=$VJS.0.0-RC.0 <$(($VJS+1)).0.0"; 15 | npm i "video.js@>=$VJS.0.0-RC.0 <\$(($VJS+1)).0.0"; 16 | else 17 | echo "video.js@$VJS ALREADY INSTALLED"; 18 | fi' 19 | - export CHROME_BIN=/usr/bin/google-chrome 20 | - export DISPLAY=:99.0 21 | - sh -e /etc/init.d/xvfb start 22 | 23 | env: 24 | - VJS=5 25 | - VJS=6 26 | 27 | addons: 28 | firefox: latest 29 | apt: 30 | sources: 31 | - google-chrome 32 | packages: 33 | - google-chrome-stable 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ========= 3 | 4 | ## HEAD (Unreleased) 5 | _(none)_ 6 | 7 | -------------------- 8 | 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | We welcome contributions from everyone! 4 | 5 | ## Getting Started 6 | 7 | Make sure you have NodeJS 0.10 or higher and npm installed. 8 | 9 | 1. Fork this repository and clone your fork 10 | 1. Install dependencies: `npm install` 11 | 1. Run a development server: `npm start` 12 | 13 | ### Making Changes 14 | 15 | Refer to the [video.js plugin standards][standards] for more detail on best practices and tooling for video.js plugin authorship. 16 | 17 | When you've made your changes, push your commit(s) to your fork and issue a pull request against the original repository. 18 | 19 | ### Running Tests 20 | 21 | Testing is a crucial part of any software project. For all but the most trivial changes (typos, etc) test cases are expected. Tests are run in actual browsers using [Karma][karma]. 22 | 23 | - In all available and supported browsers: `npm test` 24 | - In a specific browser: `npm run test:chrome`, `npm run test:firefox`, etc. 25 | - While development server is running (`npm start`), navigate to [`http://localhost:9999/test/`][local] 26 | 27 | 28 | [karma]: http://karma-runner.github.io/ 29 | [local]: http://localhost:9999/test/ 30 | [standards]: https://github.com/videojs/generator-videojs-plugin/docs/standards.md 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Dotsub <dev@dotsub.com> 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **THIS REPORITOTY IS NOT MAINTAINED ANYMORE!** If you are interested in maintaining it, please write to dev@dotsub.com. 2 | 3 | [![Build Status](https://travis-ci.org/dotsub/videojs-watermark.svg?branch=master)](https://travis-ci.org/dotsub/videojs-watermark) [![npm version](https://badge.fury.io/js/videojs-watermark.svg)](https://badge.fury.io/js/videojs-watermark) 4 | 5 | # videojs-watermark 6 | 7 | Adds a watermark image the video player. After the initial 'fadeTime' the watermark will be shown when the video players controls are shown. This also provides the ability to use the watermark as a link. 8 | 9 | ![Player Preview](https://cloud.githubusercontent.com/assets/1881100/15156352/9be2dac6-16b4-11e6-9981-d63e1421bac2.png) 10 | 11 | ## Installation 12 | 13 | ```sh 14 | npm install --save videojs-watermark 15 | ``` 16 | 17 | ## Configuration 18 | 19 | **image: Required** The URL to the image to be used as the watermark. 20 | 21 | **position:** The location to place the watermark (top-left, top-right, bottom-left, bottom-right). Defaults to 'top-right'. 22 | 23 | **fadeTime:** The amount of time in milliseconds for the initial watermark fade. Defaults to 3000. To make watermark permanently visible, use `null`. 24 | 25 | **url:** A url to be linked to from the watermark. If the user clicks the watermark the video will be paused and the link will open in a new window. 26 | 27 | ## Usage 28 | 29 | To include videojs-watermark on your website or web application, use any of the following methods. 30 | 31 | ### ` 37 | 38 | 43 | ``` 44 | 45 | ### Browserify 46 | 47 | When using with Browserify, install videojs-watermark via npm and `require` the plugin as you would any other module. 48 | 49 | ```js 50 | var videojs = require('video.js'); 51 | 52 | // The actual plugin function is exported by this module, but it is also 53 | // attached to the `Player.prototype`; so, there is no need to assign it 54 | // to a variable. 55 | require('videojs-watermark'); 56 | 57 | var player = videojs('my-video'); 58 | 59 | player.watermark(); 60 | ``` 61 | 62 | ### Browserify ES6 63 | 64 | When using with Browserify, install videojs-watermark via npm and `import` the plugin as you would any other module. 65 | 66 | ```js 67 | import videojs from 'video.js'; 68 | 69 | // The actual plugin function is exported by this module, but it is also 70 | // attached to the `Player.prototype`; so, there is no need to assign it 71 | // to a variable. 72 | import 'videojs-watermark'; 73 | 74 | const player = videojs('my-video'); 75 | player.watermark(); 76 | ``` 77 | 78 | ### RequireJS/AMD 79 | 80 | When using with RequireJS (or another AMD library), get the script in whatever way you prefer and `require` the plugin as you normally would: 81 | 82 | ```js 83 | require(['video.js', 'videojs-watermark'], function(videojs) { 84 | var player = videojs('my-video'); 85 | 86 | player.watermark(); 87 | }); 88 | ``` 89 | 90 | ## License 91 | 92 | Apache-2.0. Copyright (c) Dotsub <dev@dotsub.com> 93 | 94 | 95 | [videojs]: http://videojs.com/ 96 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Brooks Lyrette ", 3 | "homepage": "https://github.com/dotsub/videojs-watermark", 4 | "keywords": [ 5 | "videojs", 6 | "videojs-plugin", 7 | "watermark" 8 | ], 9 | "license": "Apache-2.0", 10 | "main": [ 11 | "src/plugin.js", 12 | "src/plugin.scss" 13 | ], 14 | "moduleType": "es6", 15 | "name": "videojs-watermark", 16 | "version": "1.0.0" 17 | } 18 | 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | videojs-watermark Demo 6 | 7 | 8 | 9 | 10 | 14 | 18 | 19 | 20 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "videojs-watermark", 3 | "version": "2.0.0", 4 | "description": "Adds a watermark image the video player", 5 | "repository": "https://github.com/dotsub/videojs-watermark", 6 | "main": "es5/plugin.js", 7 | "scripts": { 8 | "prebuild": "npm run clean", 9 | "build": "npm-run-all -p build:*", 10 | "build:css": "npm-run-all build:css:sass build:css:bannerize", 11 | "build:css:bannerize": "bannerize dist/videojs-watermark.css --banner=scripts/banner.ejs", 12 | "build:css:sass": "node-sass src/plugin.scss dist/videojs-watermark.css --output-style=compressed --linefeed=lf", 13 | "build:js": "npm-run-all build:js:babel build:js:browserify build:js:bannerize build:js:uglify", 14 | "build:js:babel": "babel src -d es5", 15 | "build:js:bannerize": "bannerize dist/videojs-watermark.js --banner=scripts/banner.ejs", 16 | "build:js:browserify": "browserify . -s videojs-watermark -t browserify-shim -t browserify-versionify -o dist/videojs-watermark.js", 17 | "build:js:uglify": "uglifyjs dist/videojs-watermark.js --comments --mangle --compress -o dist/videojs-watermark.min.js", 18 | "build:test": "babel-node scripts/build-test.js", 19 | "change": "chg add", 20 | "clean": "rimraf dist test/dist es5 && mkdirp dist test/dist es5", 21 | "lint": "vjsstandard", 22 | "start": "babel-node scripts/server.js", 23 | "pretest": "npm-run-all lint build", 24 | "test": "karma start test/karma.conf.js", 25 | "test:chrome": "npm run pretest && karma start test/karma.conf.js --browsers Chrome", 26 | "test:firefox": "npm run pretest && karma start test/karma.conf.js --browsers Firefox", 27 | "test:ie": "npm run pretest && karma start test/karma.conf.js --browsers IE", 28 | "test:safari": "npm run pretest && karma start test/karma.conf.js --browsers Safari", 29 | "preversion": "npm test", 30 | "version": "babel-node scripts/version.js", 31 | "postversion": "babel-node scripts/postversion.js", 32 | "prepublish": "npm run build" 33 | }, 34 | "keywords": [ 35 | "videojs", 36 | "videojs-plugin", 37 | "watermark" 38 | ], 39 | "author": "Brooks Lyrette ", 40 | "license": "Apache-2.0", 41 | "browserify-shim": { 42 | "qunit": "global:QUnit", 43 | "sinon": "global:sinon", 44 | "video.js": "global:videojs" 45 | }, 46 | "style": "dist/videojs-watermark.css", 47 | "videojs-plugin": { 48 | "style": "dist/videojs-watermark.css", 49 | "script": "dist/videojs-watermark.min.js" 50 | }, 51 | "vjsstandard": { 52 | "ignore": [ 53 | "dist", 54 | "docs", 55 | "es5", 56 | "test/dist", 57 | "test/karma.conf.js" 58 | ] 59 | }, 60 | "files": [ 61 | "CONTRIBUTING.md", 62 | "dist/", 63 | "docs/", 64 | "es5/", 65 | "index.html", 66 | "scripts/", 67 | "src/", 68 | "test/" 69 | ], 70 | "dependencies": { 71 | "video.js": "^7.5.4" 72 | }, 73 | "devDependencies": { 74 | "babel": "^5.8.35", 75 | "babelify": "^6.4.0", 76 | "bannerize": "^1.0.2", 77 | "bluebird": "^3.2.2", 78 | "browserify": "^12.0.2", 79 | "browserify-shim": "^3.8.12", 80 | "browserify-versionify": "^1.0.6", 81 | "budo": "^8.0.4", 82 | "chg": "^0.3.2", 83 | "glob": "^6.0.3", 84 | "global": "^4.3.0", 85 | "karma": "^0.13.19", 86 | "karma-chrome-launcher": "^0.2.2", 87 | "karma-detect-browsers": "^2.0.2", 88 | "karma-firefox-launcher": "^0.1.7", 89 | "karma-ie-launcher": "^0.2.0", 90 | "karma-qunit": "^0.1.9", 91 | "karma-safari-launcher": "^0.1.1", 92 | "mkdirp": "^0.5.1", 93 | "node-sass": "^4.14.1", 94 | "npm-run-all": "^1.5.1", 95 | "qunitjs": "^1.21.0", 96 | "rimraf": "^2.5.1", 97 | "sinon": "~1.14.0", 98 | "uglify-js": "^2.6.1", 99 | "videojs-standard": "^4.0.0" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /scripts/banner.ejs: -------------------------------------------------------------------------------- 1 | /** 2 | * <%- pkg.name %> 3 | * @version <%- pkg.version %> 4 | * @copyright <%- date.getFullYear() %> <%- pkg.author %> 5 | * @license <%- pkg.license %> 6 | */ 7 | -------------------------------------------------------------------------------- /scripts/build-test.js: -------------------------------------------------------------------------------- 1 | import browserify from 'browserify'; 2 | import fs from 'fs'; 3 | import glob from 'glob'; 4 | 5 | /* eslint no-console: 0 */ 6 | 7 | glob('test/**/*.test.js', (err, files) => { 8 | if (err) { 9 | throw err; 10 | } 11 | browserify(files) 12 | .transform('babelify') 13 | .transform('browserify-shim') 14 | .transform('browserify-versionify') 15 | .bundle() 16 | .pipe(fs.createWriteStream('test/dist/bundle.js')); 17 | }); 18 | -------------------------------------------------------------------------------- /scripts/postversion.js: -------------------------------------------------------------------------------- 1 | import {exec} from 'child_process'; 2 | import fs from 'fs'; 3 | import path from 'path'; 4 | 5 | /* eslint no-console: 0 */ 6 | 7 | /** 8 | * Determines whether or not the project has the Bower setup by checking for 9 | * the presence of a bower.json file. 10 | * 11 | * @return {Boolean} 12 | */ 13 | const hasBower = () => { 14 | try { 15 | fs.statSync(path.join(__dirname, '../bower.json')); 16 | return true; 17 | } catch (x) { 18 | return false; 19 | } 20 | }; 21 | 22 | // If the project supports Bower, roll HEAD back one commit to avoid having 23 | // the tagged commit - with `dist/` - in the main history. 24 | if (hasBower()) { 25 | exec('git reset --hard HEAD~1', (err, stdout, stderr) => { 26 | if (err) { 27 | process.stdout.write(err.stack); 28 | process.exit(err.status || 1); 29 | } else { 30 | process.stdout.write(stdout); 31 | } 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /scripts/server.js: -------------------------------------------------------------------------------- 1 | import Promise from 'bluebird'; 2 | import browserify from 'browserify'; 3 | import budo from 'budo'; 4 | import fs from 'fs'; 5 | import glob from 'glob'; 6 | import mkdirp from 'mkdirp'; 7 | import sass from 'node-sass'; 8 | import path from 'path'; 9 | 10 | /* eslint no-console: 0 */ 11 | 12 | const pkg = require(path.join(__dirname, '../package.json')); 13 | 14 | // Replace "%s" tokens with the plugin name in a string. 15 | const nameify = (str) => 16 | str.replace(/%s/g, pkg.name.split('/').reverse()[0]); 17 | 18 | const srces = { 19 | css: 'src/plugin.scss', 20 | js: 'src/plugin.js', 21 | tests: glob.sync('test/**/*.test.js') 22 | }; 23 | 24 | const dests = { 25 | css: nameify('dist/%s.css'), 26 | js: nameify('dist/%s.js'), 27 | tests: 'test/dist/bundle.js' 28 | }; 29 | 30 | const bundlers = { 31 | 32 | js: browserify({ 33 | debug: true, 34 | entries: [srces.js], 35 | standalone: nameify('%s'), 36 | transform: [ 37 | 'babelify', 38 | 'browserify-shim', 39 | 'browserify-versionify' 40 | ] 41 | }), 42 | 43 | tests: browserify({ 44 | debug: true, 45 | entries: srces.tests, 46 | transform: [ 47 | 'babelify', 48 | 'browserify-shim', 49 | 'browserify-versionify' 50 | ] 51 | }) 52 | }; 53 | 54 | const bundle = (name) => { 55 | return new Promise((resolve, reject) => { 56 | bundlers[name] 57 | .bundle() 58 | .pipe(fs.createWriteStream(dests[name])) 59 | .on('finish', resolve) 60 | .on('error', reject); 61 | }); 62 | }; 63 | 64 | mkdirp.sync('dist'); 65 | 66 | // Start the server _after_ the initial bundling is done. 67 | Promise.all([bundle('js'), bundle('tests')]).then(() => { 68 | const server = budo({ 69 | port: 9999, 70 | stream: process.stdout 71 | }).on('reload', (f) => console.log('reloading %s', f || 'everything')); 72 | 73 | /** 74 | * A collection of functions which are mapped to strings that are used to 75 | * generate RegExp objects. If a filepath matches the RegExp, the function 76 | * will be used to handle that watched file. 77 | * 78 | * @type {Object} 79 | */ 80 | const handlers = { 81 | 82 | /** 83 | * Handler for Sass source. 84 | * 85 | * @param {String} event 86 | * @param {String} file 87 | */ 88 | '^src/.+\.scss$'(event, file) { 89 | console.log('re-compiling sass'); 90 | let result = sass.renderSync({file: srces.css, outputStyle: 'compressed'}); 91 | 92 | fs.writeFileSync(dests.css, result.css); 93 | server.reload(); 94 | }, 95 | 96 | /** 97 | * Handler for JavaScript source. 98 | * 99 | * @param {String} event 100 | * @param {String} file 101 | */ 102 | '^src/.+\.js$'(event, file) { 103 | console.log('re-bundling javascript and tests'); 104 | Promise.all([bundle('js'), bundle('tests')]).then(() => server.reload()); 105 | }, 106 | 107 | /** 108 | * Handler for JavaScript tests. 109 | * 110 | * @param {String} event 111 | * @param {String} file 112 | */ 113 | '^test/.+\.test\.js$'(event, file) { 114 | console.log('re-bundling tests'); 115 | bundle('tests').then(() => server.reload()); 116 | } 117 | }; 118 | 119 | /** 120 | * Finds the first handler function for the file that matches a RegExp 121 | * derived from the keys. 122 | * 123 | * @param {String} file 124 | * @return {Function|Undefined} 125 | */ 126 | const findHandler = (file) => { 127 | const keys = Object.keys(handlers); 128 | 129 | for (let i = 0; i < keys.length; i++) { 130 | let regex = new RegExp(keys[i]); 131 | 132 | if (regex.test(file)) { 133 | return handlers[keys[i]]; 134 | } 135 | } 136 | }; 137 | 138 | server 139 | .live() 140 | .watch([ 141 | 'index.html', 142 | 'src/**/*.{scss,js}', 143 | 'test/**/*.test.js', 144 | 'test/index.html' 145 | ]) 146 | .on('watch', (event, file) => { 147 | const handler = findHandler(file); 148 | 149 | console.log(`detected a "${event}" event in "${file}"`); 150 | 151 | if (handler) { 152 | handler(event, file); 153 | } else { 154 | server.reload(); 155 | } 156 | }); 157 | }); 158 | -------------------------------------------------------------------------------- /scripts/version.js: -------------------------------------------------------------------------------- 1 | import {exec} from 'child_process'; 2 | import fs from 'fs'; 3 | import path from 'path'; 4 | 5 | /* eslint no-console: 0 */ 6 | 7 | const pkg = require(path.join(__dirname, '../package.json')); 8 | 9 | /** 10 | * Determines whether or not the project has the CHANGELOG setup by checking 11 | * for the presence of a CHANGELOG.md file and the necessary dependency and 12 | * npm script. 13 | * 14 | * @return {Boolean} 15 | */ 16 | const hasChangelog = () => { 17 | try { 18 | fs.statSync(path.join(__dirname, '../CHANGELOG.md')); 19 | } catch (x) { 20 | return false; 21 | } 22 | return pkg.devDependencies.hasOwnProperty('chg') && 23 | pkg.scripts.hasOwnProperty('change'); 24 | }; 25 | 26 | /** 27 | * Determines whether or not the project has the Bower setup by checking for 28 | * the presence of a bower.json file. 29 | * 30 | * @return {Boolean} 31 | */ 32 | const hasBower = () => { 33 | try { 34 | fs.statSync(path.join(__dirname, '../bower.json')); 35 | return true; 36 | } catch (x) { 37 | return false; 38 | } 39 | }; 40 | 41 | const commands = []; 42 | 43 | // If the project has a CHANGELOG, update it for the new release. 44 | if (hasChangelog()) { 45 | commands.push(`chg release "${pkg.version}"`); 46 | commands.push('git add CHANGELOG.md'); 47 | } 48 | 49 | // If the project supports Bower, perform special extra versioning step. 50 | if (hasBower()) { 51 | commands.push('git add package.json'); 52 | commands.push(`git commit -m "${pkg.version}"`); 53 | 54 | // We only need a build in the Bower-supported case because of the 55 | // temporary addition of the dist/ directory. 56 | commands.push('npm run build'); 57 | commands.push('git add -f dist'); 58 | } 59 | 60 | if (commands.length) { 61 | exec(commands.join(' && '), (err, stdout, stderr) => { 62 | if (err) { 63 | process.stdout.write(err.stack); 64 | process.exit(err.status || 1); 65 | } else { 66 | process.stdout.write(stdout); 67 | } 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /src/plugin.js: -------------------------------------------------------------------------------- 1 | import videojs from 'video.js'; 2 | 3 | // Default options for the plugin. 4 | const defaults = { 5 | position: 'top-right', 6 | fadeTime: 3000, 7 | url: undefined, 8 | image: undefined 9 | }; 10 | 11 | /** 12 | * Sets up the div, img and optional a tags for the plugin. 13 | * 14 | * @function setupWatermark 15 | * @param {Player} player 16 | * @param {Object} [options={}] 17 | */ 18 | const setupWatermark = (player, options) => { 19 | // Add a div and img tag 20 | const videoEl = player.el(); 21 | const div = document.createElement('div'); 22 | const img = document.createElement('img'); 23 | 24 | div.classList.add('vjs-watermark-content'); 25 | div.classList.add(`vjs-watermark-${options.position}`); 26 | img.src = options.image; 27 | 28 | // if a url is provided make the image link to that URL. 29 | if (options.url) { 30 | const a = document.createElement('a'); 31 | 32 | a.href = options.url; 33 | // if the user clicks the link pause and open a new window 34 | a.onclick = (e) => { 35 | e.preventDefault(); 36 | player.pause(); 37 | window.open(options.url); 38 | }; 39 | a.appendChild(img); 40 | div.appendChild(a); 41 | } else { 42 | div.appendChild(img); 43 | } 44 | videoEl.appendChild(div); 45 | }; 46 | 47 | /** 48 | * Fades the watermark image. 49 | * 50 | * @function fadeWatermark 51 | * @param {Object} [options={ 52 | * fadeTime: 53 | * 'The number of milliseconds before the inital watermark fade out'}] 54 | */ 55 | const fadeWatermark = (options) => { 56 | setTimeout( 57 | () => document.getElementsByClassName('vjs-watermark-content')[0] 58 | .classList.add('vjs-watermark-fade'), 59 | options.fadeTime 60 | ); 61 | }; 62 | 63 | /** 64 | * Function to invoke when the player is ready. 65 | * 66 | * This is a great place for your plugin to initialize itself. When this 67 | * function is called, the player will have its DOM and child components 68 | * in place. 69 | * 70 | * @function onPlayerReady 71 | * @param {Player} player 72 | * @param {Object} [options={}] 73 | */ 74 | const onPlayerReady = (player, options) => { 75 | player.addClass('vjs-watermark'); 76 | 77 | // if there is no image set just exit 78 | if (!options.image) { 79 | return; 80 | } 81 | setupWatermark(player, options); 82 | 83 | // Setup watermark autofade 84 | if (options.fadeTime === null) { 85 | return; 86 | } 87 | 88 | player.on('play', () => fadeWatermark(options)); 89 | }; 90 | 91 | /** 92 | * A video.js plugin. 93 | * 94 | * In the plugin function, the value of `this` is a video.js `Player` 95 | * instance. You cannot rely on the player being in a "ready" state here, 96 | * depending on how the plugin is invoked. This may or may not be important 97 | * to you; if not, remove the wait for "ready"! 98 | * 99 | * @function watermark 100 | * @param {Object} [options={}] 101 | * An object of options left to the plugin author to define. 102 | */ 103 | const watermark = function(options) { 104 | this.ready(() => { 105 | onPlayerReady(this, videojs.mergeOptions(defaults, options)); 106 | }); 107 | }; 108 | 109 | // Register the plugin with video.js. 110 | videojs.registerPlugin('watermark', watermark); 111 | 112 | // Include the version number. 113 | watermark.VERSION = '__VERSION__'; 114 | 115 | export default watermark; 116 | -------------------------------------------------------------------------------- /src/plugin.scss: -------------------------------------------------------------------------------- 1 | @mixin transition($args...) { 2 | -webkit-transition: $args; 3 | -moz-transition: $args; 4 | -ms-transition: $args; 5 | -o-transition: $args; 6 | transition: $args; 7 | } 8 | 9 | // Sass for videojs-watermark 10 | .video-js { 11 | &.vjs-watermark { 12 | display: block; 13 | } 14 | .vjs-watermark-content { 15 | opacity: 0.99; 16 | position: absolute; 17 | padding: 5px; 18 | @include transition(visibility 1.0s, opacity 1.0s); 19 | } 20 | 21 | // pre-defined positions 22 | .vjs-watermark-top-right { 23 | right: 0; 24 | top: 0; 25 | } 26 | .vjs-watermark-top-left { 27 | left: 0; 28 | top: 0; 29 | } 30 | // bottom set to 30px to account for controlbar 31 | .vjs-watermark-bottom-right { 32 | right: 0; 33 | bottom: 30px; 34 | } 35 | .vjs-watermark-bottom-left { 36 | left: 0; 37 | bottom: 30px; 38 | } 39 | 40 | //fade out when the user is not active and the video is playing. 41 | &.vjs-user-inactive.vjs-playing .vjs-watermark-fade { 42 | opacity: 0; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | videojs-watermark Unit Tests 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | var browsers = config.browsers; 3 | var frameworks = ['qunit']; 4 | var plugins = ['karma-qunit']; 5 | 6 | var addBrowserLauncher = function(browser) { 7 | plugins.push('karma-' + browser.toLowerCase() + '-launcher'); 8 | }; 9 | 10 | // On Travis CI, we can only run in Firefox. 11 | if (process.env.TRAVIS) { 12 | browsers = ['Firefox']; 13 | browsers.forEach(addBrowserLauncher); 14 | 15 | // If specific browsers are requested on the command line, load their 16 | // launchers. 17 | } else if (browsers.length) { 18 | browsers.forEach(addBrowserLauncher); 19 | 20 | // If no browsers are specified, we will do a `karma-detect-browsers` run, 21 | // which means we need to set up that plugin and all the browser plugins 22 | // we are supporting. 23 | } else { 24 | frameworks.push('detectBrowsers'); 25 | plugins.push('karma-detect-browsers'); 26 | ['chrome', 'firefox', 'ie', 'safari'].forEach(addBrowserLauncher); 27 | } 28 | 29 | config.set({ 30 | basePath: '..', 31 | frameworks: frameworks, 32 | 33 | files: [ 34 | 'dist/videojs-watermark.css', 35 | 'node_modules/sinon/pkg/sinon.js', 36 | 'node_modules/sinon/pkg/sinon-ie.js', 37 | 'node_modules/video.js/dist/video.js', 38 | 'node_modules/video.js/dist/video-js.css', 39 | 'test/dist/bundle.js' 40 | ], 41 | 42 | browsers: browsers, 43 | plugins: plugins, 44 | 45 | detectBrowsers: { 46 | usePhantomJS: false 47 | }, 48 | 49 | reporters: ['dots'], 50 | port: 9876, 51 | colors: true, 52 | autoWatch: false, 53 | singleRun: true, 54 | concurrency: Infinity 55 | }); 56 | }; 57 | -------------------------------------------------------------------------------- /test/plugin.test.js: -------------------------------------------------------------------------------- 1 | import document from 'global/document'; 2 | 3 | import QUnit from 'qunit'; 4 | import sinon from 'sinon'; 5 | import videojs from 'video.js'; 6 | 7 | import plugin from '../src/plugin'; 8 | 9 | const Player = videojs.getComponent('Player'); 10 | 11 | QUnit.test('the environment is sane', function(assert) { 12 | assert.strictEqual(typeof Array.isArray, 'function', 'es5 exists'); 13 | assert.strictEqual(typeof sinon, 'object', 'sinon exists'); 14 | assert.strictEqual(typeof videojs, 'function', 'videojs exists'); 15 | assert.strictEqual(typeof plugin, 'function', 'plugin is a function'); 16 | }); 17 | 18 | QUnit.module('videojs-watermark', { 19 | 20 | beforeEach() { 21 | 22 | // Mock the environment's timers because certain things - particularly 23 | // player readiness - are asynchronous in video.js 5. This MUST come 24 | // before any player is created; otherwise, timers could get created 25 | // with the actual timer methods! 26 | this.clock = sinon.useFakeTimers(); 27 | 28 | this.fixture = document.getElementById('qunit-fixture'); 29 | this.video = document.createElement('video'); 30 | this.fixture.appendChild(this.video); 31 | this.player = videojs(this.video); 32 | }, 33 | 34 | afterEach() { 35 | this.player.dispose(); 36 | this.clock.restore(); 37 | } 38 | }); 39 | 40 | QUnit.test('registers itself with video.js', function(assert) { 41 | assert.expect(2); 42 | 43 | assert.strictEqual( 44 | typeof Player.prototype.watermark, 45 | 'function', 46 | 'videojs-watermark plugin was registered' 47 | ); 48 | 49 | this.player.watermark(); 50 | 51 | // Tick the clock forward enough to trigger the player to be "ready". 52 | this.clock.tick(1); 53 | 54 | assert.ok( 55 | this.player.hasClass('vjs-watermark'), 56 | 'the plugin adds a class to the player' 57 | ); 58 | }); 59 | 60 | QUnit.test('does not add image if not configued', function(assert) { 61 | assert.expect(2); 62 | 63 | assert.strictEqual( 64 | typeof Player.prototype.watermark, 65 | 'function', 66 | 'videojs-watermark plugin was registered' 67 | ); 68 | 69 | this.player.watermark(); 70 | 71 | // Tick the clock forward enough to trigger the player to be "ready". 72 | this.clock.tick(1); 73 | 74 | assert.equal( 75 | 0, 76 | this.player.contentEl().getElementsByClassName('vjs-watermark-content').length, 77 | 'The plugin should not add content to the player if no image is configued' 78 | ); 79 | }); 80 | 81 | QUnit.test('does add image with correct path', function(assert) { 82 | const imageUrl = '/images/foo.png'; 83 | 84 | assert.expect(5); 85 | 86 | assert.strictEqual( 87 | typeof Player.prototype.watermark, 88 | 'function', 89 | 'videojs-watermark plugin was registered' 90 | ); 91 | 92 | this.player.watermark({ image: imageUrl }); 93 | 94 | // Tick the clock forward enough to trigger the player to be "ready". 95 | this.clock.tick(1); 96 | 97 | const imageContainer = this.player.contentEl() 98 | .getElementsByClassName('vjs-watermark-content')[0]; 99 | const image = imageContainer.getElementsByTagName('img')[0]; 100 | 101 | assert.ok( 102 | imageContainer, 103 | 'The plugin should add content to the player if an image is configued' 104 | ); 105 | 106 | assert.ok( 107 | image.src.endsWith(imageUrl), 108 | 'This is not the correct image' 109 | ); 110 | 111 | assert.equal( 112 | 0, 113 | imageContainer.getElementsByTagName('a').length, 114 | 'The plugin should not add a link unless there is a configured URL' 115 | ); 116 | 117 | assert.equal( 118 | imageContainer.id, 119 | '', 120 | 'the plugin doesn\'t add an ID to image container' 121 | ); 122 | }); 123 | 124 | QUnit.test('does add a link when URL is configured', function(assert) { 125 | const imageUrl = '/images/foo.png'; 126 | const linkUrl = '/some/path'; 127 | 128 | assert.expect(6); 129 | 130 | assert.strictEqual( 131 | typeof Player.prototype.watermark, 132 | 'function', 133 | 'videojs-watermark plugin was registered' 134 | ); 135 | 136 | this.player.watermark({ image: imageUrl, url: linkUrl }); 137 | 138 | // Tick the clock forward enough to trigger the player to be "ready". 139 | this.clock.tick(1); 140 | 141 | const imageContainer = this.player.contentEl() 142 | .getElementsByClassName('vjs-watermark-content')[0]; 143 | const image = imageContainer.getElementsByTagName('img')[0]; 144 | const link = imageContainer.getElementsByTagName('a')[0]; 145 | 146 | assert.ok( 147 | imageContainer, 148 | 'The plugin should add content to the player if an image is configued' 149 | ); 150 | 151 | assert.ok( 152 | image.src.endsWith(imageUrl), 153 | 'This is not the correct image' 154 | ); 155 | 156 | assert.equal( 157 | 1, 158 | imageContainer.getElementsByTagName('a').length, 159 | 'The plugin should add a link since the URL is configued' 160 | ); 161 | 162 | assert.ok( 163 | link.href.endsWith(linkUrl), 164 | 'This is not the correct link' 165 | ); 166 | 167 | assert.equal( 168 | imageContainer.id, 169 | '', 170 | 'the plugin doesn\'t add an ID to image container' 171 | ); 172 | }); 173 | 174 | QUnit.test('fades out after player is started', function(assert) { 175 | // GIVEN 176 | const imageUrl = '/images/foo.png'; 177 | 178 | this.player.watermark({ image: imageUrl, fadeTime: 1 }); 179 | this.clock.tick(1); 180 | 181 | // WHEN 182 | this.player.trigger('play'); 183 | this.clock.tick(10); 184 | 185 | // THEN 186 | const imageContainer = this.player.contentEl() 187 | .getElementsByClassName('vjs-watermark-content')[0]; 188 | 189 | assert.ok(imageContainer.classList.contains('vjs-watermark-fade')); 190 | }); 191 | --------------------------------------------------------------------------------