├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── .husky ├── .gitignore └── pre-commit ├── .npmignore ├── .nvmrc ├── .yo-rc.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── index.html ├── lang ├── de.json ├── en.json └── it.json ├── package-lock.json ├── package.json ├── scripts ├── karma.conf.js ├── postcss.config.js └── rollup.config.js ├── src ├── plugin.css ├── plugin.js └── touchOverlay.js └── 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 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | commit-message: 8 | prefix: fix 9 | prefix-development: chore 10 | include: scope 11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | should-skip: 7 | continue-on-error: true 8 | runs-on: ubuntu-latest 9 | # Map a step output to a job output 10 | outputs: 11 | should-skip-job: ${{steps.skip-check.outputs.should_skip}} 12 | steps: 13 | - id: skip-check 14 | uses: fkirc/skip-duplicate-actions@v2.1.0 15 | with: 16 | github_token: ${{github.token}} 17 | 18 | ci: 19 | needs: should-skip 20 | if: ${{needs.should-skip.outputs.should-skip-job != 'true' || github.ref == 'refs/heads/main'}} 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | os: [ubuntu-latest] 25 | env: 26 | BROWSER_STACK_USERNAME: ${{secrets.BROWSER_STACK_USERNAME}} 27 | BROWSER_STACK_ACCESS_KEY: ${{secrets.BROWSER_STACK_ACCESS_KEY}} 28 | runs-on: ${{matrix.os}} 29 | steps: 30 | - name: checkout code 31 | uses: actions/checkout@v2 32 | 33 | - name: Cache dependencies 34 | uses: actions/cache@v2 35 | with: 36 | path: | 37 | ~/.npm 38 | **/node_modules 39 | key: ${{runner.os}}-npm-${{hashFiles('**/package-lock.json')}} 40 | restore-keys: | 41 | ${{runner.os}}-npm- 42 | ${{runner.os}}- 43 | 44 | - name: read node version from .nvmrc 45 | run: echo ::set-output name=NVMRC::$(cat .nvmrc) 46 | shell: bash 47 | id: nvm 48 | 49 | - name: update apt cache on linux w/o browserstack 50 | run: sudo apt-get update 51 | if: ${{startsWith(matrix.os, 'ubuntu') && !env.BROWSER_STACK_USERNAME}} 52 | 53 | - name: install ffmpeg/pulseaudio for firefox on linux w/o browserstack 54 | run: sudo apt-get install ffmpeg pulseaudio 55 | if: ${{startsWith(matrix.os, 'ubuntu') && !env.BROWSER_STACK_USERNAME}} 56 | 57 | - name: start pulseaudio for firefox on linux w/o browserstack 58 | run: pulseaudio -D 59 | if: ${{startsWith(matrix.os, 'ubuntu') && !env.BROWSER_STACK_USERNAME}} 60 | 61 | - name: setup node 62 | uses: actions/setup-node@v1 63 | with: 64 | node-version: '${{steps.nvm.outputs.NVMRC}}' 65 | 66 | # turn off the default setup-node problem watchers... 67 | - run: echo "::remove-matcher owner=eslint-compact::" 68 | - run: echo "::remove-matcher owner=eslint-stylish::" 69 | - run: echo "::remove-matcher owner=tsc::" 70 | 71 | - name: npm install 72 | run: npm i --prefer-offline --no-audit 73 | 74 | - name: run npm test 75 | uses: GabrielBB/xvfb-action@v1 76 | with: 77 | run: npm run test 78 | -------------------------------------------------------------------------------- /.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 | es/ 31 | cjs/ 32 | docs/api/ 33 | test/dist/ 34 | .eslintcache 35 | .yo-rc.json 36 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm test -------------------------------------------------------------------------------- /.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 | 14 2 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-videojs-plugin": { 3 | "scope": "", 4 | "name": "mobile-ui", 5 | "description": "Mobile tap controls and fullscreen on rotate for Video.js", 6 | "author": "mister-ben ", 7 | "license": "mit", 8 | "pluginType": "basic", 9 | "css": true, 10 | "docs": true, 11 | "lang": true, 12 | "library": false, 13 | "precommit": true, 14 | "prepush": false 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## [1.1.1](https://github.com/mister-ben/videojs-mobile-ui/compare/v1.1.0...v1.1.1) (2023-09-22) 3 | 4 | ### Bug Fixes 5 | 6 | * Hide overlay during ad playback (#151) ([5b1aa34](https://github.com/mister-ben/videojs-mobile-ui/commit/5b1aa34)), closes [#151](https://github.com/mister-ben/videojs-mobile-ui/issues/151) 7 | 8 | ### Chores 9 | 10 | * **deps-dev:** bump [@babel](https://github.com/babel)/runtime from 7.22.6 to 7.22.15 (#148) ([610f985](https://github.com/mister-ben/videojs-mobile-ui/commit/610f985)), closes [#148](https://github.com/mister-ben/videojs-mobile-ui/issues/148) 11 | * **deps-dev:** bump postcss from 8.4.24 to 8.4.27 (#138) ([adcaa50](https://github.com/mister-ben/videojs-mobile-ui/commit/adcaa50)), closes [#138](https://github.com/mister-ben/videojs-mobile-ui/issues/138) 12 | * **deps-dev:** bump postcss from 8.4.27 to 8.4.30 (#149) ([e288c69](https://github.com/mister-ben/videojs-mobile-ui/commit/e288c69)), closes [#149](https://github.com/mister-ben/videojs-mobile-ui/issues/149) 13 | * **deps-dev:** bump video.js from 8.3.0 to 8.5.2 (#143) ([98fd09d](https://github.com/mister-ben/videojs-mobile-ui/commit/98fd09d)), closes [#143](https://github.com/mister-ben/videojs-mobile-ui/issues/143) 14 | * **deps-dev:** bump videojs-generate-rollup-config from 7.0.1 to 7.0.2 (#144) ([69cc17b](https://github.com/mister-ben/videojs-mobile-ui/commit/69cc17b)), closes [#144](https://github.com/mister-ben/videojs-mobile-ui/issues/144) 15 | * **deps-dev:** bump videojs-generator-verify from 4.1.0 to 4.1.1 (#139) ([7986622](https://github.com/mister-ben/videojs-mobile-ui/commit/7986622)), closes [#139](https://github.com/mister-ben/videojs-mobile-ui/issues/139) 16 | * **deps-dev:** bump videojs-generator-verify from 4.1.1 to 4.1.2 (#146) ([d320237](https://github.com/mister-ben/videojs-mobile-ui/commit/d320237)), closes [#146](https://github.com/mister-ben/videojs-mobile-ui/issues/146) 17 | * **deps-dev:** bump word-wrap from 1.2.3 to 1.2.5 (#140) ([4230c45](https://github.com/mister-ben/videojs-mobile-ui/commit/4230c45)), closes [#140](https://github.com/mister-ben/videojs-mobile-ui/issues/140) 18 | 19 | 20 | # [1.1.0](https://github.com/mister-ben/videojs-mobile-ui/compare/v1.0.1...v1.1.0) (2023-07-04) 21 | 22 | ### Features 23 | 24 | * Increase CSS specificity (#130) ([9e07684](https://github.com/mister-ben/videojs-mobile-ui/commit/9e07684)), closes [#130](https://github.com/mister-ben/videojs-mobile-ui/issues/130) 25 | 26 | ### Bug Fixes 27 | 28 | * **deps:** bump engine.io and socket.io (#119) ([07d0eef](https://github.com/mister-ben/videojs-mobile-ui/commit/07d0eef)), closes [#119](https://github.com/mister-ben/videojs-mobile-ui/issues/119) 29 | * **deps:** bump socket.io-parser from 4.2.1 to 4.2.4 (#124) ([e30fb2f](https://github.com/mister-ben/videojs-mobile-ui/commit/e30fb2f)), closes [#124](https://github.com/mister-ben/videojs-mobile-ui/issues/124) 30 | * **deps:** bump ua-parser-js from 0.7.31 to 0.7.33 (#106) ([a3e2487](https://github.com/mister-ben/videojs-mobile-ui/commit/a3e2487)), closes [#106](https://github.com/mister-ben/videojs-mobile-ui/issues/106) 31 | 32 | ### Chores 33 | 34 | * **deps-dev:** bump [@babel](https://github.com/babel)/runtime from 7.20.13 to 7.22.3 (#123) ([298df4d](https://github.com/mister-ben/videojs-mobile-ui/commit/298df4d)), closes [#123](https://github.com/mister-ben/videojs-mobile-ui/issues/123) 35 | * **deps-dev:** bump [@babel](https://github.com/babel)/runtime from 7.20.7 to 7.20.13 (#105) ([c955038](https://github.com/mister-ben/videojs-mobile-ui/commit/c955038)), closes [#105](https://github.com/mister-ben/videojs-mobile-ui/issues/105) 36 | * **deps-dev:** bump [@babel](https://github.com/babel)/runtime from 7.22.3 to 7.22.5 (#127) ([db4efbe](https://github.com/mister-ben/videojs-mobile-ui/commit/db4efbe)), closes [#127](https://github.com/mister-ben/videojs-mobile-ui/issues/127) 37 | * **deps-dev:** bump [@babel](https://github.com/babel)/runtime from 7.22.5 to 7.22.6 (#132) ([59eb889](https://github.com/mister-ben/videojs-mobile-ui/commit/59eb889)), closes [#132](https://github.com/mister-ben/videojs-mobile-ui/issues/132) 38 | * **deps-dev:** bump jsdoc from 4.0.0 to 4.0.2 (#109) ([1be4215](https://github.com/mister-ben/videojs-mobile-ui/commit/1be4215)), closes [#109](https://github.com/mister-ben/videojs-mobile-ui/issues/109) 39 | * **deps-dev:** bump karma from 6.4.1 to 6.4.2 (#116) ([516a58a](https://github.com/mister-ben/videojs-mobile-ui/commit/516a58a)), closes [#116](https://github.com/mister-ben/videojs-mobile-ui/issues/116) 40 | * **deps-dev:** bump postcss from 8.4.21 to 8.4.23 (#115) ([1f7cdcf](https://github.com/mister-ben/videojs-mobile-ui/commit/1f7cdcf)), closes [#115](https://github.com/mister-ben/videojs-mobile-ui/issues/115) 41 | * **deps-dev:** bump postcss from 8.4.23 to 8.4.24 (#125) ([1e22f74](https://github.com/mister-ben/videojs-mobile-ui/commit/1e22f74)), closes [#125](https://github.com/mister-ben/videojs-mobile-ui/issues/125) 42 | * **deps-dev:** bump sinon from 15.0.1 to 15.0.4 (#113) ([1d1ff27](https://github.com/mister-ben/videojs-mobile-ui/commit/1d1ff27)), closes [#113](https://github.com/mister-ben/videojs-mobile-ui/issues/113) 43 | * **deps-dev:** bump sinon from 15.0.4 to 15.1.0 (#120) ([662907b](https://github.com/mister-ben/videojs-mobile-ui/commit/662907b)), closes [#120](https://github.com/mister-ben/videojs-mobile-ui/issues/120) 44 | * **deps-dev:** bump sinon from 15.1.0 to 15.1.2 (#129) ([c0ea3b1](https://github.com/mister-ben/videojs-mobile-ui/commit/c0ea3b1)), closes [#129](https://github.com/mister-ben/videojs-mobile-ui/issues/129) 45 | * **deps-dev:** bump sinon from 15.1.2 to 15.2.0 (#131) ([3a17cc0](https://github.com/mister-ben/videojs-mobile-ui/commit/3a17cc0)), closes [#131](https://github.com/mister-ben/videojs-mobile-ui/issues/131) 46 | * **deps-dev:** bump video.js from 8.0.3 to 8.0.4 (#107) ([5dbc197](https://github.com/mister-ben/videojs-mobile-ui/commit/5dbc197)), closes [#107](https://github.com/mister-ben/videojs-mobile-ui/issues/107) 47 | * **deps-dev:** bump video.js from 8.0.4 to 8.3.0 (#117) ([e91aaee](https://github.com/mister-ben/videojs-mobile-ui/commit/e91aaee)), closes [#117](https://github.com/mister-ben/videojs-mobile-ui/issues/117) 48 | * **deps-dev:** bump videojs-generate-rollup-config from 7.0.0 to 7.0.1 (#114) ([cd7b101](https://github.com/mister-ben/videojs-mobile-ui/commit/cd7b101)), closes [#114](https://github.com/mister-ben/videojs-mobile-ui/issues/114) 49 | * Miscellaneous cleanups (#126) ([dcc9d89](https://github.com/mister-ben/videojs-mobile-ui/commit/dcc9d89)), closes [#126](https://github.com/mister-ben/videojs-mobile-ui/issues/126) 50 | 51 | ### Documentation 52 | 53 | * add npm tag for v7 compatible version to README ([43bf4e9](https://github.com/mister-ben/videojs-mobile-ui/commit/43bf4e9)) 54 | 55 | 56 | ## [1.0.1](https://github.com/mister-ben/videojs-mobile-ui/compare/v1.0.0...v1.0.1) (2023-01-17) 57 | 58 | ### Bug Fixes 59 | 60 | * **deps:** bump decode-uri-component from 0.2.0 to 0.2.2 (#95) ([10ad7b2](https://github.com/mister-ben/videojs-mobile-ui/commit/10ad7b2)), closes [#95](https://github.com/mister-ben/videojs-mobile-ui/issues/95) 61 | * **deps:** bump json5 from 2.2.1 to 2.2.3 (#102) ([529d294](https://github.com/mister-ben/videojs-mobile-ui/commit/529d294)), closes [#102](https://github.com/mister-ben/videojs-mobile-ui/issues/102) 62 | 63 | ### Chores 64 | 65 | * **deps-dev:** bump [@babel](https://github.com/babel)/runtime from 7.19.4 to 7.20.1 (#90) ([21738b6](https://github.com/mister-ben/videojs-mobile-ui/commit/21738b6)), closes [#90](https://github.com/mister-ben/videojs-mobile-ui/issues/90) 66 | * **deps-dev:** bump [@babel](https://github.com/babel)/runtime from 7.20.1 to 7.20.6 (#93) ([8665941](https://github.com/mister-ben/videojs-mobile-ui/commit/8665941)), closes [#93](https://github.com/mister-ben/videojs-mobile-ui/issues/93) 67 | * **deps-dev:** bump [@babel](https://github.com/babel)/runtime from 7.20.6 to 7.20.7 (#98) ([35078a1](https://github.com/mister-ben/videojs-mobile-ui/commit/35078a1)), closes [#98](https://github.com/mister-ben/videojs-mobile-ui/issues/98) 68 | * **deps-dev:** bump husky from 8.0.1 to 8.0.2 (#89) ([ddfe61f](https://github.com/mister-ben/videojs-mobile-ui/commit/ddfe61f)), closes [#89](https://github.com/mister-ben/videojs-mobile-ui/issues/89) 69 | * **deps-dev:** bump husky from 8.0.2 to 8.0.3 (#101) ([0588b77](https://github.com/mister-ben/videojs-mobile-ui/commit/0588b77)), closes [#101](https://github.com/mister-ben/videojs-mobile-ui/issues/101) 70 | * **deps-dev:** bump karma from 6.3.16 to 6.4.1 (#88) ([3f94ac8](https://github.com/mister-ben/videojs-mobile-ui/commit/3f94ac8)), closes [#88](https://github.com/mister-ben/videojs-mobile-ui/issues/88) 71 | * **deps-dev:** bump postcss from 8.4.18 to 8.4.19 (#91) ([7bce777](https://github.com/mister-ben/videojs-mobile-ui/commit/7bce777)), closes [#91](https://github.com/mister-ben/videojs-mobile-ui/issues/91) 72 | * **deps-dev:** bump postcss from 8.4.19 to 8.4.20 (#96) ([500a0e5](https://github.com/mister-ben/videojs-mobile-ui/commit/500a0e5)), closes [#96](https://github.com/mister-ben/videojs-mobile-ui/issues/96) 73 | * **deps-dev:** bump postcss from 8.4.20 to 8.4.21 (#104) ([a9d9263](https://github.com/mister-ben/videojs-mobile-ui/commit/a9d9263)), closes [#104](https://github.com/mister-ben/videojs-mobile-ui/issues/104) 74 | * **deps-dev:** bump rollup from 2.46.0 to 2.79.1 (#86) ([af0499e](https://github.com/mister-ben/videojs-mobile-ui/commit/af0499e)), closes [#86](https://github.com/mister-ben/videojs-mobile-ui/issues/86) 75 | * **deps-dev:** bump sinon from 14.0.2 to 15.0.0 (#94) ([19e1c6e](https://github.com/mister-ben/videojs-mobile-ui/commit/19e1c6e)), closes [#94](https://github.com/mister-ben/videojs-mobile-ui/issues/94) 76 | * **deps-dev:** bump sinon from 15.0.0 to 15.0.1 (#97) ([8933ffb](https://github.com/mister-ben/videojs-mobile-ui/commit/8933ffb)), closes [#97](https://github.com/mister-ben/videojs-mobile-ui/issues/97) 77 | * **deps-dev:** bump video.js from 8.0.0 to 8.0.1 (#87) ([e4309d4](https://github.com/mister-ben/videojs-mobile-ui/commit/e4309d4)), closes [#87](https://github.com/mister-ben/videojs-mobile-ui/issues/87) 78 | * **deps-dev:** bump video.js from 8.0.1 to 8.0.2 (#92) ([1b74370](https://github.com/mister-ben/videojs-mobile-ui/commit/1b74370)), closes [#92](https://github.com/mister-ben/videojs-mobile-ui/issues/92) 79 | * **deps-dev:** bump video.js from 8.0.2 to 8.0.3 (#103) ([3328f40](https://github.com/mister-ben/videojs-mobile-ui/commit/3328f40)), closes [#103](https://github.com/mister-ben/videojs-mobile-ui/issues/103) 80 | 81 | 82 | # 1.0.0 (2022-11-23) 83 | 84 | ### Features 85 | 86 | * Add disable options (#33) ([59e5f89](https://github.com/mister-ben/videojs-mobile-ui/commit/59e5f89)), closes [#33](https://github.com/mister-ben/videojs-mobile-ui/issues/33) 87 | * Add option to lock to landscape when entering fullscreen (#49) ([26c58d4](https://github.com/mister-ben/videojs-mobile-ui/commit/26c58d4)), closes [#49](https://github.com/mister-ben/videojs-mobile-ui/issues/49) 88 | 89 | ### Bug Fixes 90 | 91 | * **deps:** bump engine.io from 6.2.0 to 6.2.1 (#83) ([f9c4e89](https://github.com/mister-ben/videojs-mobile-ui/commit/f9c4e89)), closes [#83](https://github.com/mister-ben/videojs-mobile-ui/issues/83) 92 | * **deps:** bump minimatch from 3.0.4 to 3.1.2 (#81) ([d799f20](https://github.com/mister-ben/videojs-mobile-ui/commit/d799f20)), closes [#81](https://github.com/mister-ben/videojs-mobile-ui/issues/81) 93 | * **deps:** bump trim and videojs-standard (#64) ([581dce1](https://github.com/mister-ben/videojs-mobile-ui/commit/581dce1)), closes [#64](https://github.com/mister-ben/videojs-mobile-ui/issues/64) 94 | * Detect angle correctly where no window.orientation (#17) ([c85ac35](https://github.com/mister-ben/videojs-mobile-ui/commit/c85ac35)), closes [#17](https://github.com/mister-ben/videojs-mobile-ui/issues/17) 95 | * Fix fullscreen on rotate (#14) ([33093e4](https://github.com/mister-ben/videojs-mobile-ui/commit/33093e4)), closes [#14](https://github.com/mister-ben/videojs-mobile-ui/issues/14) 96 | * Fix typo in iOS orientation (#27) ([7e05afb](https://github.com/mister-ben/videojs-mobile-ui/commit/7e05afb)), closes [#27](https://github.com/mister-ben/videojs-mobile-ui/issues/27) 97 | * orientation string (#37) ([177f326](https://github.com/mister-ben/videojs-mobile-ui/commit/177f326)), closes [#37](https://github.com/mister-ben/videojs-mobile-ui/issues/37) 98 | * Remove event listeners on player dispose (#21) ([25c8fb1](https://github.com/mister-ben/videojs-mobile-ui/commit/25c8fb1)), closes [#21](https://github.com/mister-ben/videojs-mobile-ui/issues/21) 99 | * Split orientation string ([0eebd64](https://github.com/mister-ben/videojs-mobile-ui/commit/0eebd64)) 100 | 101 | ### Chores 102 | 103 | * **deps-dev:** bump [@babel](https://github.com/babel)/runtime from 7.14.0 to 7.19.4 (#66) ([c9c06af](https://github.com/mister-ben/videojs-mobile-ui/commit/c9c06af)), closes [#66](https://github.com/mister-ben/videojs-mobile-ui/issues/66) 104 | * **deps-dev:** bump [@babel](https://github.com/babel)/runtime from 7.19.4 to 7.20.1 (#76) ([1ec5b0c](https://github.com/mister-ben/videojs-mobile-ui/commit/1ec5b0c)), closes [#76](https://github.com/mister-ben/videojs-mobile-ui/issues/76) 105 | * **deps-dev:** bump husky from 6.0.0 to 8.0.1 (#67) ([2876f35](https://github.com/mister-ben/videojs-mobile-ui/commit/2876f35)), closes [#67](https://github.com/mister-ben/videojs-mobile-ui/issues/67) 106 | * **deps-dev:** bump husky from 8.0.1 to 8.0.2 (#79) ([4d90713](https://github.com/mister-ben/videojs-mobile-ui/commit/4d90713)), closes [#79](https://github.com/mister-ben/videojs-mobile-ui/issues/79) 107 | * **deps-dev:** bump jsdoc from 3.6.11 to 4.0.0 (#78) ([ac4b4b4](https://github.com/mister-ben/videojs-mobile-ui/commit/ac4b4b4)), closes [#78](https://github.com/mister-ben/videojs-mobile-ui/issues/78) 108 | * **deps-dev:** bump karma from 6.3.14 to 6.3.16 (#46) ([7bd42f1](https://github.com/mister-ben/videojs-mobile-ui/commit/7bd42f1)), closes [#46](https://github.com/mister-ben/videojs-mobile-ui/issues/46) 109 | * **deps-dev:** bump karma from 6.3.16 to 6.4.1 (#71) ([147dea6](https://github.com/mister-ben/videojs-mobile-ui/commit/147dea6)), closes [#71](https://github.com/mister-ben/videojs-mobile-ui/issues/71) 110 | * **deps-dev:** bump karma from 6.3.2 to 6.3.14 (#44) ([ddff196](https://github.com/mister-ben/videojs-mobile-ui/commit/ddff196)), closes [#44](https://github.com/mister-ben/videojs-mobile-ui/issues/44) 111 | * **deps-dev:** bump postcss from 8.2.13 to 8.4.18 (#65) ([62fdd71](https://github.com/mister-ben/videojs-mobile-ui/commit/62fdd71)), closes [#65](https://github.com/mister-ben/videojs-mobile-ui/issues/65) 112 | * **deps-dev:** bump postcss from 8.4.18 to 8.4.19 (#80) ([e759ac4](https://github.com/mister-ben/videojs-mobile-ui/commit/e759ac4)), closes [#80](https://github.com/mister-ben/videojs-mobile-ui/issues/80) 113 | * **deps-dev:** bump rollup from 2.46.0 to 2.79.1 (#73) ([46a80cd](https://github.com/mister-ben/videojs-mobile-ui/commit/46a80cd)), closes [#73](https://github.com/mister-ben/videojs-mobile-ui/issues/73) 114 | * **deps-dev:** bump sinon from 14.0.1 to 14.0.2 (#77) ([9afd1a8](https://github.com/mister-ben/videojs-mobile-ui/commit/9afd1a8)), closes [#77](https://github.com/mister-ben/videojs-mobile-ui/issues/77) 115 | * **deps-dev:** bump sinon from 9.2.4 to 14.0.1 (#72) ([993b688](https://github.com/mister-ben/videojs-mobile-ui/commit/993b688)), closes [#72](https://github.com/mister-ben/videojs-mobile-ui/issues/72) 116 | * **deps-dev:** bump video.js from 7.18.1 to 7.20.3 (#68) ([9addd31](https://github.com/mister-ben/videojs-mobile-ui/commit/9addd31)), closes [#68](https://github.com/mister-ben/videojs-mobile-ui/issues/68) 117 | * **deps-dev:** bump videojs-generate-karma-config from 8.0.0 to 8.0.1 (#69) ([a78e527](https://github.com/mister-ben/videojs-mobile-ui/commit/a78e527)), closes [#69](https://github.com/mister-ben/videojs-mobile-ui/issues/69) 118 | * **deps-dev:** bump videojs-generate-rollup-config from 6.2.0 to 7.0.0 (#70) ([2c3e43d](https://github.com/mister-ben/videojs-mobile-ui/commit/2c3e43d)), closes [#70](https://github.com/mister-ben/videojs-mobile-ui/issues/70) 119 | * **deps:** bump [@xmldom](https://github.com/xmldom)/xmldom from 0.7.5 to 0.7.6 (#56) ([22d4e22](https://github.com/mister-ben/videojs-mobile-ui/commit/22d4e22)), closes [#56](https://github.com/mister-ben/videojs-mobile-ui/issues/56) 120 | * **deps:** bump ansi-regex and ansi-regex (#57) ([46f4326](https://github.com/mister-ben/videojs-mobile-ui/commit/46f4326)), closes [#57](https://github.com/mister-ben/videojs-mobile-ui/issues/57) 121 | * **deps:** bump engine.io from 4.1.1 to 4.1.2 (#39) ([5031c93](https://github.com/mister-ben/videojs-mobile-ui/commit/5031c93)), closes [#39](https://github.com/mister-ben/videojs-mobile-ui/issues/39) 122 | * **deps:** bump follow-redirects from 1.14.0 to 1.14.7 (#41) ([6fcbb49](https://github.com/mister-ben/videojs-mobile-ui/commit/6fcbb49)), closes [#41](https://github.com/mister-ben/videojs-mobile-ui/issues/41) 123 | * **deps:** bump follow-redirects from 1.14.7 to 1.14.8 (#45) ([cf20418](https://github.com/mister-ben/videojs-mobile-ui/commit/cf20418)), closes [#45](https://github.com/mister-ben/videojs-mobile-ui/issues/45) 124 | * **deps:** bump log4js from 6.3.0 to 6.4.0 (#42) ([f9c69ff](https://github.com/mister-ben/videojs-mobile-ui/commit/f9c69ff)), closes [#42](https://github.com/mister-ben/videojs-mobile-ui/issues/42) 125 | * **deps:** bump marked from 2.0.5 to 4.1.1 (#58) ([e7a2353](https://github.com/mister-ben/videojs-mobile-ui/commit/e7a2353)), closes [#58](https://github.com/mister-ben/videojs-mobile-ui/issues/58) 126 | * **deps:** bump minimist from 1.2.5 to 1.2.6 (#50) ([6faea79](https://github.com/mister-ben/videojs-mobile-ui/commit/6faea79)), closes [#50](https://github.com/mister-ben/videojs-mobile-ui/issues/50) 127 | * **deps:** bump nanoid from 3.1.22 to 3.2.0 (#43) ([70e3440](https://github.com/mister-ben/videojs-mobile-ui/commit/70e3440)), closes [#43](https://github.com/mister-ben/videojs-mobile-ui/issues/43) 128 | * **deps:** bump semver-regex from 3.1.3 to 3.1.4 (#52) ([33f2b4a](https://github.com/mister-ben/videojs-mobile-ui/commit/33f2b4a)), closes [#52](https://github.com/mister-ben/videojs-mobile-ui/issues/52) 129 | * **deps:** bump shell-quote from 1.7.2 to 1.7.3 (#53) ([4e77fdc](https://github.com/mister-ben/videojs-mobile-ui/commit/4e77fdc)), closes [#53](https://github.com/mister-ben/videojs-mobile-ui/issues/53) 130 | * **deps:** bump shelljs from 0.8.4 to 0.8.5 (#40) ([bf57915](https://github.com/mister-ben/videojs-mobile-ui/commit/bf57915)), closes [#40](https://github.com/mister-ben/videojs-mobile-ui/issues/40) 131 | * **deps:** bump terser from 5.7.0 to 5.14.2 (#54) ([47528c0](https://github.com/mister-ben/videojs-mobile-ui/commit/47528c0)), closes [#54](https://github.com/mister-ben/videojs-mobile-ui/issues/54) 132 | * **deps:** bump trim-newlines and meow (#59) ([5944580](https://github.com/mister-ben/videojs-mobile-ui/commit/5944580)), closes [#59](https://github.com/mister-ben/videojs-mobile-ui/issues/59) 133 | * **deps:** bump underscore and [@videojs](https://github.com/videojs)/generator-helpers (#60) ([66fb160](https://github.com/mister-ben/videojs-mobile-ui/commit/66fb160)), closes [#60](https://github.com/mister-ben/videojs-mobile-ui/issues/60) 134 | * make video.js a peer dependency (#51) ([1e1ae5d](https://github.com/mister-ben/videojs-mobile-ui/commit/1e1ae5d)), closes [#51](https://github.com/mister-ben/videojs-mobile-ui/issues/51) 135 | * update dependencies ([6fb5a7b](https://github.com/mister-ben/videojs-mobile-ui/commit/6fb5a7b)) 136 | * Update deps for release version of v8 and merge into next (#84) ([ccf2f4f](https://github.com/mister-ben/videojs-mobile-ui/commit/ccf2f4f)), closes [#84](https://github.com/mister-ben/videojs-mobile-ui/issues/84) [#74](https://github.com/mister-ben/videojs-mobile-ui/issues/74) [#82](https://github.com/mister-ben/videojs-mobile-ui/issues/82) 137 | * Update to Node 14 / Generator 8 (#16) ([660dc86](https://github.com/mister-ben/videojs-mobile-ui/commit/660dc86)), closes [#16](https://github.com/mister-ben/videojs-mobile-ui/issues/16) 138 | * Updated package-lock ([1846941](https://github.com/mister-ben/videojs-mobile-ui/commit/1846941)) 139 | * Use conventional commit prefixes with dependabot (#63) ([018fa7e](https://github.com/mister-ben/videojs-mobile-ui/commit/018fa7e)), closes [#63](https://github.com/mister-ben/videojs-mobile-ui/issues/63) 140 | 141 | 142 | # [0.7.0](https://github.com/mister-ben/videojs-mobile-ui/compare/v0.6.1...v0.7.0) (2021-11-09) 143 | 144 | ### Features 145 | 146 | * Add disable options (#33) ([59e5f89](https://github.com/mister-ben/videojs-mobile-ui/commit/59e5f89)), closes [#33](https://github.com/mister-ben/videojs-mobile-ui/issues/33) 147 | 148 | ### Bug Fixes 149 | 150 | * orientation string (#37) ([177f326](https://github.com/mister-ben/videojs-mobile-ui/commit/177f326)), closes [#37](https://github.com/mister-ben/videojs-mobile-ui/issues/37) 151 | 152 | ### Chores 153 | 154 | * update dependencies ([6fb5a7b](https://github.com/mister-ben/videojs-mobile-ui/commit/6fb5a7b)) 155 | 156 | 157 | ## [0.6.1](https://github.com/mister-ben/videojs-mobile-ui/compare/v0.6.0...v0.6.1) (2021-08-27) 158 | 159 | ### Bug Fixes 160 | 161 | * Split orientation string ([0eebd64](https://github.com/mister-ben/videojs-mobile-ui/commit/0eebd64)) 162 | 163 | 164 | # 0.6.0 (2021-08-27) 165 | 166 | ### Bug Fixes 167 | 168 | * Detect angle correctly where no window.orientation (#17) ([c85ac35](https://github.com/mister-ben/videojs-mobile-ui/commit/c85ac35)), closes [#17](https://github.com/mister-ben/videojs-mobile-ui/issues/17) 169 | * Fix fullscreen on rotate (#14) ([33093e4](https://github.com/mister-ben/videojs-mobile-ui/commit/33093e4)), closes [#14](https://github.com/mister-ben/videojs-mobile-ui/issues/14) 170 | * Fix typo in iOS orientation (#27) ([7e05afb](https://github.com/mister-ben/videojs-mobile-ui/commit/7e05afb)), closes [#27](https://github.com/mister-ben/videojs-mobile-ui/issues/27) 171 | * Remove event listeners on player dispose (#21) ([25c8fb1](https://github.com/mister-ben/videojs-mobile-ui/commit/25c8fb1)), closes [#21](https://github.com/mister-ben/videojs-mobile-ui/issues/21) 172 | 173 | ### Chores 174 | 175 | * Update to Node 14 / Generator 8 (#16) ([660dc86](https://github.com/mister-ben/videojs-mobile-ui/commit/660dc86)), closes [#16](https://github.com/mister-ben/videojs-mobile-ui/issues/16) 176 | 177 | 178 | ## [0.5.3](https://github.com/mister-ben/videojs-mobile-ui/compare/v0.5.2...v0.5.3) (2021-05-25) 179 | 180 | ### Bug Fixes 181 | 182 | * Fix fullscreen on rotate (#14) ([33093e4](https://github.com/mister-ben/videojs-mobile-ui/commit/33093e4)), closes [#14](https://github.com/mister-ben/videojs-mobile-ui/issues/14) 183 | 184 | 185 | ## [0.5.2](https://github.com/mister-ben/videojs-mobile-ui/compare/v0.5.1...v0.5.2) (2021-01-29) 186 | 187 | 188 | ## [0.5.1](https://github.com/mister-ben/videojs-mobile-ui/compare/v0.5.0...v0.5.1) (2021-01-29) 189 | 190 | 191 | # 0.5.0 (2021-01-16) 192 | 193 | 194 | ## [0.4.1](https://github.com/mister-ben/videojs-mobile-ui/compare/v0.4.0...v0.4.1) (2018-04-30) 195 | 196 | 197 | # [0.4.0](https://github.com/mister-ben/videojs-mobile-ui/compare/v0.3.0...v0.4.0) (2018-04-30) 198 | 199 | 200 | # [0.3.0](https://github.com/mister-ben/videojs-mobile-ui/compare/v0.2.0...v0.3.0) (2018-04-28) 201 | 202 | 203 | # 0.2.0 (2018-04-28) 204 | 205 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | We welcome contributions from everyone! 4 | 5 | ## Getting Started 6 | 7 | Make sure you have Node.js 14 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 (c) mister-ben 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # videojs-mobile-ui 2 | 3 | Mobile UI for Video.js. 4 | 5 | Touch controls: 6 | 7 | - Double-tap the left side of the player to rewind ten seconds 8 | - Double-tap the right side of the player to fast-forward ten seconds 9 | - Single-tap the screen to show a play/pause toggle 10 | 11 | Fullscreen control: 12 | 13 | - Rotate to landscape to enter Fullscreen 14 | - Lock to fullscreen on rotate 15 | - Always lock to landscape when entering fullscreen (works even when device rotation is disabled/non-functional) 16 | 17 | ## Table of Contents 18 | 19 | 20 | 21 | ## Installation 22 | 23 | - [Installation](#installation) 24 | - [Plugin Options](#plugin-options) 25 | - [Default options](#default-options) 26 | - [Options](#options) 27 | - [Usage](#usage) 28 | - [` 92 | 93 | 98 | ``` 99 | 100 | The release versions will be available on jdselivr, unpkg etc. 101 | 102 | * https://cdn.jsdelivr.net/npm/videojs-mobile-ui/dist/videojs-mobile-ui.min.js 103 | * https://cdn.jsdelivr.net/npm/videojs-mobile-ui/dist/videojs-mobile-ui.css 104 | 105 | ### Browserify/CommonJS 106 | 107 | When using with Browserify, install videojs-mobile-ui via npm and `require` the plugin as you would any other module. 108 | 109 | ```js 110 | var videojs = require('video.js'); 111 | 112 | // The actual plugin function is exported by this module, but it is also 113 | // attached to the `Player.prototype`; so, there is no need to assign it 114 | // to a variable. 115 | require('videojs-mobile-ui'); 116 | 117 | var player = videojs('my-video'); 118 | 119 | player.mobileUi(); 120 | ``` 121 | 122 | Also include the CSS. 123 | 124 | ### RequireJS/AMD 125 | 126 | When using with RequireJS (or another AMD library), get the script in whatever way you prefer and `require` the plugin as you normally would: 127 | 128 | ```js 129 | require(['video.js', 'videojs-mobile-ui'], function(videojs) { 130 | var player = videojs('my-video'); 131 | 132 | player.mobileUi(); 133 | }); 134 | ``` 135 | 136 | Also include the CSS. 137 | 138 | ### Import 139 | 140 | To import into React etc import both the package and the script 141 | 142 | ```js 143 | import videojs from 'video.js' 144 | import 'videojs-mobile-ui/dist/videojs-mobile-ui.css'; 145 | import 'videojs-mobile-ui'; 146 | ``` 147 | 148 | ## License 149 | 150 | MIT. Copyright (c) mister-ben <git@misterben.me> 151 | 152 | 153 | [videojs]: http://videojs.com/ 154 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | videojs-mobile-ui Demo 7 | 8 | 9 | 19 | 20 | 21 | 27 | 28 | 29 | 33 |

Options

34 | 105 | 106 | 107 | 108 | 109 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /lang/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "seconds": "Sekunden" 3 | } 4 | -------------------------------------------------------------------------------- /lang/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "seconds": "seconds" 3 | } 4 | -------------------------------------------------------------------------------- /lang/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "seconds": "secondi" 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "videojs-mobile-ui", 3 | "version": "1.1.1", 4 | "description": "Mobile tap controls and fullscreen on rotate for Video.js", 5 | "main": "dist/videojs-mobile-ui.cjs.js", 6 | "module": "dist/videojs-mobile-ui.es.js", 7 | "repository": "https://github.com/mister-ben/videojs-mobile-ui.git", 8 | "browser": "dist/videojs-mobile-ui.js", 9 | "generator-videojs-plugin": { 10 | "version": "8.0.0" 11 | }, 12 | "scripts": { 13 | "build": "npm-run-all -s clean -p build:*", 14 | "build-prod": "cross-env-shell NO_TEST_BUNDLE=1 'npm run build'", 15 | "build-test": "cross-env-shell TEST_BUNDLE_ONLY=1 'npm run build'", 16 | "build:css": "postcss -o dist/videojs-mobile-ui.css --config scripts/postcss.config.js src/plugin.css", 17 | "build:js": "rollup -c scripts/rollup.config.js", 18 | "build:lang": "vjslang --dir dist/lang", 19 | "clean": "shx rm -rf ./dist ./test/dist ./cjs ./es && shx mkdir -p ./dist ./test/dist ./cjs ./es", 20 | "docs": "npm-run-all docs:*", 21 | "docs:api": "jsdoc src -r -d docs/api", 22 | "docs:toc": "doctoc --notitle README.md", 23 | "lint": "vjsstandard", 24 | "server": "karma start scripts/karma.conf.js --singleRun=false --auto-watch", 25 | "start": "npm-run-all -p server watch", 26 | "test": "npm-run-all lint build-test && karma start scripts/karma.conf.js", 27 | "posttest": "shx cat test/dist/coverage/text.txt", 28 | "update-changelog": "conventional-changelog -p videojs -i CHANGELOG.md -s", 29 | "preversion": "npm test", 30 | "version": "is-prerelease || npm run update-changelog && git add CHANGELOG.md", 31 | "watch": "npm-run-all -p watch:*", 32 | "watch:css": "npm run build:css -- -w", 33 | "watch:js": "npm run build:js -- -w", 34 | "prepublishOnly": "npm-run-all build-prod && vjsverify --verbose --skip-es-check" 35 | }, 36 | "engines": { 37 | "node": ">=14", 38 | "npm": ">=6" 39 | }, 40 | "keywords": [ 41 | "videojs", 42 | "videojs-plugin" 43 | ], 44 | "author": "mister-ben ", 45 | "license": "MIT", 46 | "vjsstandard": { 47 | "ignore": [ 48 | "es", 49 | "cjs", 50 | "dist", 51 | "docs", 52 | "test/dist" 53 | ] 54 | }, 55 | "files": [ 56 | "CONTRIBUTING.md", 57 | "cjs/", 58 | "dist/", 59 | "docs/", 60 | "es/", 61 | "index.html", 62 | "scripts/", 63 | "src/", 64 | "test/" 65 | ], 66 | "lint-staged": { 67 | "*.js": "vjsstandard --fix", 68 | "README.md": "doctoc --notitle" 69 | }, 70 | "dependencies": { 71 | "global": "^4.4.0" 72 | }, 73 | "peerDependencies": { 74 | "video.js": "^8" 75 | }, 76 | "devDependencies": { 77 | "@babel/runtime": "^7.14.0", 78 | "@videojs/generator-helpers": "~3.2.0", 79 | "husky": "^8.0.1", 80 | "jsdoc": "^4.0.0", 81 | "karma": "^6.3.2", 82 | "postcss": "^8.2.13", 83 | "postcss-cli": "^8.3.1", 84 | "rollup": "^2.46.0", 85 | "sinon": "^16.0.0", 86 | "video.js": "^8.0.0", 87 | "videojs-generate-karma-config": "~8.0.0", 88 | "videojs-generate-postcss-config": "~3.0.0", 89 | "videojs-generate-rollup-config": "^7.0.0", 90 | "videojs-generator-verify": "^4.1.0", 91 | "videojs-languages": "^2.0.0", 92 | "videojs-standard": "^9.0.1" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /scripts/karma.conf.js: -------------------------------------------------------------------------------- 1 | const generate = require('videojs-generate-karma-config'); 2 | 3 | module.exports = function(config) { 4 | 5 | // see https://github.com/videojs/videojs-generate-karma-config 6 | // for options 7 | const options = {}; 8 | 9 | config = generate(config, options); 10 | 11 | // any other custom stuff not supported by options here! 12 | }; 13 | -------------------------------------------------------------------------------- /scripts/postcss.config.js: -------------------------------------------------------------------------------- 1 | const generate = require('videojs-generate-postcss-config'); 2 | 3 | module.exports = function(context) { 4 | const result = generate({}, context); 5 | 6 | // do custom stuff here 7 | 8 | return result; 9 | }; 10 | -------------------------------------------------------------------------------- /scripts/rollup.config.js: -------------------------------------------------------------------------------- 1 | const generate = require('videojs-generate-rollup-config'); 2 | 3 | // see https://github.com/videojs/videojs-generate-rollup-config 4 | // for options 5 | const options = {}; 6 | const config = generate(options); 7 | 8 | // Add additonal builds/customization here! 9 | 10 | // export the builds to rollup 11 | export default Object.values(config.builds); 12 | -------------------------------------------------------------------------------- /src/plugin.css: -------------------------------------------------------------------------------- 1 | /** 2 | * css for videojs-mobile-ui 3 | */ 4 | 5 | @keyframes fadeAndScale { 6 | 0% { 7 | opacity: 0 8 | } 9 | 25% { 10 | opacity: 1; 11 | } 12 | 100% { 13 | opacity: 0; 14 | } 15 | } 16 | 17 | .video-js.vjs-mobile-ui { 18 | 19 | &.vjs-has-started:not(.vjs-ad-playing) .vjs-touch-overlay { 20 | position: absolute; 21 | pointer-events: auto; 22 | top: 0; 23 | } 24 | 25 | .vjs-touch-overlay { 26 | display: block; 27 | width: 100%; 28 | height: 100%; 29 | pointer-events: none; 30 | 31 | &.skip { 32 | opacity: 0; 33 | animation: fadeAndScale 0.8s linear; 34 | background-repeat: no-repeat; 35 | background-position: 80% center; 36 | background-size: 10%; 37 | background-image: url('data:image/svg+xml;utf8,'); 38 | 39 | &:after { 40 | content: attr(data-skip-text); 41 | position: absolute; 42 | top: 60%; 43 | left: 70%; 44 | } 45 | } 46 | 47 | &.skip.reverse { 48 | background-position: 20% center; 49 | background-image: url('data:image/svg+xml;utf8,'); 50 | 51 | &:after { 52 | right: 70%; 53 | left: unset; 54 | } 55 | } 56 | 57 | .vjs-play-control { 58 | top: 50%; 59 | left: 50%; 60 | transform: translate(-50%, -50%); 61 | position: absolute; 62 | width: 30%; 63 | height: 80%; 64 | pointer-events: none; 65 | opacity: 0; 66 | transition: opacity 0.3s ease; 67 | 68 | .vjs-icon-placeholder::before { 69 | content: ''; 70 | background-size: 60%; 71 | background-position: center center; 72 | background-repeat: no-repeat; 73 | background-image: url('data:image/svg+xml;utf8,'); 74 | } 75 | 76 | &.vjs-paused .vjs-icon-placeholder::before { 77 | content: ''; 78 | background-image: url('data:image/svg+xml;utf8,'); 79 | } 80 | 81 | &.vjs-ended .vjs-icon-placeholder::before { 82 | content: ''; 83 | background-image: url('data:image/svg+xml;utf8,'); 84 | } 85 | } 86 | 87 | &.show-play-toggle .vjs-play-control { 88 | opacity: 1; 89 | pointer-events: auto; 90 | } 91 | 92 | } 93 | 94 | &.vjs-mobile-ui-disable-end.vjs-ended .vjs-touch-overlay { 95 | display: none; 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /src/plugin.js: -------------------------------------------------------------------------------- 1 | import videojs from 'video.js'; 2 | import {version as VERSION} from '../package.json'; 3 | import './touchOverlay.js'; 4 | import window from 'global/window'; 5 | 6 | // Default options for the plugin. 7 | const defaults = { 8 | fullscreen: { 9 | enterOnRotate: true, 10 | exitOnRotate: true, 11 | lockOnRotate: true, 12 | lockToLandscapeOnEnter: false, 13 | disabled: false 14 | }, 15 | touchControls: { 16 | seekSeconds: 10, 17 | tapTimeout: 300, 18 | disableOnEnd: false, 19 | disabled: false 20 | } 21 | }; 22 | 23 | const screen = window.screen; 24 | 25 | /** 26 | * Gets 'portrait' or 'lanscape' from the two orientation APIs 27 | * 28 | * @return {string} orientation 29 | */ 30 | const getOrientation = () => { 31 | if (screen) { 32 | // Prefer the string over angle, as 0° can be landscape on some tablets 33 | const orientationString = ((screen.orientation || {}).type || screen.mozOrientation || screen.msOrientation || '').split('-')[0]; 34 | 35 | if (orientationString === 'landscape' || orientationString === 'portrait') { 36 | return orientationString; 37 | } 38 | } 39 | 40 | // iOS only supports window.orientation 41 | if (typeof window.orientation === 'number') { 42 | if (window.orientation === 0 || window.orientation === 180) { 43 | return 'portrait'; 44 | } 45 | return 'landscape'; 46 | } 47 | 48 | return 'portrait'; 49 | }; 50 | 51 | /** 52 | * Add UI and event listeners 53 | * 54 | * @function onPlayerReady 55 | * @param {Player} player 56 | * A Video.js player object. 57 | * 58 | * @param {Object} [options={}] 59 | * A plain object containing options for the plugin. 60 | */ 61 | const onPlayerReady = (player, options) => { 62 | player.addClass('vjs-mobile-ui'); 63 | 64 | if (!options.touchControls.disabled) { 65 | 66 | if (options.touchControls.disableOnEnd || typeof player.endscreen === 'function') { 67 | player.addClass('vjs-mobile-ui-disable-end'); 68 | } 69 | 70 | // Insert before the control bar 71 | const controlBarIdx = player.children_.indexOf(player.getChild('ControlBar')); 72 | 73 | player.touchOverlay = player.addChild('TouchOverlay', options.touchControls, controlBarIdx); 74 | } 75 | 76 | if (options.fullscreen.disabled) { 77 | return; 78 | } 79 | 80 | let locked = false; 81 | 82 | const rotationHandler = () => { 83 | const currentOrientation = getOrientation(); 84 | 85 | if (currentOrientation === 'landscape' && options.fullscreen.enterOnRotate) { 86 | if (player.paused() === false) { 87 | player.requestFullscreen(); 88 | if ((options.fullscreen.lockOnRotate || options.fullscreen.lockToLandscapeOnEnter) && 89 | screen.orientation && screen.orientation.lock) { 90 | screen.orientation.lock('landscape').then(() => { 91 | locked = true; 92 | }).catch((e) => { 93 | videojs.log('Browser refused orientation lock:', e); 94 | }); 95 | } 96 | } 97 | } else if (currentOrientation === 'portrait' && options.fullscreen.exitOnRotate && !locked) { 98 | if (player.isFullscreen()) { 99 | player.exitFullscreen(); 100 | } 101 | } 102 | }; 103 | 104 | if (options.fullscreen.enterOnRotate || options.fullscreen.exitOnRotate) { 105 | if (videojs.browser.IS_IOS) { 106 | window.addEventListener('orientationchange', rotationHandler); 107 | 108 | player.on('dispose', () => { 109 | window.removeEventListener('orientationchange', rotationHandler); 110 | }); 111 | } else if (screen.orientation) { 112 | // addEventListener('orientationchange') is not a user interaction on Android 113 | screen.orientation.onchange = rotationHandler; 114 | 115 | player.on('dispose', () => { 116 | screen.orientation.onchange = null; 117 | }); 118 | } 119 | } 120 | 121 | player.on('fullscreenchange', _ => { 122 | if (player.isFullscreen() && options.fullscreen.lockToLandscapeOnEnter && getOrientation() === 'portrait') { 123 | screen.orientation.lock('landscape').then(()=>{ 124 | locked = true; 125 | }).catch((e) => { 126 | videojs.log('Browser refused orientation lock:', e); 127 | }); 128 | } else if (!player.isFullscreen() && locked) { 129 | screen.orientation.unlock(); 130 | locked = false; 131 | } 132 | }); 133 | 134 | player.on('ended', _ => { 135 | if (locked === true) { 136 | screen.orientation.unlock(); 137 | locked = false; 138 | } 139 | }); 140 | }; 141 | 142 | /** 143 | * A video.js plugin. 144 | * 145 | * Adds a monile UI for player control, and fullscreen orientation control 146 | * 147 | * @function mobileUi 148 | * @param {Object} [options={}] 149 | * Plugin options. 150 | * @param {boolean} [options.forceForTesting=false] 151 | * Enables the display regardless of user agent, for testing purposes 152 | * @param {Object} [options.fullscreen={}] 153 | * Fullscreen options. 154 | * @param {boolean} [options.fullscreen.disabled=false] 155 | * If true no fullscreen handling except the *deprecated* iOS fullwindow hack 156 | * @param {boolean} [options.fullscreen.enterOnRotate=true] 157 | * Whether to go fullscreen when rotating to landscape 158 | * @param {boolean} [options.fullscreen.exitOnRotate=true] 159 | * Whether to leave fullscreen when rotating to portrait (if not locked) 160 | * @param {boolean} [options.fullscreen.lockOnRotate=true] 161 | * Whether to lock orientation when rotating to landscape 162 | * Unlocked when exiting fullscreen or on 'ended 163 | * @param {boolean} [options.fullscreen.lockToLandscapeOnEnter=false] 164 | * Whether to always lock orientation to landscape on fullscreen mode 165 | * Unlocked when exiting fullscreen or on 'ended' 166 | * @param {Object} [options.touchControls={}] 167 | * Touch UI options. 168 | * @param {boolean} [options.touchControls.disabled=false] 169 | * If true no touch controls are added. 170 | * @param {int} [options.touchControls.seekSeconds=10] 171 | * Number of seconds to seek on double-tap 172 | * @param {int} [options.touchControls.tapTimeout=300] 173 | * Interval in ms to be considered a doubletap 174 | * @param {boolean} [options.touchControls.disableOnEnd=false] 175 | * Whether to disable when the video ends (e.g., if there is an endscreen) 176 | * Never shows if the endscreen plugin is present 177 | */ 178 | const mobileUi = function(options = {}) { 179 | if (options.forceForTesting || videojs.browser.IS_ANDROID || videojs.browser.IS_IOS) { 180 | this.ready(() => { 181 | onPlayerReady(this, videojs.obj.merge(defaults, options)); 182 | }); 183 | } 184 | }; 185 | 186 | // Register the plugin with video.js. 187 | videojs.registerPlugin('mobileUi', mobileUi); 188 | 189 | // Include the version number. 190 | mobileUi.VERSION = VERSION; 191 | 192 | export default mobileUi; 193 | -------------------------------------------------------------------------------- /src/touchOverlay.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file touchOverlay.js 3 | * Touch UI component 4 | */ 5 | 6 | import videojs from 'video.js'; 7 | import window from 'global/window'; 8 | 9 | const Component = videojs.getComponent('Component'); 10 | const dom = videojs.dom || videojs; 11 | 12 | /** 13 | * The `TouchOverlay` is an overlay to capture tap events. 14 | * 15 | * @extends Component 16 | */ 17 | class TouchOverlay extends Component { 18 | 19 | /** 20 | * Creates an instance of the this class. 21 | * 22 | * @param {Player} player 23 | * The `Player` that this class should be attached to. 24 | * 25 | * @param {Object} [options] 26 | * The key/value store of player options. 27 | */ 28 | constructor(player, options) { 29 | super(player, options); 30 | 31 | this.seekSeconds = options.seekSeconds; 32 | this.tapTimeout = options.tapTimeout; 33 | this.taps = 0; 34 | 35 | // Add play toggle overlay 36 | this.addChild('playToggle', {}); 37 | 38 | // Clear overlay when playback starts or with control fade 39 | player.on(['playing', 'userinactive'], e => { 40 | this.removeClass('show-play-toggle'); 41 | }); 42 | 43 | // A 0 inactivity timeout won't work here 44 | if (this.player_.options_.inactivityTimeout === 0) { 45 | this.player_.options_.inactivityTimeout = 5000; 46 | } 47 | 48 | /** 49 | * Debounced tap handler. 50 | * Seeks number of (taps - 1) * configured seconds to skip. 51 | * One tap is a non-op 52 | * 53 | * @param {Event} event 54 | */ 55 | this.handleTaps_ = videojs.fn.debounce(event => { 56 | const increment = (this.taps - 1) * this.seekSeconds; 57 | 58 | this.taps = 0; 59 | if (increment < 1) { 60 | return; 61 | } 62 | 63 | const rect = this.el_.getBoundingClientRect(); 64 | const x = event.changedTouches[0].clientX - rect.left; 65 | 66 | // Check if double tap is in left or right area 67 | if (x < rect.width * 0.4) { 68 | this.player_.currentTime(Math.max(0, this.player_.currentTime() - increment)); 69 | this.addClass('reverse'); 70 | } else if (x > rect.width - (rect.width * 0.4)) { 71 | this.player_.currentTime(Math.min(this.player_.duration(), this.player_.currentTime() + increment)); 72 | this.removeClass('reverse'); 73 | } else { 74 | return; 75 | } 76 | 77 | // Remove play toggle if showing 78 | this.removeClass('show-play-toggle'); 79 | 80 | // Remove and readd class to trigger animation 81 | this.setAttribute('data-skip-text', `${increment} ${this.localize('seconds')}`); 82 | this.removeClass('skip'); 83 | window.requestAnimationFrame(() => { 84 | this.addClass('skip'); 85 | }); 86 | }, this.tapTimeout); 87 | 88 | this.enable(); 89 | } 90 | 91 | /** 92 | * Builds the DOM element. 93 | * 94 | * @return {Element} 95 | * The DOM element. 96 | */ 97 | createEl() { 98 | const el = dom.createEl('div', { 99 | className: 'vjs-touch-overlay', 100 | // Touch overlay is not tabbable. 101 | tabIndex: -1 102 | }); 103 | 104 | return el; 105 | } 106 | 107 | /** 108 | * Debounces to either handle a delayed single tap, or a double tap 109 | * 110 | * @param {Event} event 111 | * The touch event 112 | * 113 | */ 114 | handleTap(event) { 115 | // Don't handle taps on the play button 116 | if (event.target !== this.el_) { 117 | return; 118 | } 119 | 120 | event.preventDefault(); 121 | 122 | this.taps += 1; 123 | if (this.taps === 1) { 124 | this.removeClass('skip'); 125 | this.toggleClass('show-play-toggle'); 126 | } 127 | this.handleTaps_(event); 128 | } 129 | 130 | /** 131 | * Enables touch handler 132 | */ 133 | enable() { 134 | this.firstTapCaptured = false; 135 | this.on('touchend', this.handleTap); 136 | } 137 | 138 | /** 139 | * Disables touch handler 140 | */ 141 | disable() { 142 | this.off('touchend', this.handleTap); 143 | } 144 | 145 | } 146 | 147 | Component.registerComponent('TouchOverlay', TouchOverlay); 148 | export default TouchOverlay; 149 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | videojs-mobile-ui 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 | const detectBrowsers = { 3 | enabled: false, 4 | usePhantomJS: false 5 | }; 6 | 7 | // On Travis CI, we can only run in Firefox and Chrome; so, enforce that. 8 | if (process.env.TRAVIS) { 9 | config.browsers = ['Firefox', 'travisChrome']; 10 | } 11 | 12 | // If no browsers are specified, we enable `karma-detect-browsers` 13 | // this will detect all browsers that are available for testing 14 | if (!config.browsers.length) { 15 | detectBrowsers.enabled = true; 16 | } 17 | 18 | config.set({ 19 | basePath: '..', 20 | frameworks: ['qunit', 'detectBrowsers'], 21 | files: [ 22 | 'node_modules/video.js/dist/video-js.css', 23 | 'dist/videojs-mobile-ui.css', 24 | 25 | 'node_modules/sinon/pkg/sinon.js', 26 | 'node_modules/video.js/dist/video.js', 27 | 'test/dist/bundle.js' 28 | ], 29 | customLaunchers: { 30 | travisChrome: { 31 | base: 'Chrome', 32 | flags: ['--no-sandbox'] 33 | } 34 | }, 35 | detectBrowsers, 36 | reporters: ['dots'], 37 | port: 9876, 38 | colors: true, 39 | autoWatch: false, 40 | singleRun: true, 41 | concurrency: Infinity 42 | }); 43 | }; 44 | -------------------------------------------------------------------------------- /test/plugin.test.js: -------------------------------------------------------------------------------- 1 | import document from 'global/document'; 2 | import window from 'global/window'; 3 | 4 | import QUnit from 'qunit'; 5 | import sinon from 'sinon'; 6 | import videojs from 'video.js'; 7 | 8 | import plugin from '../src/plugin'; 9 | 10 | const Player = videojs.getComponent('Player'); 11 | 12 | QUnit.test('the environment is sane', function(assert) { 13 | assert.strictEqual(typeof Array.isArray, 'function', 'es5 exists'); 14 | assert.strictEqual(typeof sinon, 'object', 'sinon exists'); 15 | assert.strictEqual(typeof videojs, 'function', 'videojs exists'); 16 | assert.strictEqual(typeof plugin, 'function', 'plugin is a function'); 17 | }); 18 | 19 | QUnit.module('videojs-mobile-ui', { 20 | 21 | beforeEach() { 22 | 23 | // Mock the environment's timers because certain things - particularly 24 | // player readiness - are asynchronous in video.js 5. This MUST come 25 | // before any player is created; otherwise, timers could get created 26 | // with the actual timer methods! 27 | this.clock = sinon.useFakeTimers(); 28 | 29 | this.fixture = document.getElementById('qunit-fixture'); 30 | this.video = document.createElement('video'); 31 | this.fixture.appendChild(this.video); 32 | this.player = videojs(this.video); 33 | }, 34 | 35 | afterEach() { 36 | if (!this.player.isDisposed()) { 37 | this.player.dispose(); 38 | } 39 | this.clock.restore(); 40 | } 41 | }); 42 | 43 | QUnit.test('registers itself with video.js', function(assert) { 44 | 45 | assert.strictEqual( 46 | typeof Player.prototype.mobileUi, 47 | 'function', 48 | 'videojs-mobile-ui plugin was registered' 49 | ); 50 | }); 51 | 52 | QUnit.test('inserts element before control bar', function(assert) { 53 | 54 | this.player.mobileUi({forceForTesting: true}); 55 | 56 | this.clock.tick(1); 57 | 58 | assert.strictEqual( 59 | this.player.getChild('TouchOverlay').el_.nextSibling, 60 | this.player.getChild('ControlBar').el_, 61 | 'TouchOverlay is before ControlBar' 62 | ); 63 | }); 64 | 65 | QUnit.test('does not insert if disabled', function(assert) { 66 | 67 | this.player.mobileUi({ 68 | forceForTesting: true, 69 | touchControls: { 70 | disabled: true 71 | } 72 | }); 73 | 74 | this.clock.tick(1); 75 | 76 | assert.strictEqual( 77 | this.player.touchOverlay, 78 | undefined, 79 | 'TouchOverlay should not be present' 80 | ); 81 | }); 82 | 83 | QUnit.test('iOS event listeners', function(assert) { 84 | 85 | const oldBrowser = videojs.browser; 86 | 87 | videojs.browser = videojs.mergeOptions(videojs.browser, { 88 | IS_IOS: true, 89 | IS_ANDROID: false 90 | }); 91 | 92 | const addSpy = sinon.spy(window, 'addEventListener'); 93 | const removeSpy = sinon.spy(window, 'removeEventListener'); 94 | 95 | this.player.mobileUi({ forceForTesting: true }); 96 | 97 | this.clock.tick(1); 98 | 99 | assert.strictEqual( 100 | 'orientationchange', 101 | addSpy.getCall(0).args[0], 102 | 'orientationchange listener added' 103 | ); 104 | 105 | this.player.dispose(); 106 | 107 | this.clock.tick(1); 108 | 109 | assert.strictEqual( 110 | 'orientationchange', 111 | removeSpy.getCall(0).args[0], 112 | 'orientationchange listener removed when player disposed' 113 | ); 114 | 115 | addSpy.restore(); 116 | removeSpy.restore(); 117 | 118 | videojs.browser = oldBrowser; 119 | }); 120 | 121 | const testOrSkip = (window.screen && window.screen.orientation) ? 'test' : 'skip'; 122 | 123 | QUnit[testOrSkip]('Android event listeners', function(assert) { 124 | 125 | const oldBrowser = videojs.browser; 126 | 127 | videojs.browser = videojs.mergeOptions(videojs.browser, { 128 | IS_IOS: false, 129 | IS_ANDROID: true 130 | }); 131 | 132 | this.player.mobileUi({forceForTesting: true}); 133 | 134 | this.clock.tick(1); 135 | 136 | assert.strictEqual( 137 | typeof window.screen.orientation.onchange, 138 | 'function', 139 | 'screen.orientation.onchange is used for android' 140 | ); 141 | 142 | this.player.dispose(); 143 | 144 | this.clock.tick(1); 145 | 146 | assert.strictEqual( 147 | window.screen.orientation.onchange, 148 | null, 149 | 'screen.orientation.onchange is removed after dispose' 150 | ); 151 | 152 | videojs.browser = oldBrowser; 153 | }); 154 | 155 | QUnit[testOrSkip]('Android event listeners skipped if disabled', function(assert) { 156 | 157 | const oldBrowser = videojs.browser; 158 | 159 | videojs.browser = videojs.mergeOptions(videojs.browser, { 160 | IS_IOS: false, 161 | IS_ANDROID: true 162 | }); 163 | 164 | this.player.mobileUi({ 165 | forceForTesting: true, 166 | fullscreen: { 167 | disabled: true 168 | } 169 | }); 170 | 171 | this.clock.tick(1); 172 | 173 | assert.notStrictEqual( 174 | typeof window.screen.orientation.onchange, 175 | 'function', 176 | 'screen.orientation.onchange skipped for android' 177 | ); 178 | 179 | videojs.browser = oldBrowser; 180 | }); 181 | --------------------------------------------------------------------------------