├── .node-version
├── .gitignore
├── assets
└── logo.png
├── circle.yml
├── nodemon.json
├── babel.config.js
├── .eslintrc
├── test
├── index.html
└── src
│ └── index.jsx
├── .editorconfig
├── LICENSE
├── package.json
├── css
├── modal-video.min.css
└── modal-video.css
├── scss
└── modal-video.scss
├── readme.md
├── src
└── index.jsx
└── lib
└── index.js
/.node-version:
--------------------------------------------------------------------------------
1 | 18.12.1
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | yarn.lock
3 | .DS_Store
4 | .idea
5 | test/dist
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/appleple/react-modal-video/HEAD/assets/logo.png
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | machine:
2 | node:
3 | version: 6.2.0
4 | dependencies:
5 | override:
6 | - "npm install"
7 | test:
8 | override:
9 | - "npm run test"
--------------------------------------------------------------------------------
/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "execMap": {
3 | "js": "node",
4 | "jsx": "jsx {{filename}} | node"
5 | },
6 | "ext": "jsx scss",
7 | "ignore": [
8 | "test/dist",
9 | "node_modules",
10 | "lib"
11 | ],
12 | "verbose": true
13 | }
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | ['@babel/preset-env',
4 | {
5 | targets: {
6 | ie: 11,
7 | },
8 | useBuiltIns: 'usage',
9 | corejs: 3,
10 | },
11 | ],
12 | ['@babel/preset-react'],
13 | ]
14 | };
15 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true
4 | },
5 | "globals": {
6 | "document": true,
7 | "window": true
8 | },
9 | "rules":{
10 | "comma-dangle":0
11 | },
12 | "parserOptions": {
13 | "sourceType": "module",
14 | "ecmaVersion": 2015,
15 | "ecmaFeatures": {
16 | "jsx": true
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | modal-video.js
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*.js]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.html]
13 | # すべてのファイルに適用する
14 | charset = utf-8
15 | # 文字コードを統一
16 | indent_style = tab
17 | #インデントを統一する。「tab」か「 space」
18 | indent_size = 2
19 | # インデントの数を統一
20 | trim_trailing_whitespace = true
21 | # 行末のホワイトスペースを削除
22 | insert_final_newline = true
23 | # フォルダの最後の行に改行
24 | end_of_line = lf
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 appleple
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/test/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import ModalVideo from '../../lib/index';
4 |
5 | class App extends React.Component {
6 | constructor() {
7 | super();
8 | this.state = {
9 | isOpen: false,
10 | isOpenYouku: false,
11 | isOpenCustom: false,
12 | customUrl: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
13 | };
14 | this.openModal = this.openModal.bind(this);
15 | }
16 |
17 | openModal() {
18 | this.setState({ isOpen: true });
19 | }
20 |
21 | render() {
22 | return (
23 |
24 | this.setState({ isOpen: false })}
30 | />
31 |
32 |
33 | this.setState({ isOpenVimeo: false })}
38 | />
39 |
40 |
41 | this.setState({ isOpenYouku: false })}
46 | />
47 |
48 |
49 | this.setState({ isOpenCustom: false })}
54 | />
55 |
56 |
57 | );
58 | }
59 | }
60 |
61 | ReactDOM.render(, document.getElementById('root'));
62 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-modal-video",
3 | "version": "2.0.2",
4 | "main": "lib/index.js",
5 | "description": "Modal Video Viewer",
6 | "author": "appleple",
7 | "license": "MIT",
8 | "scripts": {
9 | "test": "eslint ./src/index.jsx --fix",
10 | "build:js": "npm-run-all -p build:lib build:test",
11 | "build:lib": "npm run babel",
12 | "build:test": "browserify ./test/src/index.jsx -t babelify -o ./test/dist/index.js",
13 | "build:sass": "npm-run-all -p sass sass:min",
14 | "babel": "babel src --out-dir lib",
15 | "sass": "sass ./scss/modal-video.scss ./css/modal-video.css --style expanded --no-source-map",
16 | "sass:min": "sass ./scss/modal-video.scss ./css/modal-video.min.css --style compressed --no-source-map",
17 | "watch:js": "onchange \"src/\" -- npm run build:js",
18 | "watch:sass": "onchange \"scss\" -- npm run build:sass",
19 | "watch:test": "onchange \"test/src\" -- npm run build:test",
20 | "sync": "browser-sync start --server './' --files './test/dist/*.js' './css/*.css' --startPath '/test/index.html'",
21 | "start": "npm-run-all -p watch:js watch:sass watch:test sync",
22 | "deploy": "np --no-cleanup"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/appleple/react-modal-video.git"
27 | },
28 | "devDependencies": {
29 | "@babel/cli": "^7.17.10",
30 | "@babel/core": "^7.18.5",
31 | "@babel/preset-env": "^7.20.2",
32 | "@babel/preset-react": "^7.18.6",
33 | "babelify": "^10.0.0",
34 | "browser-sync": "^2.27.10",
35 | "browserify": "^17.0.0",
36 | "eslint": "^8.17.0",
37 | "eslint-config-airbnb": "^19.0.4",
38 | "eslint-config-airbnb-base": "^15.0.0",
39 | "eslint-plugin-import": "^2.26.0",
40 | "npm-run-all": "^4.1.5",
41 | "sass": "^1.52.3",
42 | "onchange": "^7.1.0",
43 | "np": "^7.6.1"
44 | },
45 | "dependencies": {
46 | "core-js": "^3.27.2",
47 | "react-transition-group": "^4.4.2"
48 | },
49 | "peerDependencies": {
50 | "react": "^17.0.0 || ^18.2.0",
51 | "react-dom": "^17.0.0 || ^18.2.0"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/css/modal-video.min.css:
--------------------------------------------------------------------------------
1 | @keyframes modal-video{from{opacity:0}to{opacity:1}}@keyframes modal-video-inner{from{transform:translate(0, 100px)}to{transform:translate(0, 0)}}.modal-video{position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,.5);z-index:1000000;cursor:pointer;opacity:1;animation-timing-function:ease-out;animation-duration:.3s;animation-name:modal-video;-webkit-transition:opacity .3s ease-out;-moz-transition:opacity .3s ease-out;-ms-transition:opacity .3s ease-out;-o-transition:opacity .3s ease-out;transition:opacity .3s ease-out}.modal-video-effect-exit{opacity:0}.modal-video-effect-exit .modal-video-movie-wrap{-webkit-transform:translate(0, 100px);-moz-transform:translate(0, 100px);-ms-transform:translate(0, 100px);-o-transform:translate(0, 100px);transform:translate(0, 100px)}.modal-video-body{max-width:960px;width:100%;height:100%;margin:0 auto;padding:0 10px;display:flex;justify-content:center;box-sizing:border-box}.modal-video-inner{display:flex;justify-content:center;align-items:center;width:100%;height:100%}@media(orientation: landscape){.modal-video-inner{padding:10px 60px;box-sizing:border-box}}.modal-video-movie-wrap{width:100%;height:0;position:relative;padding-bottom:56.25%;background-color:#333;animation-timing-function:ease-out;animation-duration:.3s;animation-name:modal-video-inner;-webkit-transform:translate(0, 0);-moz-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-ms-transition:-ms-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal-video-movie-wrap iframe{position:absolute;top:0;left:0;width:100%;height:100%}.modal-video-close-btn{position:absolute;z-index:2;top:-45px;right:0px;display:inline-block;width:35px;height:35px;overflow:hidden;border:none;background:rgba(0,0,0,0)}@media(orientation: landscape){.modal-video-close-btn{top:0;right:-45px}}.modal-video-close-btn:before{transform:rotate(45deg)}.modal-video-close-btn:after{transform:rotate(-45deg)}.modal-video-close-btn:before,.modal-video-close-btn:after{content:"";position:absolute;height:2px;width:100%;top:50%;left:0;margin-top:-1px;background:#fff;border-radius:5px;margin-top:-6px}
2 |
--------------------------------------------------------------------------------
/css/modal-video.css:
--------------------------------------------------------------------------------
1 | @keyframes modal-video {
2 | from {
3 | opacity: 0;
4 | }
5 | to {
6 | opacity: 1;
7 | }
8 | }
9 | @keyframes modal-video-inner {
10 | from {
11 | transform: translate(0, 100px);
12 | }
13 | to {
14 | transform: translate(0, 0);
15 | }
16 | }
17 | .modal-video {
18 | position: fixed;
19 | top: 0;
20 | left: 0;
21 | width: 100%;
22 | height: 100%;
23 | background-color: rgba(0, 0, 0, 0.5);
24 | z-index: 1000000;
25 | cursor: pointer;
26 | opacity: 1;
27 | animation-timing-function: ease-out;
28 | animation-duration: 0.3s;
29 | animation-name: modal-video;
30 | -webkit-transition: opacity 0.3s ease-out;
31 | -moz-transition: opacity 0.3s ease-out;
32 | -ms-transition: opacity 0.3s ease-out;
33 | -o-transition: opacity 0.3s ease-out;
34 | transition: opacity 0.3s ease-out;
35 | }
36 |
37 | .modal-video-effect-exit {
38 | opacity: 0;
39 | }
40 | .modal-video-effect-exit .modal-video-movie-wrap {
41 | -webkit-transform: translate(0, 100px);
42 | -moz-transform: translate(0, 100px);
43 | -ms-transform: translate(0, 100px);
44 | -o-transform: translate(0, 100px);
45 | transform: translate(0, 100px);
46 | }
47 |
48 | .modal-video-body {
49 | max-width: 960px;
50 | width: 100%;
51 | height: 100%;
52 | margin: 0 auto;
53 | padding: 0 10px;
54 | display: flex;
55 | justify-content: center;
56 | box-sizing: border-box;
57 | }
58 |
59 | .modal-video-inner {
60 | display: flex;
61 | justify-content: center;
62 | align-items: center;
63 | width: 100%;
64 | height: 100%;
65 | }
66 | @media (orientation: landscape) {
67 | .modal-video-inner {
68 | padding: 10px 60px;
69 | box-sizing: border-box;
70 | }
71 | }
72 |
73 | .modal-video-movie-wrap {
74 | width: 100%;
75 | height: 0;
76 | position: relative;
77 | padding-bottom: 56.25%;
78 | background-color: #333;
79 | animation-timing-function: ease-out;
80 | animation-duration: 0.3s;
81 | animation-name: modal-video-inner;
82 | -webkit-transform: translate(0, 0);
83 | -moz-transform: translate(0, 0);
84 | -ms-transform: translate(0, 0);
85 | -o-transform: translate(0, 0);
86 | transform: translate(0, 0);
87 | -webkit-transition: -webkit-transform 0.3s ease-out;
88 | -moz-transition: -moz-transform 0.3s ease-out;
89 | -ms-transition: -ms-transform 0.3s ease-out;
90 | -o-transition: -o-transform 0.3s ease-out;
91 | transition: transform 0.3s ease-out;
92 | }
93 | .modal-video-movie-wrap iframe {
94 | position: absolute;
95 | top: 0;
96 | left: 0;
97 | width: 100%;
98 | height: 100%;
99 | }
100 |
101 | .modal-video-close-btn {
102 | position: absolute;
103 | z-index: 2;
104 | top: -45px;
105 | right: 0px;
106 | display: inline-block;
107 | width: 35px;
108 | height: 35px;
109 | overflow: hidden;
110 | border: none;
111 | background: transparent;
112 | }
113 | @media (orientation: landscape) {
114 | .modal-video-close-btn {
115 | top: 0;
116 | right: -45px;
117 | }
118 | }
119 | .modal-video-close-btn:before {
120 | transform: rotate(45deg);
121 | }
122 | .modal-video-close-btn:after {
123 | transform: rotate(-45deg);
124 | }
125 | .modal-video-close-btn:before, .modal-video-close-btn:after {
126 | content: "";
127 | position: absolute;
128 | height: 2px;
129 | width: 100%;
130 | top: 50%;
131 | left: 0;
132 | margin-top: -1px;
133 | background: #fff;
134 | border-radius: 5px;
135 | margin-top: -6px;
136 | }
137 |
--------------------------------------------------------------------------------
/scss/modal-video.scss:
--------------------------------------------------------------------------------
1 | $animation-speed: .3s;
2 | $animation-function: ease-out;
3 | $backdrop-color: rgba(0, 0, 0, .5);
4 |
5 | @keyframes modal-video {
6 | from {
7 | opacity: 0;
8 | }
9 |
10 | to {
11 | opacity: 1;
12 | }
13 | }
14 |
15 | @keyframes modal-video-inner {
16 | from {
17 | transform: translate(0, 100px);
18 | }
19 |
20 | to {
21 | transform: translate(0, 0);
22 | }
23 | }
24 |
25 | .modal-video {
26 | position: fixed;
27 | top: 0;
28 | left: 0;
29 | width: 100%;
30 | height: 100%;
31 | background-color: $backdrop-color;
32 | z-index: 1000000;
33 | cursor: pointer;
34 | opacity: 1;
35 | animation-timing-function: $animation-function;
36 | animation-duration: $animation-speed;
37 | animation-name: modal-video;
38 | -webkit-transition: opacity $animation-speed $animation-function;
39 | -moz-transition: opacity $animation-speed $animation-function;
40 | -ms-transition: opacity $animation-speed $animation-function;
41 | -o-transition: opacity $animation-speed $animation-function;
42 | transition: opacity $animation-speed $animation-function;
43 | }
44 |
45 | .modal-video-effect-exit {
46 | opacity: 0;
47 |
48 | & .modal-video-movie-wrap {
49 | -webkit-transform: translate(0, 100px);
50 | -moz-transform: translate(0, 100px);
51 | -ms-transform: translate(0, 100px);
52 | -o-transform: translate(0, 100px);
53 | transform: translate(0, 100px);
54 | }
55 | }
56 |
57 | .modal-video-body {
58 | max-width: 960px;
59 | width: 100%;
60 | height: 100%;
61 | margin: 0 auto;
62 | padding: 0 10px;
63 | display: flex;
64 | justify-content: center;
65 | box-sizing: border-box;
66 | }
67 |
68 | .modal-video-inner {
69 | display: flex;
70 | justify-content: center;
71 | align-items: center;
72 | width: 100%;
73 | height: 100%;
74 |
75 | @media (orientation: landscape) {
76 | padding: 10px 60px;
77 | box-sizing: border-box;
78 | }
79 | }
80 |
81 | .modal-video-movie-wrap {
82 | width: 100%;
83 | height: 0;
84 | position: relative;
85 | padding-bottom: 56.25%;
86 | background-color: #333;
87 | animation-timing-function: $animation-function;
88 | animation-duration: $animation-speed;
89 | animation-name: modal-video-inner;
90 | -webkit-transform: translate(0, 0);
91 | -moz-transform: translate(0, 0);
92 | -ms-transform: translate(0, 0);
93 | -o-transform: translate(0, 0);
94 | transform: translate(0, 0);
95 | -webkit-transition: -webkit-transform $animation-speed $animation-function;
96 | -moz-transition: -moz-transform $animation-speed $animation-function;
97 | -ms-transition: -ms-transform $animation-speed $animation-function;
98 | -o-transition: -o-transform $animation-speed $animation-function;
99 | transition: transform $animation-speed $animation-function;
100 |
101 | & iframe {
102 | position: absolute;
103 | top: 0;
104 | left: 0;
105 | width: 100%;
106 | height: 100%;
107 | }
108 | }
109 |
110 | .modal-video-close-btn {
111 | position: absolute;
112 | z-index: 2;
113 | top: -45px;
114 | right: 0px;
115 | display: inline-block;
116 | width: 35px;
117 | height: 35px;
118 | overflow: hidden;
119 | border: none;
120 | background: transparent;
121 |
122 | @media (orientation: landscape) {
123 | top: 0;
124 | right: -45px;
125 | }
126 |
127 | &:before {
128 | transform: rotate(45deg);
129 | }
130 |
131 | &:after {
132 | transform: rotate(-45deg);
133 | }
134 |
135 | &:before,
136 | &:after {
137 | content: '';
138 | position: absolute;
139 | height: 2px;
140 | width: 100%;
141 | top: 50%;
142 | left: 0;
143 | margin-top: -1px;
144 | background: #fff;
145 | border-radius: 5px;
146 | margin-top: -6px;
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # react-modal-video
2 |
3 | React Modal Video Component
4 |
5 | ## Features
6 |
7 | - Not affected by dom structure.
8 | - Beautiful transition
9 | - Accessible for keyboard navigation and screen readers.
10 | - Rich options for youtube API and Vimeo API
11 |
12 | ## Demo
13 |
14 | [https://unpkg.com/react-modal-video@latest/test/index.html](https://unpkg.com/react-modal-video@latest/test/index.html)
15 |
16 | ## Install
17 |
18 | ### npm
19 |
20 | ```sh
21 | npm install react-modal-video
22 | ```
23 |
24 | ## Usage
25 |
26 | import sass file to your project
27 |
28 | ```scss
29 | @import 'node_modules/react-modal-video/scss/modal-video.scss';
30 | ```
31 |
32 | ### Functional Implementation with Hooks
33 |
34 | ```jsx
35 | import React, { useState } from 'react';
36 | import ReactDOM from 'react-dom';
37 | import ModalVideo from 'react-modal-video';
38 |
39 | const App = () => {
40 | const [isOpen, setOpen] = useState(false);
41 |
42 | return (
43 |
44 | setOpen(false)}
50 | />
51 |
54 |
55 | );
56 | };
57 |
58 | ReactDOM.render(, document.getElementById('root'));
59 | ```
60 |
61 | ### Class Implementation
62 |
63 | change "isOpen" property to open and close the modal-video
64 |
65 | ```jsx
66 | import React from 'react';
67 | import ReactDOM from 'react-dom';
68 | import ModalVideo from 'react-modal-video';
69 |
70 | class App extends React.Component {
71 | constructor() {
72 | super();
73 | this.state = {
74 | isOpen: false,
75 | };
76 | this.openModal = this.openModal.bind(this);
77 | }
78 |
79 | openModal() {
80 | this.setState({ isOpen: true });
81 | }
82 |
83 | render() {
84 | return (
85 |
86 | this.setState({ isOpen: false })}
91 | />
92 |
93 |
94 | );
95 | }
96 | }
97 |
98 | ReactDOM.render(, document.getElementById('root'));
99 | ```
100 |
101 | ## Options
102 |
103 | - About YouTube options, please refer to https://developers.google.com/youtube/player_parameters?hl=en
104 | - About Vimeo options, please refer to https://developer.vimeo.com/apis/oembed
105 |
106 |
107 |
108 | | properties |
109 | default |
110 |
111 |
112 | | channel |
113 | 'youtube' |
114 |
115 |
116 | | youtube |
117 | autoplay |
118 | 1 |
119 |
120 |
121 | | cc_load_policy |
122 | 1 |
123 |
124 |
125 | | color |
126 | null |
127 |
128 |
129 | | controls |
130 | 1 |
131 |
132 |
133 | | disablekb |
134 | 0 |
135 |
136 |
137 | | enablejsapi |
138 | 0 |
139 |
140 |
141 | | end |
142 | null |
143 |
144 |
145 | | fs |
146 | 1 |
147 |
148 |
149 | | h1 |
150 | null |
151 |
152 |
153 | | iv_load_policy |
154 | 1 |
155 |
156 |
157 | | list |
158 | null |
159 |
160 |
161 | | listType |
162 | null |
163 |
164 |
165 | | loop |
166 | 0 |
167 |
168 |
169 | | modestbranding |
170 | null |
171 |
172 |
173 | | origin |
174 | null |
175 |
176 |
177 | | playlist |
178 | null |
179 |
180 |
181 | | playsinline |
182 | null |
183 |
184 |
185 | | rel |
186 | 0 |
187 |
188 |
189 | | showinfo |
190 | 1 |
191 |
192 |
193 | | start |
194 | 0 |
195 |
196 |
197 | | wmode |
198 | 'transparent' |
199 |
200 |
201 | | theme |
202 | 'dark' |
203 |
204 |
205 | | mute |
206 | 0 |
207 |
208 |
209 | | vimeo |
210 | api |
211 | false |
212 |
213 |
214 | | autopause |
215 | true |
216 |
217 |
218 | | autoplay |
219 | true |
220 |
221 |
222 | | byline |
223 | true |
224 |
225 |
226 | | callback |
227 | null |
228 |
229 |
230 | | color |
231 | null |
232 |
233 |
234 | | height |
235 | null |
236 |
237 |
238 | | loop |
239 | false |
240 |
241 |
242 | | maxheight |
243 | null |
244 |
245 |
246 | | maxwidth |
247 | null |
248 |
249 |
250 | | player_id |
251 | null |
252 |
253 |
254 | | portrait |
255 | true |
256 |
257 |
258 | | title |
259 | true |
260 |
261 |
262 | | width |
263 | null |
264 |
265 |
266 | | xhtml |
267 | false |
268 |
269 |
270 | | youku |
271 | autoplay |
272 | 1 |
273 |
274 |
275 | | show_related |
276 | 0 |
277 |
278 |
279 | | custom |
280 | url |
281 | MP4 URL / iframe URL |
282 |
283 |
284 | | ratio |
285 | '16:9' |
286 |
287 |
288 | | allowFullScreen |
289 | true |
290 |
291 |
292 | | animationSpeed |
293 | 300 |
294 |
295 |
296 | | classNames |
297 | modalVideo |
298 | 'modal-video' |
299 |
300 |
301 | | modalVideoClose |
302 | 'modal-video-close' |
303 |
304 |
305 | | modalVideoBody |
306 | 'modal-video-body' |
307 |
308 |
309 | | modalVideoInner |
310 | 'modal-video-inner' |
311 |
312 |
313 | | modalVideoIframeWrap |
314 | 'modal-video-movie-wrap' |
315 |
316 |
317 | | modalVideoCloseBtn |
318 | 'modal-video-close-btn' |
319 |
320 |
321 | | aria |
322 | openMessage |
323 | 'You just opened the modal video' |
324 |
325 |
326 | | dismissBtnMessage |
327 | 'Close the modal by clicking here' |
328 |
329 |
330 |
331 | ## FAQ
332 |
333 | ### How to track YouTube videos playing in modal-video by GA4?
334 |
335 | 1. Enable JS API. Turn `enablejsapi` property to `1`.
336 | 2. Load YouTube Iframe API. Add `` in HTML file.
337 |
338 | ## Licence
339 |
340 | [MIT](https://github.com/appleple/modal-video.js/blob/master/LICENSE)
341 |
--------------------------------------------------------------------------------
/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CSSTransition from 'react-transition-group/CSSTransition';
3 |
4 | export default class ModalVideo extends React.Component {
5 | constructor(props) {
6 | super(props);
7 | this.state = {
8 | isOpen: false,
9 | modalVideoWidth: '100%'
10 | };
11 | this.closeModal = this.closeModal.bind(this);
12 | this.updateFocus = this.updateFocus.bind(this);
13 |
14 | this.timeout; // used for resizing video.
15 | }
16 |
17 | openModal() {
18 | this.setState({ isOpen: true });
19 | }
20 |
21 | closeModal() {
22 | this.setState({ isOpen: false });
23 | if (typeof this.props.onClose === 'function') {
24 | this.props.onClose();
25 | }
26 | }
27 |
28 | keydownHandler(e) {
29 | if (e.keyCode === 27) {
30 | this.closeModal();
31 | }
32 | }
33 |
34 | componentDidMount() {
35 | document.addEventListener('keydown', this.keydownHandler.bind(this));
36 | window.addEventListener('resize', this.resizeModalVideoWhenHeightGreaterThanWindowHeight.bind(this));
37 | this.setState({
38 | modalVideoWidth: this.getWidthFulfillAspectRatio(this.props.ratio, window.innerHeight, window.innerWidth)
39 | });
40 | }
41 |
42 | componentWillUnmount() {
43 | document.removeEventListener('keydown', this.keydownHandler.bind(this));
44 | window.removeEventListener('resize', this.resizeModalVideoWhenHeightGreaterThanWindowHeight.bind(this));
45 | }
46 |
47 | static getDerivedStateFromProps(props) {
48 | return { isOpen: props.isOpen };
49 | }
50 |
51 | componentDidUpdate() {
52 | if (this.state.isOpen && this.modal) {
53 | this.modal.focus();
54 | }
55 | }
56 |
57 | updateFocus(e) {
58 | if (this.state.isOpen) {
59 | e.preventDefault();
60 | e.stopPropagation();
61 |
62 | if (e.keyCode === 9) {
63 | if (this.modal === document.activeElement) {
64 | this.modaliflame.focus();
65 | }
66 | else if (this.modalbtn === document.activeElement) {
67 | this.modal.focus();
68 | }
69 | }
70 | }
71 | }
72 |
73 | /**
74 | * Resize modal-video-iframe-wrap when window size changed when the height of the video is greater than the height of the window.
75 | */
76 | resizeModalVideoWhenHeightGreaterThanWindowHeight() {
77 | clearTimeout(this.timeout);
78 |
79 | this.timeout = setTimeout(() => {
80 | const width = this.getWidthFulfillAspectRatio(this.props.ratio, window.innerHeight, window.innerWidth);
81 | if (this.state.modalVideoWidth != width) {
82 | this.setState({
83 | modalVideoWidth: width
84 | });
85 | }
86 | }, 10);
87 | }
88 |
89 | getQueryString(obj) {
90 | let url = '';
91 | for (const key in obj) {
92 | if (obj.hasOwnProperty(key)) {
93 | if (obj[key] !== null) {
94 | url += `${key}=${obj[key]}&`;
95 | }
96 | }
97 | }
98 | return url.substr(0, url.length - 1);
99 | }
100 |
101 | getYoutubeUrl(youtube, videoId) {
102 | const query = this.getQueryString(youtube);
103 | return `//www.youtube.com/embed/${videoId}?${query}`;
104 | }
105 |
106 | getVimeoUrl(vimeo, videoId) {
107 | const query = this.getQueryString(vimeo);
108 | return `//player.vimeo.com/video/${videoId}?${query}`;
109 | }
110 |
111 | getYoukuUrl(youku, videoId) {
112 | const query = this.getQueryString(youku);
113 | return `//player.youku.com/embed/${videoId}?${query}`;
114 | }
115 |
116 | getVideoUrl(opt, videoId) {
117 | if (opt.channel === 'youtube') {
118 | return this.getYoutubeUrl(opt.youtube, videoId);
119 | } if (opt.channel === 'vimeo') {
120 | return this.getVimeoUrl(opt.vimeo, videoId);
121 | } if (opt.channel === 'youku') {
122 | return this.getYoukuUrl(opt.youku, videoId);
123 | } if (opt.channel === 'custom') {
124 | return opt.url;
125 | }
126 | }
127 |
128 | getPadding(ratio) {
129 | const arr = ratio.split(':');
130 | const width = Number(arr[0]);
131 | const height = Number(arr[1]);
132 | const padding = height * 100 / width;
133 | return `${padding}%`;
134 | }
135 |
136 | /**
137 | * Calculate the width of the video fulfill aspect ratio.
138 | * When the height of the video is greater than the height of the window,
139 | * this function return the width that fulfill the aspect ratio for the height of the window.
140 | * In other cases, this function return '100%'(the height relative to the width is determined by css).
141 | *
142 | * @param string ratio
143 | * @param number maxWidth
144 | * @returns number | '100%'
145 | */
146 | getWidthFulfillAspectRatio(ratio, maxHeight, maxWidth) {
147 | const arr = ratio.split(':');
148 | const width = Number(arr[0]);
149 | const height = Number(arr[1]);
150 |
151 | // Height that fulfill the aspect ratio for maxWidth.
152 | const videoHeight = maxWidth * (height / width);
153 |
154 | if (maxHeight < videoHeight) {
155 | // when the height of the video is greater than the height of the window.
156 | // calculate the width that fulfill the aspect ratio for the height of the window.
157 |
158 | // ex: 16:9 aspect ratio
159 | // 16:9 = width : height
160 | // → width = 16 / 9 * height
161 | return Math.floor(width / height * maxHeight);
162 | }
163 |
164 | return '100%';
165 | }
166 |
167 | render() {
168 | const modalVideoInnerStyle = {
169 | width: this.state.modalVideoWidth
170 | };
171 |
172 | const modalVideoIframeWrapStyle = {
173 | paddingBottom: this.getPadding(this.props.ratio)
174 | };
175 |
176 | return (
177 |
181 | {() => {
182 | if (!this.state.isOpen) {
183 | return null;
184 | }
185 |
186 | return (
187 | {this.modal = node; }} onKeyDown={this.updateFocus}>
189 |
190 |
191 |
192 | {
193 | this.props.children
194 | ||
211 |
212 |
213 |
);
214 | }}
215 |
216 | );
217 | }
218 | }
219 |
220 | ModalVideo.defaultProps = {
221 | channel: 'youtube',
222 | isOpen: false,
223 | youtube: {
224 | autoplay: 1,
225 | cc_load_policy: 1,
226 | color: null,
227 | controls: 1,
228 | disablekb: 0,
229 | enablejsapi: 0,
230 | end: null,
231 | fs: 1,
232 | h1: null,
233 | iv_load_policy: 1,
234 | list: null,
235 | listType: null,
236 | loop: 0,
237 | modestbranding: null,
238 | origin: null,
239 | playlist: null,
240 | playsinline: null,
241 | rel: 0,
242 | showinfo: 1,
243 | start: 0,
244 | wmode: 'transparent',
245 | theme: 'dark',
246 | mute: 0
247 | },
248 | ratio: '16:9',
249 | vimeo: {
250 | api: false,
251 | autopause: true,
252 | autoplay: true,
253 | byline: true,
254 | callback: null,
255 | color: null,
256 | height: null,
257 | loop: false,
258 | maxheight: null,
259 | maxwidth: null,
260 | player_id: null,
261 | portrait: true,
262 | title: true,
263 | width: null,
264 | xhtml: false
265 | },
266 | youku: {
267 | autoplay: 1,
268 | show_related: 0
269 | },
270 | allowFullScreen: true,
271 | animationSpeed: 300,
272 | classNames: {
273 | modalVideoEffect: 'modal-video-effect',
274 | modalVideo: 'modal-video',
275 | modalVideoClose: 'modal-video-close',
276 | modalVideoBody: 'modal-video-body',
277 | modalVideoInner: 'modal-video-inner',
278 | modalVideoIframeWrap: 'modal-video-movie-wrap',
279 | modalVideoCloseBtn: 'modal-video-close-btn'
280 | },
281 | aria: {
282 | openMessage: 'You just opened the modal video',
283 | dismissBtnMessage: 'Close the modal by clicking here'
284 | }
285 | };
286 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
4 | require("core-js/modules/es.object.to-string.js");
5 | require("core-js/modules/es.reflect.construct.js");
6 | require("core-js/modules/es.symbol.to-primitive.js");
7 | require("core-js/modules/es.date.to-primitive.js");
8 | require("core-js/modules/es.symbol.js");
9 | require("core-js/modules/es.symbol.description.js");
10 | require("core-js/modules/es.symbol.iterator.js");
11 | require("core-js/modules/es.array.iterator.js");
12 | require("core-js/modules/es.string.iterator.js");
13 | require("core-js/modules/web.dom-collections.iterator.js");
14 | Object.defineProperty(exports, "__esModule", {
15 | value: true
16 | });
17 | exports.default = void 0;
18 | require("core-js/modules/es.array.concat.js");
19 | require("core-js/modules/es.number.constructor.js");
20 | require("core-js/modules/es.object.get-prototype-of.js");
21 | var _react = _interopRequireDefault(require("react"));
22 | var _CSSTransition = _interopRequireDefault(require("react-transition-group/CSSTransition"));
23 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
24 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
25 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
26 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
27 | function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
28 | function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
29 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
30 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
31 | function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
32 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
33 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
34 | function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
35 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
36 | var ModalVideo = /*#__PURE__*/function (_React$Component) {
37 | _inherits(ModalVideo, _React$Component);
38 | var _super = _createSuper(ModalVideo);
39 | function ModalVideo(props) {
40 | var _this;
41 | _classCallCheck(this, ModalVideo);
42 | _this = _super.call(this, props);
43 | _this.state = {
44 | isOpen: false,
45 | modalVideoWidth: '100%'
46 | };
47 | _this.closeModal = _this.closeModal.bind(_assertThisInitialized(_this));
48 | _this.updateFocus = _this.updateFocus.bind(_assertThisInitialized(_this));
49 | _this.timeout; // used for resizing video.
50 | return _this;
51 | }
52 | _createClass(ModalVideo, [{
53 | key: "openModal",
54 | value: function openModal() {
55 | this.setState({
56 | isOpen: true
57 | });
58 | }
59 | }, {
60 | key: "closeModal",
61 | value: function closeModal() {
62 | this.setState({
63 | isOpen: false
64 | });
65 | if (typeof this.props.onClose === 'function') {
66 | this.props.onClose();
67 | }
68 | }
69 | }, {
70 | key: "keydownHandler",
71 | value: function keydownHandler(e) {
72 | if (e.keyCode === 27) {
73 | this.closeModal();
74 | }
75 | }
76 | }, {
77 | key: "componentDidMount",
78 | value: function componentDidMount() {
79 | document.addEventListener('keydown', this.keydownHandler.bind(this));
80 | window.addEventListener('resize', this.resizeModalVideoWhenHeightGreaterThanWindowHeight.bind(this));
81 | this.setState({
82 | modalVideoWidth: this.getWidthFulfillAspectRatio(this.props.ratio, window.innerHeight, window.innerWidth)
83 | });
84 | }
85 | }, {
86 | key: "componentWillUnmount",
87 | value: function componentWillUnmount() {
88 | document.removeEventListener('keydown', this.keydownHandler.bind(this));
89 | window.removeEventListener('resize', this.resizeModalVideoWhenHeightGreaterThanWindowHeight.bind(this));
90 | }
91 | }, {
92 | key: "componentDidUpdate",
93 | value: function componentDidUpdate() {
94 | if (this.state.isOpen && this.modal) {
95 | this.modal.focus();
96 | }
97 | }
98 | }, {
99 | key: "updateFocus",
100 | value: function updateFocus(e) {
101 | if (this.state.isOpen) {
102 | e.preventDefault();
103 | e.stopPropagation();
104 | if (e.keyCode === 9) {
105 | if (this.modal === document.activeElement) {
106 | this.modaliflame.focus();
107 | } else if (this.modalbtn === document.activeElement) {
108 | this.modal.focus();
109 | }
110 | }
111 | }
112 | }
113 |
114 | /**
115 | * Resize modal-video-iframe-wrap when window size changed when the height of the video is greater than the height of the window.
116 | */
117 | }, {
118 | key: "resizeModalVideoWhenHeightGreaterThanWindowHeight",
119 | value: function resizeModalVideoWhenHeightGreaterThanWindowHeight() {
120 | var _this2 = this;
121 | clearTimeout(this.timeout);
122 | this.timeout = setTimeout(function () {
123 | var width = _this2.getWidthFulfillAspectRatio(_this2.props.ratio, window.innerHeight, window.innerWidth);
124 | if (_this2.state.modalVideoWidth != width) {
125 | _this2.setState({
126 | modalVideoWidth: width
127 | });
128 | }
129 | }, 10);
130 | }
131 | }, {
132 | key: "getQueryString",
133 | value: function getQueryString(obj) {
134 | var url = '';
135 | for (var key in obj) {
136 | if (obj.hasOwnProperty(key)) {
137 | if (obj[key] !== null) {
138 | url += "".concat(key, "=").concat(obj[key], "&");
139 | }
140 | }
141 | }
142 | return url.substr(0, url.length - 1);
143 | }
144 | }, {
145 | key: "getYoutubeUrl",
146 | value: function getYoutubeUrl(youtube, videoId) {
147 | var query = this.getQueryString(youtube);
148 | return "//www.youtube.com/embed/".concat(videoId, "?").concat(query);
149 | }
150 | }, {
151 | key: "getVimeoUrl",
152 | value: function getVimeoUrl(vimeo, videoId) {
153 | var query = this.getQueryString(vimeo);
154 | return "//player.vimeo.com/video/".concat(videoId, "?").concat(query);
155 | }
156 | }, {
157 | key: "getYoukuUrl",
158 | value: function getYoukuUrl(youku, videoId) {
159 | var query = this.getQueryString(youku);
160 | return "//player.youku.com/embed/".concat(videoId, "?").concat(query);
161 | }
162 | }, {
163 | key: "getVideoUrl",
164 | value: function getVideoUrl(opt, videoId) {
165 | if (opt.channel === 'youtube') {
166 | return this.getYoutubeUrl(opt.youtube, videoId);
167 | }
168 | if (opt.channel === 'vimeo') {
169 | return this.getVimeoUrl(opt.vimeo, videoId);
170 | }
171 | if (opt.channel === 'youku') {
172 | return this.getYoukuUrl(opt.youku, videoId);
173 | }
174 | if (opt.channel === 'custom') {
175 | return opt.url;
176 | }
177 | }
178 | }, {
179 | key: "getPadding",
180 | value: function getPadding(ratio) {
181 | var arr = ratio.split(':');
182 | var width = Number(arr[0]);
183 | var height = Number(arr[1]);
184 | var padding = height * 100 / width;
185 | return "".concat(padding, "%");
186 | }
187 |
188 | /**
189 | * Calculate the width of the video fulfill aspect ratio.
190 | * When the height of the video is greater than the height of the window,
191 | * this function return the width that fulfill the aspect ratio for the height of the window.
192 | * In other cases, this function return '100%'(the height relative to the width is determined by css).
193 | *
194 | * @param string ratio
195 | * @param number maxWidth
196 | * @returns number | '100%'
197 | */
198 | }, {
199 | key: "getWidthFulfillAspectRatio",
200 | value: function getWidthFulfillAspectRatio(ratio, maxHeight, maxWidth) {
201 | var arr = ratio.split(':');
202 | var width = Number(arr[0]);
203 | var height = Number(arr[1]);
204 |
205 | // Height that fulfill the aspect ratio for maxWidth.
206 | var videoHeight = maxWidth * (height / width);
207 | if (maxHeight < videoHeight) {
208 | // when the height of the video is greater than the height of the window.
209 | // calculate the width that fulfill the aspect ratio for the height of the window.
210 |
211 | // ex: 16:9 aspect ratio
212 | // 16:9 = width : height
213 | // → width = 16 / 9 * height
214 | return Math.floor(width / height * maxHeight);
215 | }
216 | return '100%';
217 | }
218 | }, {
219 | key: "render",
220 | value: function render() {
221 | var _this3 = this;
222 | var modalVideoInnerStyle = {
223 | width: this.state.modalVideoWidth
224 | };
225 | var modalVideoIframeWrapStyle = {
226 | paddingBottom: this.getPadding(this.props.ratio)
227 | };
228 | return /*#__PURE__*/_react.default.createElement(_CSSTransition.default, {
229 | classNames: this.props.classNames.modalVideoEffect,
230 | timeout: this.props.animationSpeed
231 | }, function () {
232 | if (!_this3.state.isOpen) {
233 | return null;
234 | }
235 | return /*#__PURE__*/_react.default.createElement("div", {
236 | className: _this3.props.classNames.modalVideo,
237 | tabIndex: "-1",
238 | role: "dialog",
239 | "area-modal": "true",
240 | "aria-label": _this3.props.aria.openMessage,
241 | onClick: _this3.closeModal,
242 | ref: function ref(node) {
243 | _this3.modal = node;
244 | },
245 | onKeyDown: _this3.updateFocus
246 | }, /*#__PURE__*/_react.default.createElement("div", {
247 | className: _this3.props.classNames.modalVideoBody
248 | }, /*#__PURE__*/_react.default.createElement("div", {
249 | className: _this3.props.classNames.modalVideoInner,
250 | style: modalVideoInnerStyle
251 | }, /*#__PURE__*/_react.default.createElement("div", {
252 | className: _this3.props.classNames.modalVideoIframeWrap,
253 | style: modalVideoIframeWrapStyle
254 | }, _this3.props.children || /*#__PURE__*/_react.default.createElement("iframe", {
255 | width: "460",
256 | height: "230",
257 | src: _this3.getVideoUrl(_this3.props, _this3.props.videoId),
258 | frameBorder: "0",
259 | allow: 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture',
260 | allowFullScreen: _this3.props.allowFullScreen,
261 | onKeyDown: _this3.updateFocus,
262 | ref: function ref(node) {
263 | _this3.modaliflame = node;
264 | },
265 | tabIndex: "-1"
266 | }), /*#__PURE__*/_react.default.createElement("button", {
267 | className: _this3.props.classNames.modalVideoCloseBtn,
268 | "aria-label": _this3.props.aria.dismissBtnMessage,
269 | ref: function ref(node) {
270 | _this3.modalbtn = node;
271 | },
272 | onKeyDown: _this3.updateFocus
273 | })))));
274 | });
275 | }
276 | }], [{
277 | key: "getDerivedStateFromProps",
278 | value: function getDerivedStateFromProps(props) {
279 | return {
280 | isOpen: props.isOpen
281 | };
282 | }
283 | }]);
284 | return ModalVideo;
285 | }(_react.default.Component);
286 | exports.default = ModalVideo;
287 | ModalVideo.defaultProps = {
288 | channel: 'youtube',
289 | isOpen: false,
290 | youtube: {
291 | autoplay: 1,
292 | cc_load_policy: 1,
293 | color: null,
294 | controls: 1,
295 | disablekb: 0,
296 | enablejsapi: 0,
297 | end: null,
298 | fs: 1,
299 | h1: null,
300 | iv_load_policy: 1,
301 | list: null,
302 | listType: null,
303 | loop: 0,
304 | modestbranding: null,
305 | origin: null,
306 | playlist: null,
307 | playsinline: null,
308 | rel: 0,
309 | showinfo: 1,
310 | start: 0,
311 | wmode: 'transparent',
312 | theme: 'dark',
313 | mute: 0
314 | },
315 | ratio: '16:9',
316 | vimeo: {
317 | api: false,
318 | autopause: true,
319 | autoplay: true,
320 | byline: true,
321 | callback: null,
322 | color: null,
323 | height: null,
324 | loop: false,
325 | maxheight: null,
326 | maxwidth: null,
327 | player_id: null,
328 | portrait: true,
329 | title: true,
330 | width: null,
331 | xhtml: false
332 | },
333 | youku: {
334 | autoplay: 1,
335 | show_related: 0
336 | },
337 | allowFullScreen: true,
338 | animationSpeed: 300,
339 | classNames: {
340 | modalVideoEffect: 'modal-video-effect',
341 | modalVideo: 'modal-video',
342 | modalVideoClose: 'modal-video-close',
343 | modalVideoBody: 'modal-video-body',
344 | modalVideoInner: 'modal-video-inner',
345 | modalVideoIframeWrap: 'modal-video-movie-wrap',
346 | modalVideoCloseBtn: 'modal-video-close-btn'
347 | },
348 | aria: {
349 | openMessage: 'You just opened the modal video',
350 | dismissBtnMessage: 'Close the modal by clicking here'
351 | }
352 | };
--------------------------------------------------------------------------------