├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .nvmrc ├── .travis.yml ├── .whitesource ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── scripts ├── karma.conf.js ├── postcss.config.js └── rollup.config.js ├── src ├── index.js ├── overlay-component.js ├── plugin.js └── plugin.scss └── test └── 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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | Briefly describe the issue. 3 | Include a [reduced test case](https://css-tricks.com/reduced-test-cases/). 4 | 5 | ## Steps to reproduce 6 | Explain in detail the exact steps necessary to reproduce the issue. 7 | 8 | 1. 9 | 2. 10 | 3. 11 | 12 | ## Results 13 | ### Expected 14 | Please describe what you expected to see. 15 | 16 | ### Actual 17 | Please describe what actually happened. 18 | 19 | ### Error output 20 | If there are any errors at all, please include them here. 21 | 22 | ## Additional Information 23 | Please include any additional information necessary here. Including the following: 24 | 25 | ### versions 26 | #### videojs 27 | what version of videojs does this occur with? 28 | 29 | #### browsers 30 | what browser are affected? 31 | 32 | #### OSes 33 | what platforms (operating systems and devices) are affected? 34 | 35 | ### plugins 36 | are any videojs plugins being used on the page? If so, please list them below. 37 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | Please describe the change as necessary. 3 | If it's a feature or enhancement please be as detailed as possible. 4 | If it's a bug fix, please link the issue that it fixes or describe the bug in as much detail. 5 | 6 | ## Specific Changes proposed 7 | Please list the specific changes involved in this pull request. 8 | 9 | ## Requirements Checklist 10 | - [ ] Feature implemented / Bug fixed 11 | - [ ] If necessary, more likely in a feature request than a bug fix 12 | - [ ] Unit Tests updated or fixed 13 | - [ ] Docs/guides updated 14 | - [ ] Reviewed by Two Core Contributors 15 | -------------------------------------------------------------------------------- /.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 | # Build-related directories 29 | dist/ 30 | docs/api/ 31 | test/dist/ 32 | .eslintcache 33 | .yo-rc.json 34 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/* 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | dist: trusty 3 | language: node_js 4 | # node version is specified using the .nvmrc file 5 | before_install: 6 | - npm install -g greenkeeper-lockfile@1 7 | before_script: 8 | - export DISPLAY=:99.0 9 | - sh -e /etc/init.d/xvfb start 10 | - greenkeeper-lockfile-update 11 | after_script: 12 | - greenkeeper-lockfile-upload 13 | addons: 14 | firefox: latest 15 | chrome: stable 16 | 17 | -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "settingsInheritedFrom": "brightcove/whitesource-config@main" 3 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # [4.0.0](https://github.com/brightcove/videojs-overlay/compare/v3.1.0...v4.0.0) (2024-10-28) 3 | 4 | ### Features 5 | 6 | * move to advanced plugin and add reset API (#224) ([8524c38](https://github.com/brightcove/videojs-overlay/commit/8524c38)), closes [#224](https://github.com/brightcove/videojs-overlay/issues/224) 7 | 8 | ### Bug Fixes 9 | 10 | * package json export for publish (#225) ([944acd6](https://github.com/brightcove/videojs-overlay/commit/944acd6)), closes [#225](https://github.com/brightcove/videojs-overlay/issues/225) 11 | 12 | 13 | # [3.1.0](https://github.com/brightcove/videojs-overlay/compare/v3.0.0...v3.1.0) (2023-06-15) 14 | 15 | ### Features 16 | 17 | * make overlays mutable (#223) ([a6c0353](https://github.com/brightcove/videojs-overlay/commit/a6c0353)), closes [#223](https://github.com/brightcove/videojs-overlay/issues/223) 18 | 19 | 20 | # [3.0.0](https://github.com/brightcove/videojs-overlay/compare/v2.1.5...v3.0.0) (2022-12-16) 21 | 22 | ### Chores 23 | 24 | * skip vjsverify es check ([e10a7b1](https://github.com/brightcove/videojs-overlay/commit/e10a7b1)) 25 | * update build tooling to drop older browser support (#220) ([85fe717](https://github.com/brightcove/videojs-overlay/commit/85fe717)), closes [#220](https://github.com/brightcove/videojs-overlay/issues/220) 26 | 27 | 28 | ### BREAKING CHANGES 29 | 30 | * This removes support for some older browsers such as IE 11 31 | 32 | 33 | ## [2.1.5](https://github.com/brightcove/videojs-overlay/compare/v2.1.4...v2.1.5) (2021-09-13) 34 | 35 | ### Bug Fixes 36 | 37 | * qualityMenu button to left of playToggle (#118) ([952188e](https://github.com/brightcove/videojs-overlay/commit/952188e)), closes [#118](https://github.com/brightcove/videojs-overlay/issues/118) 38 | 39 | ### Chores 40 | 41 | * **package:** update lint-staged to version 8.1.0 (#94) ([b3cee2f](https://github.com/brightcove/videojs-overlay/commit/b3cee2f)), closes [#94](https://github.com/brightcove/videojs-overlay/issues/94) 42 | * **package:** update npm-run-all/videojs-generator-verify for security ([9d2d40f](https://github.com/brightcove/videojs-overlay/commit/9d2d40f)) 43 | * **package:** update videojs-generate-karma-config to version 5.0.0 (#93) ([4e9d161](https://github.com/brightcove/videojs-overlay/commit/4e9d161)), closes [#93](https://github.com/brightcove/videojs-overlay/issues/93) 44 | * **package:** update videojs-generate-rollup-config to version 2.3.1 (#95) ([dedba7c](https://github.com/brightcove/videojs-overlay/commit/dedba7c)), closes [#95](https://github.com/brightcove/videojs-overlay/issues/95) 45 | * **package:** update videojs-standard to version 8.0.2 (#96) ([b548b3b](https://github.com/brightcove/videojs-overlay/commit/b548b3b)), closes [#96](https://github.com/brightcove/videojs-overlay/issues/96) 46 | 47 | 48 | ## [2.1.4](https://github.com/brightcove/videojs-overlay/compare/v2.1.3...v2.1.4) (2018-09-19) 49 | 50 | ### Bug Fixes 51 | 52 | * Properly expose plugin version (#80) ([9c8822c](https://github.com/brightcove/videojs-overlay/commit/9c8822c)), closes [#80](https://github.com/brightcove/videojs-overlay/issues/80) 53 | * Remove the postinstall script to prevent install issues (#77) ([5edecf7](https://github.com/brightcove/videojs-overlay/commit/5edecf7)), closes [#77](https://github.com/brightcove/videojs-overlay/issues/77) 54 | 55 | ### Chores 56 | 57 | * update to generator-videojs-plugin[@7](https://github.com/7).2.0 ([7e0b357](https://github.com/brightcove/videojs-overlay/commit/7e0b357)) 58 | 59 | 60 | ## [2.1.3](https://github.com/brightcove/videojs-overlay/compare/v2.1.2...v2.1.3) (2018-08-23) 61 | 62 | ### Chores 63 | 64 | * generator v7 (#73) ([449679e](https://github.com/brightcove/videojs-overlay/commit/449679e)), closes [#73](https://github.com/brightcove/videojs-overlay/issues/73) 65 | 66 | 67 | ## [2.1.2](https://github.com/brightcove/videojs-overlay/compare/v2.1.1...v2.1.2) (2018-08-03) 68 | 69 | ### Bug Fixes 70 | 71 | * babel the es dist, by updating the generator (#68) ([bd7f070](https://github.com/brightcove/videojs-overlay/commit/bd7f070)), closes [#68](https://github.com/brightcove/videojs-overlay/issues/68) 72 | 73 | ### Chores 74 | 75 | * **package:** update dependencies, enable greenkeeper (#67) ([70afc00](https://github.com/brightcove/videojs-overlay/commit/70afc00)), closes [#67](https://github.com/brightcove/videojs-overlay/issues/67) 76 | 77 | 78 | ## [2.1.1](https://github.com/brightcove/videojs-overlay/compare/v2.1.0...v2.1.1) (2018-07-05) 79 | 80 | ### Chores 81 | 82 | * update to generator v6 (#63) ([4ac2452](https://github.com/brightcove/videojs-overlay/commit/4ac2452)), closes [#63](https://github.com/brightcove/videojs-overlay/issues/63) 83 | 84 | 85 | # [2.1.0](https://github.com/brightcove/videojs-overlay/compare/v2.0.0...v2.1.0) (2018-04-20) 86 | 87 | ### Features 88 | 89 | * Allow choosing the placement of overlay elements in the control bar. ([b8b0607](https://github.com/brightcove/videojs-overlay/commit/b8b0607)) 90 | 91 | ### Bug Fixes 92 | 93 | * Upgrade rollup to v0.52.x to fix build failures ([#60](https://github.com/brightcove/videojs-overlay/issues/60)) ([b0b3a5d](https://github.com/brightcove/videojs-overlay/commit/b0b3a5d)) 94 | 95 | 96 | # [2.0.0](https://github.com/brightcove/videojs-overlay/compare/v1.1.3...v2.0.0) (2017-08-24) 97 | 98 | ### Features 99 | 100 | * Fix vertical centre alignment and add align-center ([#38](https://github.com/brightcove/videojs-overlay/issues/38)) ([8649210](https://github.com/brightcove/videojs-overlay/commit/8649210)) 101 | 102 | ### Bug Fixes 103 | 104 | * Fix malformed README link ([#43](https://github.com/brightcove/videojs-overlay/issues/43)) ([c2b1315](https://github.com/brightcove/videojs-overlay/commit/c2b1315)) 105 | * remove global browserify transforms, so parent packages don't break ([#48](https://github.com/brightcove/videojs-overlay/issues/48)) ([aa74853](https://github.com/brightcove/videojs-overlay/commit/aa74853)) 106 | 107 | ### Code Refactoring 108 | 109 | * Update to use generator v5 tooling. ([#51](https://github.com/brightcove/videojs-overlay/issues/51)) ([bfeff8c](https://github.com/brightcove/videojs-overlay/commit/bfeff8c)) 110 | 111 | ## 1.1.4 (2017-04-03) 112 | * fix: remove global browserify transforms, so parent packages don't break 113 | 114 | ## 1.1.3 (2017-02-27) 115 | * update travis to test vjs 5/6 (#46) 116 | 117 | ## 1.1.2 (2017-02-03) 118 | * Added Video.js 5 and 6 cross-compatibility. 119 | 120 | ## 1.1.1 (2016-08-05) 121 | * Fixed issue where max-width was being set on all overlays rather than only those showBackground=false. 122 | 123 | ## 1.1.0 (2016-07-27) 124 | * Added showBackground option to show or hide the overlay background. 125 | * Added attachToControlBar option to allow bottom align control bars to move when the control bar minimizes. 126 | 127 | ## 1.0.2 (2016-06-10) 128 | _(none)_ 129 | 130 | ## 1.0.1 (2016-03-08) 131 | * Fixed #22, should not have been checking for integers only. 132 | 133 | ## 1.0.0 (2016-02-12) 134 | * Major refactoring of plugin to align with generator-videojs-plugin standards. 135 | * Fixed significant edge-case issues with creation/destruction of overlays. 136 | 137 | ## 0.1.0 (2014-04-29) 138 | * Initial release 139 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | We welcome contributions from everyone! 4 | 5 | ## Getting Started 6 | 7 | Make sure you have Node.js 4.8 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 conventions][conventions] 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 | [conventions]: https://github.com/videojs/generator-videojs-plugin/blob/master/docs/conventions.md 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Brightcove, Inc. 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 | # videojs-overlay 2 | 3 | [](https://travis-ci.org/brightcove/videojs-overlay) 4 | [](https://greenkeeper.io/) 5 | [](http://slack.videojs.com) 6 | 7 | [](https://nodei.co/npm/videojs-overlay/) 8 | 9 | A plugin to display simple overlays - similar to YouTube's "Annotations" feature in appearance - during video playback. 10 | 11 | _Note_: This meaning of an "overlay" is distinct from that of a modal dialog, which can overlay the entire player. This is built into video.js as [the `ModalDialog` component](http://docs.videojs.com/docs/api/modal-dialog.html). 12 | 13 | Maintenance Status: Stable 14 | 15 | 16 | 17 | 18 | 19 | - [Getting Started](#getting-started) 20 | - [Documentation](#documentation) 21 | - [API](#api) 22 | - [`player.overlay()`](#playeroverlay) 23 | - [`overlay.get()`](#overlayget) 24 | - [`overlay.add(object|array)`](#overlayaddobjectarray) 25 | - [`overlay.remove(object)`](#overlayremoveobject) 26 | - [Plugin Options](#plugin-options) 27 | - [`align`](#align) 28 | - [`showBackground`](#showbackground) 29 | - [`attachToControlBar`](#attachtocontrolbar) 30 | - [`class`](#class) 31 | - [`content`](#content) 32 | - [`overlays`](#overlays) 33 | - [Examples](#examples) 34 | 35 | 36 | 37 | 38 | ## Getting Started 39 | 40 | Once you've added the plugin script to your page, you can use it with any video: 41 | 42 | ```html 43 | 44 | 47 | ``` 48 | 49 | There's also a [working example](https://github.com/brightcove/videojs-overlay/blob/master/index.html) of the plugin you can check out if you're having trouble. 50 | 51 | ## Documentation 52 | 53 | ### API 54 | #### `player.overlay()` 55 | This is the main interface and the way to initialize this plugin. It takes [an options object as input](#plugin-options). 56 | 57 | #### `overlay.get()` 58 | 59 | Returns an array of all the overlays set up for the current video. 60 | 61 | #### `overlay.add(Object|Array)` 62 | 63 | Adds one or more overlays to the current list of overlays without replacing the current list of overlays. 64 | Returns a reference to the added overlays. 65 | 66 | ```js 67 | const overlay = player.overlay({ 68 | content: 'Default overlay content', 69 | debug: true, 70 | overlays: [{ 71 | content: 'The video is playing!', 72 | start: 'play', 73 | end: 'pause' 74 | }] 75 | }); 76 | const addedOverlays = overlay.add({content: "this is a new one", start: "play", end: "pause"}); 77 | ``` 78 | 79 | 80 | #### `overlay.remove(Object)` 81 | 82 | Removes an individual overlay from the list of overlays. Calling this method with an invalid overlay object removes nothing from the list. 83 | 84 | ```js 85 | const overlay = player.overlay({ 86 | content: 'Default overlay content', 87 | debug: true, 88 | overlays: [{ 89 | content: 'The video is playing!', 90 | start: 'play', 91 | end: 'pause' 92 | }] 93 | }); 94 | const overlayToRemove = overlay.get()[0]; 95 | overlay.remove(overlayToRemove); 96 | ``` 97 | 98 | #### `overlay.reset(Object)` 99 | 100 | Once the plugin is initialized, the plugin options can be reset by passing this function an object of options. This will remove the previous configuration and overlays, and update the plugin with the new values. It takes [an options object as input](#plugin-options). 101 | 102 | ```js 103 | // First initialization 104 | const overlay = player.overlay({ 105 | debug: true, 106 | overlays: [{ 107 | content: 'The video is playing!', 108 | start: 'play', 109 | end: 'pause' 110 | }] 111 | }); 112 | 113 | // Update configuration with different overlays 114 | const overlayToRemove = overlay.reset({ 115 | debug: false, 116 | overlays: [{ 117 | content: 'Some new overlay content!', 118 | start: 'play', 119 | end: 'pause' 120 | }] 121 | }); 122 | ``` 123 | 124 | ### Plugin Options 125 | 126 | You may pass in an options object to the plugin upon initialization. This 127 | object may contain any of the following properties: 128 | 129 | #### `align` 130 | 131 | __Type:__ `String` 132 | __Default:__ `"top-left"` 133 | 134 | _This setting can be overridden by being set on individual overlay objects._ 135 | 136 | Where to display overlays, by default. Assuming the included stylesheet is used, the following values are supported: `"top-left"`, `"top"`, `"top-right"`, `"right"`, `"bottom-right"`, `"bottom"`, `"bottom-left"`, `"left"`. 137 | 138 | #### `showBackground` 139 | 140 | __Type:__ `Boolean` 141 | __Default:__ `true` 142 | 143 | _This setting can be overridden by being set on individual overlay objects._ 144 | 145 | Whether or not to include background styling & padding around the overlay. 146 | 147 | #### `attachToControlBar` 148 | 149 | __Type:__ `Boolean`, `String` 150 | __Default:__ `false` 151 | 152 | _This setting can be overridden by being set on individual overlay objects._ 153 | 154 | If set to `true` or a `string` value, bottom aligned overlays will adjust positioning when the control bar minimizes. This has no effect on overlays that are not aligned to bottom, bottom-left, or bottom-right. For use with the default control bar, it may not work for custom control bars. 155 | 156 | The value of `string` must be the name of a ControlBar component. 157 | 158 | Bottom aligned overlays will be inserted before the specified component. Otherwise, bottom aligned overlays are inserted before the first child component of the ControlBar. All other overlays are inserted before the ControlBar component. 159 | 160 | #### `class` 161 | 162 | __Type:__ `String` 163 | __Default:__ `""` 164 | 165 | _This setting can be overridden by being set on individual overlay objects._ 166 | 167 | A custom HTML class to add to each overlay element. 168 | 169 | #### `content` 170 | 171 | __Type:__ `String`, `Element`, `DocumentFragment` 172 | __Default:__ `"This overlay will show up while the video is playing"` 173 | 174 | _This setting can be overridden by being set on individual overlay objects._ 175 | 176 | The default HTML that the overlay includes. 177 | 178 | #### `overlays` 179 | 180 | __Type:__ `Array` 181 | __Default:__ an array with a single example overlay 182 | 183 | An array of overlay objects. An overlay object should consist of: 184 | 185 | - `start` (`String` or `Number`): When to show the overlay. If its value is a string, it is understood as the name of an event. If it is a number, the overlay will be shown when that moment in the playback timeline is passed. 186 | - `end` (`String` or `Number`): When to hide the overlay. The values of this property have the same semantics as `start`. 187 | 188 | And it can optionally include `align`, `class`, and/or `content` to override top-level settings. 189 | 190 | All properties are currently optional. That is, you may leave `start` or `end` off and the plugin will not complain, but you should always pass a `start` and an `end`. This will be required in a future release. 191 | 192 | ### Examples 193 | 194 | You can setup overlays to be displayed when particular events are emitted by the player, including your own custom events: 195 | 196 | ```js 197 | player.overlay({ 198 | overlays: [{ 199 | 200 | // This overlay will appear when a video is playing and disappear when 201 | // the player is paused. 202 | start: 'playing', 203 | end: 'pause' 204 | }, { 205 | 206 | // This overlay will appear when the "custom1" event is triggered and 207 | // disappear when the "custom2" event is triggered. 208 | start: 'custom1', 209 | end: 'custom2' 210 | }] 211 | }); 212 | ``` 213 | 214 | Multiple overlays can be displayed simultaneously. You probably want to specify an alignment for one or more of them so they don't overlap: 215 | 216 | ```js 217 | player.overlay({ 218 | overlays: [{ 219 | 220 | // This overlay appears at 3 seconds and disappears at 15 seconds. 221 | start: 3, 222 | end: 15 223 | }, { 224 | 225 | // This overlay appears at 7 seconds and disappears at 22 seconds. 226 | start: 7, 227 | end: 22, 228 | align: 'bottom' 229 | }] 230 | }); 231 | ``` 232 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |overlay text
'; 170 | 171 | this.player.overlay({ 172 | content: innerHTML, 173 | overlays: [{ 174 | start: 'playing', 175 | end: 'ended' 176 | }] 177 | }); 178 | 179 | this.player.trigger('playing'); 180 | 181 | assert.strictEqual( 182 | this.player.$('.vjs-overlay').innerHTML, 183 | innerHTML, 184 | 'innerHTML matched' 185 | ); 186 | } 187 | ); 188 | 189 | QUnit.test('an element can be used as the content of overlays', function(assert) { 190 | assert.expect(1); 191 | 192 | const content = document.createElement('p'); 193 | 194 | content.innerHTML = 'this is some text'; 195 | 196 | this.player.overlay({ 197 | content, 198 | overlays: [{ 199 | start: 5, 200 | end: 10 201 | }] 202 | }); 203 | 204 | this.updateTime(5); 205 | 206 | assert.strictEqual( 207 | this.player.$('.vjs-overlay p'), 208 | content, 209 | 'sets the content element' 210 | ); 211 | }); 212 | 213 | QUnit.test('a DocumentFragment can be used as the content of overlays', function(assert) { 214 | assert.expect(1); 215 | 216 | const fragment = document.createDocumentFragment(); 217 | const br = document.createElement('br'); 218 | 219 | fragment.appendChild(br); 220 | 221 | this.player.overlay({ 222 | content: fragment, 223 | overlays: [{ 224 | start: 'showoverlay', 225 | end: 'hideoverlay' 226 | }] 227 | }); 228 | 229 | this.player.trigger('showoverlay'); 230 | 231 | assert.strictEqual( 232 | this.player.$('.vjs-overlay br'), 233 | br, 234 | 'sets the content fragment' 235 | ); 236 | }); 237 | 238 | QUnit.test('allows content to be specified per overlay', function(assert) { 239 | assert.expect(5); 240 | 241 | const text = 'some text'; 242 | const html = 'overlay text
'; 243 | const element = document.createElement('i'); 244 | const fragment = document.createDocumentFragment(); 245 | 246 | fragment.appendChild(document.createElement('img')); 247 | 248 | this.player.overlay({ 249 | content: text, 250 | overlays: [{ 251 | start: 0, 252 | end: 1 253 | }, { 254 | content: html, 255 | start: 0, 256 | end: 1 257 | }, { 258 | content: element, 259 | start: 0, 260 | end: 1 261 | }, { 262 | content: fragment, 263 | start: 0, 264 | end: 1 265 | }] 266 | }); 267 | 268 | this.updateTime(0); 269 | this.assertOverlayCount(assert, 4); 270 | 271 | assert.strictEqual( 272 | this.player.$$('.vjs-overlay b').length, 273 | 1, 274 | 'shows a default overlay' 275 | ); 276 | 277 | assert.strictEqual( 278 | this.player.$$('.vjs-overlay p').length, 279 | 1, 280 | 'shows an HTML string' 281 | ); 282 | 283 | assert.strictEqual( 284 | this.player.$$('.vjs-overlay i').length, 285 | 1, 286 | 'shows a DOM element' 287 | ); 288 | 289 | assert.strictEqual( 290 | this.player.$$('.vjs-overlay img').length, 291 | 1, 292 | 'shows a document fragment' 293 | ); 294 | }); 295 | 296 | QUnit.test('allows css class to be specified per overlay', function(assert) { 297 | assert.expect(3); 298 | 299 | const text = 'some text'; 300 | const fragment = document.createDocumentFragment(); 301 | 302 | fragment.appendChild(document.createElement('img')); 303 | 304 | this.player.overlay({ 305 | content: text, 306 | overlays: [{ 307 | class: 'first-class-overlay', 308 | start: 0, 309 | end: 1 310 | }, { 311 | class: 'second-class-overlay', 312 | start: 0, 313 | end: 1 314 | }, { 315 | start: 0, 316 | end: 1 317 | }] 318 | }); 319 | 320 | this.updateTime(0); 321 | 322 | this.assertOverlayCount(assert, 3); 323 | 324 | assert.strictEqual( 325 | this.player.$$('.first-class-overlay').length, 326 | 1, 327 | 'shows an overlay with a custom class' 328 | ); 329 | 330 | assert.strictEqual( 331 | this.player.$$('.second-class-overlay').length, 332 | 1, 333 | 'shows an overlay with a different custom class' 334 | ); 335 | }); 336 | 337 | QUnit.test('does not double add overlays that are triggered twice', function(assert) { 338 | assert.expect(1); 339 | 340 | this.player.overlay({ 341 | overlays: [{ 342 | start: 'start', 343 | end: 'end' 344 | }] 345 | }); 346 | 347 | this.player.trigger('start'); 348 | this.player.trigger('start'); 349 | this.assertOverlayCount(assert, 1); 350 | }); 351 | 352 | QUnit.test('does not double remove overlays that are triggered twice', function(assert) { 353 | assert.expect(1); 354 | 355 | this.player.overlay({ 356 | overlays: [{ 357 | start: 'start', 358 | end: 'end' 359 | }] 360 | }); 361 | 362 | this.player.trigger('start'); 363 | this.player.trigger('end'); 364 | this.player.trigger('end'); 365 | this.assertOverlayCount(assert, 0); 366 | }); 367 | 368 | QUnit.test( 369 | 'displays overlays that mix event and playback time triggers', 370 | function(assert) { 371 | assert.expect(4); 372 | 373 | this.player.overlay({ 374 | overlays: [{ 375 | start: 'start', 376 | end: 10 377 | }, { 378 | start: 5, 379 | end: 'end' 380 | }] 381 | }); 382 | 383 | this.player.trigger('start'); 384 | this.assertOverlayCount(assert, 1); 385 | 386 | this.updateTime(6); 387 | this.assertOverlayCount(assert, 2); 388 | 389 | this.updateTime(10); 390 | this.assertOverlayCount(assert, 1); 391 | 392 | this.player.trigger('end'); 393 | this.assertOverlayCount(assert, 0); 394 | } 395 | ); 396 | 397 | QUnit.test('shows mixed trigger overlays once per seek', function(assert) { 398 | assert.expect(6); 399 | 400 | this.player.overlay({ 401 | overlays: [{ 402 | start: 1, 403 | end: 'pause' 404 | }] 405 | }); 406 | 407 | this.updateTime(1); 408 | this.assertOverlayCount(assert, 1); 409 | 410 | this.player.trigger('pause'); 411 | this.assertOverlayCount(assert, 0); 412 | 413 | this.updateTime(2); 414 | this.assertOverlayCount(assert, 0); 415 | 416 | this.updateTime(1); 417 | this.assertOverlayCount(assert, 1); 418 | 419 | this.player.trigger('pause'); 420 | this.assertOverlayCount(assert, 0); 421 | 422 | this.updateTime(2); 423 | this.assertOverlayCount(assert, 0); 424 | }); 425 | 426 | QUnit.test('applies simple alignment class names', function(assert) { 427 | assert.expect(4); 428 | 429 | this.player.overlay({ 430 | overlays: [{ 431 | start: 'start', 432 | align: 'top' 433 | }, { 434 | start: 'start', 435 | align: 'left' 436 | }, { 437 | start: 'start', 438 | align: 'right' 439 | }, { 440 | start: 'start', 441 | align: 'bottom' 442 | }] 443 | }); 444 | 445 | this.player.trigger('start'); 446 | 447 | assert.ok( 448 | this.player.$('.vjs-overlay.vjs-overlay-top'), 449 | 'applies top class' 450 | ); 451 | 452 | assert.ok( 453 | this.player.$('.vjs-overlay.vjs-overlay-right'), 454 | 'applies right class' 455 | ); 456 | 457 | assert.ok( 458 | this.player.$('.vjs-overlay.vjs-overlay-bottom'), 459 | 'applies bottom class' 460 | ); 461 | 462 | assert.ok( 463 | this.player.$('.vjs-overlay.vjs-overlay-left'), 464 | 'applies left class' 465 | ); 466 | }); 467 | 468 | QUnit.test('applies compound alignment class names', function(assert) { 469 | assert.expect(4); 470 | 471 | this.player.overlay({ 472 | overlays: [{ 473 | start: 'start', 474 | align: 'top-left' 475 | }, { 476 | start: 'start', 477 | align: 'top-right' 478 | }, { 479 | start: 'start', 480 | align: 'bottom-left' 481 | }, { 482 | start: 'start', 483 | align: 'bottom-right' 484 | }] 485 | }); 486 | 487 | this.player.trigger('start'); 488 | 489 | assert.ok( 490 | this.player.$('.vjs-overlay.vjs-overlay-top-left'), 491 | 'applies top class' 492 | ); 493 | 494 | assert.ok( 495 | this.player.$('.vjs-overlay.vjs-overlay-top-right'), 496 | 'applies right class' 497 | ); 498 | 499 | assert.ok( 500 | this.player.$('.vjs-overlay.vjs-overlay-bottom-left'), 501 | 'applies bottom class' 502 | ); 503 | 504 | assert.ok( 505 | this.player.$('.vjs-overlay.vjs-overlay-bottom-right'), 506 | 'applies left class' 507 | ); 508 | }); 509 | 510 | QUnit.test('removes time based overlays if the user seeks backward', function(assert) { 511 | assert.expect(2); 512 | 513 | this.player.overlay({ 514 | overlays: [{ 515 | start: 5, 516 | end: 10 517 | }] 518 | }); 519 | 520 | this.updateTime(6); 521 | this.assertOverlayCount(assert, 1); 522 | 523 | this.updateTime(4); 524 | this.assertOverlayCount(assert, 0); 525 | }); 526 | 527 | QUnit.test('applies background styling when showBackground is true', function(assert) { 528 | assert.expect(1); 529 | 530 | this.player.overlay({ 531 | overlays: [{ 532 | start: 'start', 533 | showBackground: true 534 | }] 535 | }); 536 | 537 | this.player.trigger('start'); 538 | 539 | assert.ok( 540 | this.player.$('.vjs-overlay.vjs-overlay-background'), 541 | 'applies background styling' 542 | ); 543 | }); 544 | 545 | QUnit.test('doesn\'t apply background when showBackground is false', function(assert) { 546 | assert.expect(1); 547 | 548 | this.player.overlay({ 549 | overlays: [{ 550 | start: 'start', 551 | showBackground: false 552 | }] 553 | }); 554 | 555 | this.player.trigger('start'); 556 | 557 | assert.notOk( 558 | this.player.$('.vjs-overlay.vjs-overlay-background'), 559 | 'does not apply background styling' 560 | ); 561 | }); 562 | 563 | QUnit.test('attaches bottom aligned overlays to the controlBar', function(assert) { 564 | assert.expect(4); 565 | 566 | this.player.overlay({ 567 | attachToControlBar: true, 568 | overlays: [{ 569 | start: 'start', 570 | align: 'bottom-left' 571 | }, { 572 | start: 'start', 573 | align: 'bottom' 574 | }, { 575 | start: 'start', 576 | align: 'bottom-right' 577 | }, { 578 | start: 'start', 579 | align: 'top-right' 580 | }] 581 | }); 582 | 583 | this.player.trigger('start'); 584 | 585 | assert.ok( 586 | this.player.controlBar.$('.vjs-overlay.vjs-overlay-bottom-left'), 587 | 'bottom-left attaches to control bar' 588 | ); 589 | 590 | assert.ok( 591 | this.player.controlBar.$('.vjs-overlay.vjs-overlay-bottom'), 592 | 'bottom attaches to control bar' 593 | ); 594 | 595 | assert.ok( 596 | this.player.controlBar.$('.vjs-overlay.vjs-overlay-bottom-right'), 597 | 'bottom-right attaches to control bar' 598 | ); 599 | 600 | assert.notOk( 601 | this.player.controlBar.$('.vjs-overlay.vjs-overlay-top-right'), 602 | 'top-right is not attached to control bar' 603 | ); 604 | }); 605 | 606 | QUnit.test('attach only to player when attachToControlbar is false', function(assert) { 607 | assert.expect(2); 608 | 609 | this.player.overlay({ 610 | attachToControlBar: false, 611 | overlays: [{ 612 | start: 'start', 613 | align: 'bottom-left' 614 | }, { 615 | start: 'start', 616 | align: 'bottom' 617 | }] 618 | }); 619 | 620 | assert.notOk( 621 | this.player.controlBar.$('.vjs-overlay.vjs-overlay-bottom-left'), 622 | 'bottom-left is not attached to control bar' 623 | ); 624 | 625 | assert.notOk( 626 | this.player.controlBar.$('.vjs-overlay.vjs-overlay-bottom'), 627 | 'bottom is not attached to control bar' 628 | ); 629 | }); 630 | 631 | QUnit.test('can reinitialize the plugin on reset', function(assert) { 632 | assert.expect(3); 633 | 634 | const overlayPlugin = this.player.overlay({ 635 | attachToControlBar: true, 636 | overlays: [{ 637 | start: 'start', 638 | align: 'bottom-left' 639 | }, { 640 | start: 'start', 641 | align: 'top-right' 642 | }] 643 | }); 644 | 645 | overlayPlugin.reset({ 646 | overlays: [{ 647 | start: 'start', 648 | align: 'top-left' 649 | }] 650 | }); 651 | 652 | assert.notOk( 653 | this.player.$('.vjs-overlay.vjs-overlay-bottom-left'), 654 | 'previous bottom-left aligned overlay removed' 655 | ); 656 | 657 | assert.notOk( 658 | this.player.$('.vjs-overlay.vjs-overlay-top-right'), 659 | 'previous top-right aligned overlay removed' 660 | ); 661 | 662 | assert.ok( 663 | this.player.$('.vjs-overlay.vjs-overlay-top-left'), 664 | 'new top-left overlay added' 665 | ); 666 | }); 667 | 668 | QUnit.test('attach bottom overlay as first child when attachToControlBar is invalid component', function(assert) { 669 | assert.expect(1); 670 | 671 | this.player.overlay({ 672 | attachToControlBar: 'InvalidComponent', 673 | overlays: [{ 674 | start: 'start', 675 | align: 'bottom' 676 | }] 677 | }); 678 | 679 | this.player.trigger('start'); 680 | 681 | assert.equal( 682 | this.player.$('.vjs-overlay.vjs-overlay-bottom'), 683 | this.player.controlBar.el().firstChild, 684 | 'bottom attaches as first child of controlBar' 685 | ); 686 | }); 687 | 688 | QUnit.test('attach top overlay as previous sibling when attachToControlBar is invalid component', function(assert) { 689 | assert.expect(1); 690 | 691 | this.player.overlay({ 692 | attachToControlBar: 'InvalidComponent', 693 | overlays: [{ 694 | start: 'start', 695 | align: 'top' 696 | }] 697 | }); 698 | 699 | this.player.trigger('start'); 700 | 701 | assert.equal( 702 | this.player.$('.vjs-overlay.vjs-overlay-top'), 703 | this.player.controlBar.el().previousSibling, 704 | 'top attaches as previous sibiling of controlBar' 705 | ); 706 | }); 707 | 708 | QUnit.test('attach overlays when attachToControlBar is true', function(assert) { 709 | assert.expect(4); 710 | 711 | const overlayPlugin = this.player.overlay({ 712 | attachToControlBar: true, 713 | overlays: [{ 714 | start: 'start', 715 | align: 'bottom' 716 | }] 717 | }); 718 | 719 | this.player.trigger('start'); 720 | 721 | assert.equal( 722 | this.player.controlBar.$('.vjs-overlay.vjs-overlay-bottom'), 723 | this.player.controlBar.el().firstChild, 724 | 'bottom attaches as first child of control bar' 725 | ); 726 | 727 | overlayPlugin.reset({ 728 | attachToControlBar: true, 729 | overlays: [{ 730 | start: 'start', 731 | align: 'top' 732 | }] 733 | }); 734 | 735 | this.player.trigger('start'); 736 | 737 | assert.equal( 738 | this.player.$('.vjs-overlay.vjs-overlay-top'), 739 | this.player.controlBar.el().previousSibling, 740 | 'top attaches as previous sibiling of controlBar' 741 | ); 742 | 743 | overlayPlugin.reset({ 744 | attachToControlBar: 'RemainingTimeDisplay', 745 | overlays: [{ 746 | start: 'start', 747 | align: 'bottom' 748 | }] 749 | }); 750 | 751 | this.player.trigger('start'); 752 | 753 | assert.equal( 754 | this.player.controlBar.$('.vjs-overlay.vjs-overlay-bottom'), 755 | this.player.controlBar.remainingTimeDisplay.el().previousSibling, 756 | 'bottom attaches as previous sibiling of attachToControlBar component' 757 | ); 758 | 759 | overlayPlugin.reset({ 760 | attachToControlBar: 'RemainingTimeDisplay', 761 | overlays: [{ 762 | start: 'start', 763 | align: 'top' 764 | }] 765 | }); 766 | 767 | this.player.trigger('start'); 768 | 769 | assert.equal( 770 | this.player.$('.vjs-overlay.vjs-overlay-top'), 771 | this.player.controlBar.el().previousSibling, 772 | 'top attaches as previous sibiling of controlBar when using attachToControlBar component' 773 | ); 774 | }); 775 | 776 | QUnit.test('attach overlays as last child when no controls are present', function(assert) { 777 | assert.expect(2); 778 | this.player.controls(false); 779 | 780 | const overlayPlugin = this.player.overlay({ 781 | overlays: [{ 782 | start: 'start', 783 | align: 'bottom' 784 | }] 785 | }); 786 | 787 | this.player.trigger('start'); 788 | 789 | assert.equal( 790 | this.player.$('.vjs-overlay.vjs-overlay-bottom'), 791 | this.player.el().lastChild, 792 | 'bottom attaches as last child of player' 793 | ); 794 | 795 | overlayPlugin.reset({ 796 | overlays: [{ 797 | start: 'start', 798 | align: 'top' 799 | }] 800 | }); 801 | 802 | this.player.trigger('start'); 803 | 804 | assert.equal( 805 | this.player.$('.vjs-overlay.vjs-overlay-top'), 806 | this.player.el().lastChild, 807 | 'top attaches as last child of player' 808 | ); 809 | }); 810 | 811 | QUnit.test('can get all existing overlays with the `get` fn', function(assert) { 812 | assert.expect(1); 813 | this.player.controls(false); 814 | 815 | const overlay = this.player.overlay({ 816 | overlays: [{ 817 | content: 'this is the first overlay', 818 | start: 'start', 819 | align: 'bottom' 820 | }] 821 | }); 822 | 823 | this.player.trigger('start'); 824 | 825 | const overlays = overlay.get(); 826 | 827 | assert.equal(overlays[0].options_.content, 'this is the first overlay'); 828 | }); 829 | 830 | QUnit.test('can add an individual overlay using the `add` fn', function(assert) { 831 | assert.expect(3); 832 | this.player.controls(false); 833 | 834 | const overlay = this.player.overlay({ 835 | overlays: [{ 836 | start: 'start', 837 | align: 'bottom' 838 | }] 839 | }); 840 | 841 | this.player.trigger('start'); 842 | 843 | assert.equal( 844 | this.player.$('.vjs-overlay.vjs-overlay-bottom'), 845 | this.player.el().lastChild, 846 | 'initial bottom overlay is attached as last child of player' 847 | ); 848 | 849 | const addedOverlay = overlay.add({content: 'newly added overlay', start: 'start', align: 'top'}); 850 | 851 | assert.equal(addedOverlay[0].options_.content, 'newly added overlay', 'added overlay object is returned by `add` fn'); 852 | 853 | this.player.trigger('start'); 854 | assert.equal( 855 | this.player.$('.vjs-overlay.vjs-overlay-top'), 856 | this.player.el().lastChild, 857 | 'top gets added as last child of player' 858 | ); 859 | }); 860 | 861 | QUnit.test('can add a list of overlays using the `add` fn', function(assert) { 862 | assert.expect(2); 863 | this.player.controls(false); 864 | 865 | const overlay = this.player.overlay(); 866 | 867 | overlay.add([{start: 'start', align: 'top'}, {start: 'start', align: 'bottom'}]); 868 | 869 | this.player.trigger('start'); 870 | 871 | assert.equal( 872 | this.player.$('.vjs-overlay.vjs-overlay-bottom'), 873 | this.player.el().lastChild, 874 | 'bottom gets added as last child of player' 875 | ); 876 | 877 | assert.equal( 878 | this.player.$('.vjs-overlay.vjs-overlay-top'), 879 | this.player.el().lastChild.previousSibling, 880 | 'top gets added as second last child of player' 881 | ); 882 | }); 883 | 884 | QUnit.test('can remove an overlay using the `remove` fn', function(assert) { 885 | assert.expect(2); 886 | this.player.controls(false); 887 | 888 | const overlay = this.player.overlay({ 889 | overlays: [{ 890 | start: 'start', 891 | align: 'bottom' 892 | }, { 893 | start: 'start', 894 | align: 'top' 895 | }] 896 | }); 897 | 898 | assert.equal( 899 | this.player.$('.vjs-overlay.vjs-overlay-bottom'), 900 | this.player.el().lastChild.previousSibling, 901 | 'bottom gets added as second last child of player' 902 | ); 903 | 904 | overlay.remove(overlay.get()[0]); 905 | 906 | assert.notOk( 907 | this.player.$('.vjs-overlay.vjs-overlay-bottom'), 908 | 'bottom overlay has been removed' 909 | ); 910 | }); 911 | 912 | QUnit.test('`remove` fn does not remove anything if an invalid overlay is passed into it', function(assert) { 913 | assert.expect(2); 914 | this.player.controls(false); 915 | 916 | const overlay = this.player.overlay({ 917 | overlays: [{ 918 | start: 'start', 919 | align: 'bottom' 920 | }] 921 | }); 922 | 923 | assert.equal( 924 | this.player.$('.vjs-overlay.vjs-overlay-bottom'), 925 | this.player.el().lastChild, 926 | 'bottom gets added as last child of player' 927 | ); 928 | 929 | overlay.remove(undefined); 930 | 931 | assert.equal( 932 | this.player.$('.vjs-overlay.vjs-overlay-bottom'), 933 | this.player.el().lastChild, 934 | 'bottom is still last child of player' 935 | ); 936 | }); 937 | --------------------------------------------------------------------------------