cloudinary-video-player 1.6.2-edge.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +4 -0
- package/.snyk +19 -0
- package/.travis.yml +8 -0
- package/CHANGELOG.md +329 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/dist/cld-video-player.css +2110 -0
- package/dist/cld-video-player.js +5249 -0
- package/dist/cld-video-player.light.css +1766 -0
- package/dist/cld-video-player.light.js +1399 -0
- package/dist/cld-video-player.light.min.css +1 -0
- package/dist/cld-video-player.light.min.js +2 -0
- package/dist/cld-video-player.light.min.js.LICENSE.txt +23 -0
- package/dist/cld-video-player.min.css +1 -0
- package/dist/cld-video-player.min.js +2 -0
- package/dist/cld-video-player.min.js.LICENSE.txt +26 -0
- package/dist/fonts/cloudinary_icon_for_black_bg.svg +69 -0
- package/dist/fonts/cloudinary_icon_for_white_bg.svg +69 -0
- package/docs/360.html +102 -0
- package/docs/_template.html +93 -0
- package/docs/adaptive-streaming.html +297 -0
- package/docs/analytics.html +140 -0
- package/docs/api.html +302 -0
- package/docs/audio.html +136 -0
- package/docs/autoplay-fallback.html +138 -0
- package/docs/autoplay-on-scroll.html +107 -0
- package/docs/codec-fallback.html +158 -0
- package/docs/colors.html +135 -0
- package/docs/components.html +284 -0
- package/docs/custom-cld-errors.html +134 -0
- package/docs/floating-player.html +98 -0
- package/docs/fluid.html +117 -0
- package/docs/force-hls-subtitles-ios.html +159 -0
- package/docs/index.html +83 -0
- package/docs/interaction-area.html +398 -0
- package/docs/live-customer.html +128 -0
- package/docs/multiple-players.html +125 -0
- package/docs/playlist-by-tag-cap.html +182 -0
- package/docs/playlist-by-tag.html +133 -0
- package/docs/playlist.html +133 -0
- package/docs/poster.html +155 -0
- package/docs/raw-url.html +104 -0
- package/docs/recommendations.html +155 -0
- package/docs/scripts.js +156 -0
- package/docs/seek-thumbs.html +90 -0
- package/docs/shoppable.html +335 -0
- package/docs/subtitles-and-captions.html +267 -0
- package/docs/transformations.html +171 -0
- package/docs/ui-config.html +108 -0
- package/docs/vast-vpaid.html +149 -0
- package/env.example.js +6 -0
- package/env.js +6 -0
- package/jest-puppeteer.config.js +14 -0
- package/jest.config.js +196 -0
- package/package.json +99 -0
- package/sandbox.config.json +3 -0
- package/setupJest.js +1 -0
- package/src/assets/fonts/VideoJS.svg +120 -0
- package/src/assets/fonts/VideoJS.ttf +0 -0
- package/src/assets/fonts/VideoJS.woff +0 -0
- package/src/assets/fonts/icons.json +120 -0
- package/src/assets/icons/cloudinary_icon_for_black_bg.svg +69 -0
- package/src/assets/icons/cloudinary_icon_for_white_bg.svg +69 -0
- package/src/assets/icons/cloudinary_logo_for_dark_bg.svg +188 -0
- package/src/assets/icons/cloudinary_logo_for_white_bg.svg +188 -0
- package/src/assets/icons/info-circle.svg +17 -0
- package/src/assets/styles/ads-label.scss +16 -0
- package/src/assets/styles/components/interaction-areas.scss +158 -0
- package/src/assets/styles/components/playlist.scss +213 -0
- package/src/assets/styles/components/themedButton.scss +48 -0
- package/src/assets/styles/components/thumbnail.scss +94 -0
- package/src/assets/styles/components/title-bar.scss +67 -0
- package/src/assets/styles/components/triangle-volume-bar.scss +52 -0
- package/src/assets/styles/icons.scss +257 -0
- package/src/assets/styles/main.scss +324 -0
- package/src/assets/styles/mixins/aspect-ratio.scss +16 -0
- package/src/assets/styles/mixins/disable-transition.scss +3 -0
- package/src/assets/styles/mixins/mixins.scss +5 -0
- package/src/assets/styles/mixins/skin.scss +64 -0
- package/src/assets/styles/variables.scss +2 -0
- package/src/assets/styles/videojs-ima.scss +252 -0
- package/src/components/component-utils.js +20 -0
- package/src/components/index.js +21 -0
- package/src/components/interaction-area/interaction-area.const.js +30 -0
- package/src/components/interaction-area/interaction-area.service.js +223 -0
- package/src/components/interaction-area/interaction-area.utils.js +236 -0
- package/src/components/jumpButtons/jump-10-minus.js +21 -0
- package/src/components/jumpButtons/jump-10-plus.js +20 -0
- package/src/components/logoButton/logo-button.const.js +3 -0
- package/src/components/logoButton/logo-button.js +30 -0
- package/src/components/logoButton/logo-button.scss +15 -0
- package/src/components/playlist/components/playlist-button.js +34 -0
- package/src/components/playlist/components/playlist-next-button.js +18 -0
- package/src/components/playlist/components/playlist-previous-button.js +18 -0
- package/src/components/playlist/components/playlist.js +5 -0
- package/src/components/playlist/components/playlist.scss +15 -0
- package/src/components/playlist/components/upcoming-video-overlay.js +149 -0
- package/src/components/playlist/components/upcoming-video-overlay.scss +86 -0
- package/src/components/playlist/layout/playlist-layout-custom.js +21 -0
- package/src/components/playlist/layout/playlist-layout-horizontal.js +16 -0
- package/src/components/playlist/layout/playlist-layout-vertical.js +19 -0
- package/src/components/playlist/layout/playlist-layout.js +110 -0
- package/src/components/playlist/panel/playlist-panel-item.js +86 -0
- package/src/components/playlist/panel/playlist-panel.js +92 -0
- package/src/components/playlist/playlist-widget.js +119 -0
- package/src/components/playlist/playlist.const.js +14 -0
- package/src/components/playlist/playlist.js +413 -0
- package/src/components/playlist/thumbnail/thumbnail.js +69 -0
- package/src/components/progress-control-events-blocker/progress-control-events-blocker.js +17 -0
- package/src/components/qualitySelector/quality-selector.scss +10 -0
- package/src/components/qualitySelector/qualitySelector.js +152 -0
- package/src/components/recommendations-overlay/index.js +3 -0
- package/src/components/recommendations-overlay/recommendations-overlay-content.js +57 -0
- package/src/components/recommendations-overlay/recommendations-overlay-hide-button.js +18 -0
- package/src/components/recommendations-overlay/recommendations-overlay-item.js +35 -0
- package/src/components/recommendations-overlay/recommendations-overlay-primary-item.js +81 -0
- package/src/components/recommendations-overlay/recommendations-overlay-secondary-item.js +48 -0
- package/src/components/recommendations-overlay/recommendations-overlay-secondary-items-container.js +35 -0
- package/src/components/recommendations-overlay/recommendations-overlay.js +94 -0
- package/src/components/recommendations-overlay/recommendations-overlay.scss +182 -0
- package/src/components/shoppable-bar/layout/bar-layout.js +111 -0
- package/src/components/shoppable-bar/layout/shoppable-panel-toggle.js +64 -0
- package/src/components/shoppable-bar/layout/shoppable-products-overlay.js +87 -0
- package/src/components/shoppable-bar/panel/shoppable-panel-item.js +105 -0
- package/src/components/shoppable-bar/panel/shoppable-panel.js +172 -0
- package/src/components/shoppable-bar/shoppable-post-widget.js +110 -0
- package/src/components/shoppable-bar/shoppable-widget.const.js +52 -0
- package/src/components/shoppable-bar/shoppable-widget.js +111 -0
- package/src/components/shoppable-bar/shoppable-widget.scss +359 -0
- package/src/components/themeButton/themedButton.const.js +3 -0
- package/src/components/themeButton/themedButton.js +25 -0
- package/src/components/title-bar/title-bar.js +79 -0
- package/src/config/defaults.js +25 -0
- package/src/extended-events.js +228 -0
- package/src/index.js +18 -0
- package/src/mixins/eventable.js +54 -0
- package/src/mixins/playlistable.js +106 -0
- package/src/plugins/analytics/index.js +245 -0
- package/src/plugins/autoplay-on-scroll/index.js +86 -0
- package/src/plugins/cloudinary/common.js +216 -0
- package/src/plugins/cloudinary/event-handler-registry.js +46 -0
- package/src/plugins/cloudinary/index.js +345 -0
- package/src/plugins/cloudinary/models/audio-source/audio-source.const.js +11 -0
- package/src/plugins/cloudinary/models/audio-source/audio-source.js +82 -0
- package/src/plugins/cloudinary/models/base-source.js +107 -0
- package/src/plugins/cloudinary/models/image-source.js +26 -0
- package/src/plugins/cloudinary/models/video-source/video-source.const.js +32 -0
- package/src/plugins/cloudinary/models/video-source/video-source.js +239 -0
- package/src/plugins/cloudinary/models/video-source/video-source.utils.js +57 -0
- package/src/plugins/colors/index.js +303 -0
- package/src/plugins/context-menu/components/context-menu-item.js +12 -0
- package/src/plugins/context-menu/components/context-menu.js +63 -0
- package/src/plugins/context-menu/context-menu.scss +30 -0
- package/src/plugins/context-menu/contextMenuContent.js +53 -0
- package/src/plugins/context-menu/index.js +134 -0
- package/src/plugins/dash/index.js +26 -0
- package/src/plugins/dash/setup-audio-tracks.js +112 -0
- package/src/plugins/dash/setup-text-tracks.js +195 -0
- package/src/plugins/dash/videojs-dash.js +372 -0
- package/src/plugins/floating-player/floating-player.scss +74 -0
- package/src/plugins/floating-player/index.js +129 -0
- package/src/plugins/ima/index.js +1775 -0
- package/src/plugins/index.js +31 -0
- package/src/plugins/interactive-plugin/index.js +10 -0
- package/src/plugins/videojs-http-source-selector/components/SourceMenuButton.js +98 -0
- package/src/plugins/videojs-http-source-selector/components/SourceMenuItem.js +52 -0
- package/src/plugins/videojs-http-source-selector/plugin.js +82 -0
- package/src/plugins/videojs-http-source-selector/plugin.scss +9 -0
- package/src/plugins/vtt-thumbnails/index.js +526 -0
- package/src/plugins/vtt-thumbnails/vtt-thumbnails.scss +29 -0
- package/src/utils/api.js +32 -0
- package/src/utils/apply-with-props.js +32 -0
- package/src/utils/array.js +22 -0
- package/src/utils/assign.js +27 -0
- package/src/utils/attributes-normalizer.js +72 -0
- package/src/utils/cloudinary.js +165 -0
- package/src/utils/css-prefix.js +43 -0
- package/src/utils/dom.js +74 -0
- package/src/utils/find.js +28 -0
- package/src/utils/fontFace.js +25 -0
- package/src/utils/groupBy.js +12 -0
- package/src/utils/index.js +29 -0
- package/src/utils/matches.js +11 -0
- package/src/utils/mixin.js +5 -0
- package/src/utils/object.js +26 -0
- package/src/utils/playButton.js +9 -0
- package/src/utils/positioning.js +78 -0
- package/src/utils/querystring.js +12 -0
- package/src/utils/slicing.js +21 -0
- package/src/utils/string.js +15 -0
- package/src/utils/throttle.js +30 -0
- package/src/utils/time.js +77 -0
- package/src/utils/type-inference.js +35 -0
- package/src/validators/validators-functions.js +48 -0
- package/src/validators/validators-types.js +78 -0
- package/src/validators/validators.js +110 -0
- package/src/video-player.const.js +68 -0
- package/src/video-player.js +761 -0
- package/src/video-player.utils.js +123 -0
- package/test/adaptive-streaming.test.js +38 -0
- package/test/ads.test.js +35 -0
- package/test/analytics.test.js +111 -0
- package/test/api.test.js +111 -0
- package/test/autoplay.scroll.test.js +23 -0
- package/test/basic-ui.test.js +59 -0
- package/test/colors.test.js +58 -0
- package/test/components.test.js +21 -0
- package/test/custom-error.test.js +24 -0
- package/test/fluid.test.js +36 -0
- package/test/isValidConfig.test.js +224 -0
- package/test/mocks/cloudinary-core-mock.js +0 -0
- package/test/mocks/styleMock.js +1 -0
- package/test/multiplayer.test.js +25 -0
- package/test/playlist.test.js +60 -0
- package/test/puppeteer/vp-env.js +19 -0
- package/test/recommendations.test.js +38 -0
- package/test/title-bar.test.js +28 -0
- package/test/ui-conf.test.js +49 -0
- package/test/unit/cloudinaryConfig.test.js +22 -0
- package/test/unit/cloudinaryUtils.test.js +53 -0
- package/test/unit/utils.test.js +27 -0
- package/test/unit/videoSource.test.js +454 -0
- package/tsconfig.json +15 -0
- package/types/video-player-tests.js +12 -0
- package/types/video-player-tests.ts +31 -0
- package/types/video-player.d.ts +570 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { elementsCreator, styleElement } from '../../utils/dom';
|
|
2
|
+
import { get } from '../../utils/object';
|
|
3
|
+
import {
|
|
4
|
+
CLOSE_INTERACTION_AREA_LAYOUT_DELAY,
|
|
5
|
+
INTERACTION_AREA_HAND_ICON,
|
|
6
|
+
INTERACTION_AREA_LAYOUT_LOCAL_STORAGE_NAME,
|
|
7
|
+
INTERACTION_AREAS_CONTAINER_CLASS_NAME,
|
|
8
|
+
INTERACTION_AREAS_PREFIX,
|
|
9
|
+
INTERACTION_AREAS_THEME
|
|
10
|
+
} from './interaction-area.const';
|
|
11
|
+
import { noop } from '../../utils/type-inference';
|
|
12
|
+
import { getDefaultPlayerColor } from '../../plugins/colors';
|
|
13
|
+
import { forEach, some } from '../../utils/array';
|
|
14
|
+
import { themedButton } from '../themeButton/themedButton';
|
|
15
|
+
import { BUTTON_THEME } from '../themeButton/themedButton.const';
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
const getInteractionAreaItemId = (item, index) => item.id || item.type || `id_${index}`;
|
|
19
|
+
|
|
20
|
+
export const getInteractionAreaItem = ({ playerOptions, videojsOptions }, item, index, durationTime, onClick) => {
|
|
21
|
+
const defaultColor = getDefaultPlayerColor(videojsOptions);
|
|
22
|
+
const accentColor = playerOptions && playerOptions.colors ? playerOptions.colors.accent : defaultColor.accent;
|
|
23
|
+
|
|
24
|
+
// theme = 'pulsing' / 'shadowed'
|
|
25
|
+
const theme = get(videojsOptions, 'interactionAreas.theme.template', INTERACTION_AREAS_THEME.PULSING);
|
|
26
|
+
|
|
27
|
+
return elementsCreator({
|
|
28
|
+
tag: 'div',
|
|
29
|
+
attr: { class: `${INTERACTION_AREAS_PREFIX}-item theme-${theme}`, 'data-id': getInteractionAreaItemId(item, index) },
|
|
30
|
+
style: {
|
|
31
|
+
left: `${item.left}%`,
|
|
32
|
+
top: `${item.top}%`,
|
|
33
|
+
width: `${item.width}%`,
|
|
34
|
+
height: `${item.height}%`,
|
|
35
|
+
transitionDuration: `${durationTime}ms`
|
|
36
|
+
},
|
|
37
|
+
event: {
|
|
38
|
+
name: 'click',
|
|
39
|
+
callback: onClick
|
|
40
|
+
},
|
|
41
|
+
children: [
|
|
42
|
+
{
|
|
43
|
+
tag: 'div',
|
|
44
|
+
attr: { class: `${INTERACTION_AREAS_PREFIX}-area-marker` },
|
|
45
|
+
children: [
|
|
46
|
+
{
|
|
47
|
+
tag: 'div',
|
|
48
|
+
attr: { class: `${INTERACTION_AREAS_PREFIX}-marker-shadow` },
|
|
49
|
+
style: { [theme === INTERACTION_AREAS_THEME.SHADOWED ? 'backgroundColor' : 'borderColor']: accentColor }
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
tag: 'div',
|
|
53
|
+
attr: { class: `${INTERACTION_AREAS_PREFIX}-marker-main` },
|
|
54
|
+
style: { borderColor: accentColor }
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
export const percentageToFixedValue = (outOf, value) => (outOf / (100 / +value));
|
|
64
|
+
|
|
65
|
+
export const getZoomTransformation = (videoElement, interactionAreaItem) => {
|
|
66
|
+
|
|
67
|
+
const { videoHeight, videoWidth } = videoElement;
|
|
68
|
+
|
|
69
|
+
const itemX = percentageToFixedValue(videoWidth, interactionAreaItem.left);
|
|
70
|
+
const itemY = percentageToFixedValue(videoHeight, interactionAreaItem.top);
|
|
71
|
+
const itemWidth = percentageToFixedValue(videoWidth, interactionAreaItem.width);
|
|
72
|
+
const itemHeight = percentageToFixedValue(videoHeight, interactionAreaItem.height);
|
|
73
|
+
|
|
74
|
+
const videoAspectRatio = videoWidth / videoHeight;
|
|
75
|
+
const itemAspectRatio = itemWidth / itemHeight;
|
|
76
|
+
|
|
77
|
+
const width = Math.round(itemAspectRatio > 1 || videoAspectRatio > 1 ? itemHeight * itemAspectRatio : itemWidth);
|
|
78
|
+
const height = Math.round(width / videoAspectRatio);
|
|
79
|
+
|
|
80
|
+
const x = Math.round(itemX - (width - itemWidth) / 2);
|
|
81
|
+
const y = Math.round(itemY - (height - itemHeight) / 2);
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
width,
|
|
85
|
+
height,
|
|
86
|
+
x: Math.min(Math.max(x, 0), videoWidth - width),
|
|
87
|
+
y: Math.min(Math.max(y, 0), videoHeight - height),
|
|
88
|
+
crop: 'crop'
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
export const setInteractionAreasContainer = (videojs, newInteractionAreasContainer) => {
|
|
94
|
+
const currentInteractionAreasContainer = getInteractionAreasContainer(videojs);
|
|
95
|
+
|
|
96
|
+
if (currentInteractionAreasContainer) {
|
|
97
|
+
currentInteractionAreasContainer.replaceWith(newInteractionAreasContainer);
|
|
98
|
+
} else {
|
|
99
|
+
// do not use element.append for ie11 support
|
|
100
|
+
videojs.el().appendChild(newInteractionAreasContainer);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const getInteractionAreaElementById = (interactionAreasContainer, item, index) => interactionAreasContainer.querySelector(`[data-id=${getInteractionAreaItemId(item, index)}]`);
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
export const updateInteractionAreasItem = (videojs, configs, interactionAreasData, previousInteractionAreasData, durationTime, onClick) => {
|
|
108
|
+
const interactionAreasContainer = getInteractionAreasContainer(videojs);
|
|
109
|
+
|
|
110
|
+
forEach(interactionAreasData, (item, index) => {
|
|
111
|
+
const itemElement = getInteractionAreaElementById(interactionAreasContainer, item, index);
|
|
112
|
+
const itemId = getInteractionAreaItemId(item);
|
|
113
|
+
const isExistItem = some(previousInteractionAreasData, i => getInteractionAreaItemId(i) === itemId);
|
|
114
|
+
|
|
115
|
+
// in case the element of the item is in the dom and exist in the previous data , it update the element position
|
|
116
|
+
if (isExistItem && itemElement) {
|
|
117
|
+
styleElement(itemElement, {
|
|
118
|
+
left: `${item.left}%`,
|
|
119
|
+
top: `${item.top}%`,
|
|
120
|
+
width: `${item.width}%`,
|
|
121
|
+
height: `${item.height}%`,
|
|
122
|
+
transitionDuration: `${durationTime}ms`
|
|
123
|
+
});
|
|
124
|
+
// if the element did not exist before , not in the dom and not in the previous data , it add a new element
|
|
125
|
+
} else if (!isExistItem && !itemElement) {
|
|
126
|
+
// do not use element.append for ie11 support
|
|
127
|
+
interactionAreasContainer.appendChild(getInteractionAreaItem(configs, item, index, durationTime, (event) => {
|
|
128
|
+
onClick({ event, item, index });
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// checking the previous data for element that should be removed if not exist in the new data object.
|
|
134
|
+
forEach(previousInteractionAreasData, (item, index) => {
|
|
135
|
+
const itemElement = getInteractionAreaElementById(interactionAreasContainer, item, index);
|
|
136
|
+
const itemId = getInteractionAreaItemId(item);
|
|
137
|
+
const shouldBeRemoved = !some(interactionAreasData, i => getInteractionAreaItemId(i) === itemId);
|
|
138
|
+
|
|
139
|
+
if (itemElement && shouldBeRemoved) {
|
|
140
|
+
// do not use element.remove for ie11 support
|
|
141
|
+
itemElement.parentNode.removeChild(itemElement);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const shouldShowAreaLayoutMessage = (interactionAreasConfig) => {
|
|
148
|
+
const isLayoutEnabled = get(interactionAreasConfig, 'layout.enable', true);
|
|
149
|
+
|
|
150
|
+
return isLayoutEnabled && localStorage.getItem(INTERACTION_AREA_LAYOUT_LOCAL_STORAGE_NAME) !== 'true';
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
const onClickInteractionAreaLayoutClick = (checked, onClick = noop) => {
|
|
155
|
+
localStorage.setItem(INTERACTION_AREA_LAYOUT_LOCAL_STORAGE_NAME, JSON.parse(checked));
|
|
156
|
+
onClick();
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export const createInteractionAreaLayoutMessage = (videojs, onClick, showItAgainCheckbox = false) => {
|
|
160
|
+
|
|
161
|
+
let checked = false;
|
|
162
|
+
|
|
163
|
+
const id = `checkbox_${Math.round(Math.random() * 10000)}`;
|
|
164
|
+
|
|
165
|
+
const tracksContainer = elementsCreator({
|
|
166
|
+
tag: 'div',
|
|
167
|
+
attr: { class: `${INTERACTION_AREAS_CONTAINER_CLASS_NAME} ${INTERACTION_AREAS_PREFIX}-layout-message ${showItAgainCheckbox ? '' : 'clickable'}` },
|
|
168
|
+
onClick: !showItAgainCheckbox ? () => onClickInteractionAreaLayoutClick(checked, onClick) : null,
|
|
169
|
+
children: [
|
|
170
|
+
{
|
|
171
|
+
tag: 'img',
|
|
172
|
+
attr: { class: `${INTERACTION_AREAS_PREFIX}-layout-icon`, src: INTERACTION_AREA_HAND_ICON }
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
tag: 'h3',
|
|
176
|
+
attr: { class: `${INTERACTION_AREAS_PREFIX}-layout-message-title` },
|
|
177
|
+
children: 'Tap on dots to zoom for a product.'
|
|
178
|
+
},
|
|
179
|
+
themedButton({
|
|
180
|
+
text: 'Got it',
|
|
181
|
+
theme: BUTTON_THEME.TRANSPARENT_WHITE,
|
|
182
|
+
loadingDelay: showItAgainCheckbox ? 0 : CLOSE_INTERACTION_AREA_LAYOUT_DELAY,
|
|
183
|
+
onClick: showItAgainCheckbox ? () => onClickInteractionAreaLayoutClick(checked, onClick) : null
|
|
184
|
+
}),
|
|
185
|
+
showItAgainCheckbox && {
|
|
186
|
+
tag: 'div',
|
|
187
|
+
attr: { class: `${INTERACTION_AREAS_PREFIX}-layout-message-do-not-show` },
|
|
188
|
+
children: [
|
|
189
|
+
{
|
|
190
|
+
tag: 'input',
|
|
191
|
+
attr: { type: 'checkbox', class: `${INTERACTION_AREAS_PREFIX}-layout-message-checkbox`, id },
|
|
192
|
+
event: {
|
|
193
|
+
name: 'input',
|
|
194
|
+
callback: (event) => {
|
|
195
|
+
checked = event.target.checked;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
tag: 'label',
|
|
201
|
+
attr: { class: `${INTERACTION_AREAS_PREFIX}-layout-message-checkbox-title`, for: id },
|
|
202
|
+
children: 'Don׳t show it again'
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
].filter(i => i)
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
setInteractionAreasContainer(videojs, tracksContainer);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const getInteractionAreasContainer = (videojs) => videojs.el().querySelector(`.${INTERACTION_AREAS_CONTAINER_CLASS_NAME}`);
|
|
213
|
+
|
|
214
|
+
export const removeInteractionAreasContainer = (videojs) => {
|
|
215
|
+
const interactionAreasContainer = getInteractionAreasContainer(videojs);
|
|
216
|
+
|
|
217
|
+
// do not use element.remove for ie11 support
|
|
218
|
+
interactionAreasContainer && interactionAreasContainer.parentNode.removeChild(interactionAreasContainer);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
export const setInteractionAreasContainerSize = (videojs, videoElement) => {
|
|
222
|
+
|
|
223
|
+
const interactionAreasContainer = getInteractionAreasContainer(videojs);
|
|
224
|
+
|
|
225
|
+
if (!interactionAreasContainer) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const { videoHeight, videoWidth } = videoElement;
|
|
230
|
+
const videoAspectRatio = videoWidth / videoHeight;
|
|
231
|
+
|
|
232
|
+
const width = videoAspectRatio * videoElement.clientHeight;
|
|
233
|
+
|
|
234
|
+
interactionAreasContainer.style.width = `${videoElement.clientWidth < width ? '100%' : width}px`;
|
|
235
|
+
interactionAreasContainer.style.height = videoElement.clientWidth < width ? `${videoElement.clientWidth / videoAspectRatio}px` : '100%';
|
|
236
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import videojs from 'video.js';
|
|
2
|
+
|
|
3
|
+
const ClickableComponent = videojs.getComponent('ClickableComponent');
|
|
4
|
+
|
|
5
|
+
class JumpBackButton extends ClickableComponent {
|
|
6
|
+
|
|
7
|
+
handleClick(event) {
|
|
8
|
+
super.handleClick(event);
|
|
9
|
+
this.player().currentTime(this.player().currentTime() - 10);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
createEl() {
|
|
13
|
+
return videojs.dom.createEl('button', {
|
|
14
|
+
className: 'vjs-control vjs-icon-skip-10-min vjs-button'
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
videojs.registerComponent('JumpBackButton', JumpBackButton);
|
|
20
|
+
|
|
21
|
+
export default JumpBackButton;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import videojs from 'video.js';
|
|
2
|
+
|
|
3
|
+
const ClickableComponent = videojs.getComponent('ClickableComponent');
|
|
4
|
+
|
|
5
|
+
class JumpForwardButton extends ClickableComponent {
|
|
6
|
+
handleClick(event) {
|
|
7
|
+
super.handleClick(event);
|
|
8
|
+
this.player().currentTime(this.player().currentTime() + 10);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
createEl() {
|
|
12
|
+
return videojs.dom.createEl('button', {
|
|
13
|
+
className: 'vjs-control vjs-icon-skip-10-plus vjs-button'
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
videojs.registerComponent('JumpForwardButton', JumpForwardButton);
|
|
19
|
+
|
|
20
|
+
export default JumpForwardButton;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export const LIGHT_BG_ICON = 'https://res.cloudinary.com/cloudinary-marketing/image/upload/v1597164189/creative_source/Logo/Cloud%20Glyph/cloudinary_cloud_glyph_regular.svg';
|
|
2
|
+
|
|
3
|
+
export const DARK_BG_ICON = 'https://res.cloudinary.com/cloudinary-marketing/image/upload/v1597164191/creative_source/Logo/Cloud%20Glyph/cloudinary_cloud_glyph_white.svg';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import videojs from 'video.js';
|
|
2
|
+
import './logo-button.scss';
|
|
3
|
+
import { DARK_BG_ICON, LIGHT_BG_ICON } from './logo-button.const';
|
|
4
|
+
import { isLight } from '../../video-player.utils';
|
|
5
|
+
|
|
6
|
+
// support VJS5 & VJS6 at the same time
|
|
7
|
+
const ClickableComponent = videojs.getComponent('ClickableComponent');
|
|
8
|
+
|
|
9
|
+
class LogoButton extends ClickableComponent {
|
|
10
|
+
|
|
11
|
+
createEl() {
|
|
12
|
+
const opts = this.options_.playerOptions;
|
|
13
|
+
const display = opts.showLogo ? 'block' : 'none';
|
|
14
|
+
|
|
15
|
+
const _isLight = isLight(opts) ? LIGHT_BG_ICON : DARK_BG_ICON;
|
|
16
|
+
|
|
17
|
+
const bgIcon = opts.logoImageUrl ? opts.logoImageUrl : _isLight;
|
|
18
|
+
|
|
19
|
+
return videojs.dom.createEl('a', {}, {
|
|
20
|
+
class: 'vjs-control vjs-cloudinary-button vjs-button',
|
|
21
|
+
href: opts.logoOnclickUrl,
|
|
22
|
+
target: '_blank',
|
|
23
|
+
style: `display: ${display}; background-image: url(${bgIcon})`
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
videojs.registerComponent('logoButton', LogoButton);
|
|
29
|
+
|
|
30
|
+
export default LogoButton;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Player Cloudinary button
|
|
2
|
+
.vjs-control-bar a.vjs-control.vjs-cloudinary-button {
|
|
3
|
+
background-image: url("../../assets/icons/cloudinary_icon_for_black_bg.svg");
|
|
4
|
+
background-size: 25px;
|
|
5
|
+
background-position: center;
|
|
6
|
+
background-repeat: no-repeat;
|
|
7
|
+
|
|
8
|
+
.cld-video-player-skin-light & {
|
|
9
|
+
background-image: url("../../assets/icons/cloudinary_icon_for_white_bg.svg");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
&:hover {
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import videojs from 'video.js';
|
|
2
|
+
|
|
3
|
+
// Get the ClickableComponent base class from Video.js
|
|
4
|
+
const ClickableComponent = videojs.getComponent('ClickableComponent');
|
|
5
|
+
|
|
6
|
+
// Create a common class for playlist buttons
|
|
7
|
+
class PlaylistButton extends ClickableComponent {
|
|
8
|
+
|
|
9
|
+
constructor(player, options) {
|
|
10
|
+
// It is important to invoke the superclass before anything else,
|
|
11
|
+
// to get all the features of components out of the box!
|
|
12
|
+
super(player, options);
|
|
13
|
+
|
|
14
|
+
const type = options.type;
|
|
15
|
+
|
|
16
|
+
if (!type && type !== 'previous' && type !== 'next') {
|
|
17
|
+
throw new Error('Type must be either \'previous\' or \'next\'');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// The `createEl` function of a component creates its DOM element.
|
|
22
|
+
createEl() {
|
|
23
|
+
const type = this.options_.type;
|
|
24
|
+
const typeCssClass = `vjs-icon-play-${type}`;
|
|
25
|
+
|
|
26
|
+
return videojs.dom.createEl('button', {
|
|
27
|
+
// Prefixing classes of elements within a player with "vjs-"
|
|
28
|
+
// is a convention used in Video.js.
|
|
29
|
+
className: `vjs-control vjs-playlist-button vjs-button ${typeCssClass}`
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default PlaylistButton;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import PlaylistButton from './playlist-button';
|
|
2
|
+
import videojs from 'video.js';
|
|
3
|
+
|
|
4
|
+
class PlaylistNextButton extends PlaylistButton {
|
|
5
|
+
|
|
6
|
+
constructor(player) {
|
|
7
|
+
super(player, { type: 'next' });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
handleClick(event) {
|
|
11
|
+
super.handleClick(event);
|
|
12
|
+
this.player().cloudinary.playlist().playNext();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
videojs.registerComponent('PlaylistNextButton', PlaylistNextButton);
|
|
17
|
+
|
|
18
|
+
export default PlaylistNextButton;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import PlaylistButton from './playlist-button';
|
|
2
|
+
import videojs from 'video.js';
|
|
3
|
+
|
|
4
|
+
class PlaylistPreviousButton extends PlaylistButton {
|
|
5
|
+
|
|
6
|
+
constructor(player) {
|
|
7
|
+
super(player, { type: 'previous' });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
handleClick(event) {
|
|
11
|
+
super.handleClick(event);
|
|
12
|
+
this.player().cloudinary.playlist().playPrevious();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
videojs.registerComponent('PlaylistPreviousButton', PlaylistPreviousButton);
|
|
17
|
+
|
|
18
|
+
export default PlaylistPreviousButton;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import videojs from 'video.js';
|
|
2
|
+
import './upcoming-video-overlay.scss';
|
|
3
|
+
|
|
4
|
+
// support VJS5 & VJS6 at the same time
|
|
5
|
+
const dom = videojs.dom || videojs;
|
|
6
|
+
|
|
7
|
+
const Component = videojs.getComponent('Component');
|
|
8
|
+
const ClickableComponent = videojs.getComponent('ClickableComponent');
|
|
9
|
+
|
|
10
|
+
class UpcomingVideoOverlay extends ClickableComponent {
|
|
11
|
+
|
|
12
|
+
constructor(player, ...args) {
|
|
13
|
+
super(player, ...args);
|
|
14
|
+
|
|
15
|
+
this._setEvents(player);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
_setEvents(player) {
|
|
19
|
+
player.on('upcomingvideoshow', this._show.bind(this));
|
|
20
|
+
player.on('upcomingvideohide', this._hide.bind(this));
|
|
21
|
+
player.on('playlistitemchanged', this._onPlaylistItemChange.bind(this));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
_hide() {
|
|
25
|
+
this.removeClass('vjs-upcoming-video-show');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
_disableTransition(block) {
|
|
29
|
+
this.addClass('disable-transition');
|
|
30
|
+
block();
|
|
31
|
+
this.removeClass('disable-transition');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_onPlaylistItemChange(_, event) {
|
|
35
|
+
this._hide();
|
|
36
|
+
this._disableTransition(() => {
|
|
37
|
+
if (event.next) {
|
|
38
|
+
this.setItem(event.next);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_show() {
|
|
44
|
+
const videoShowClass = 'vjs-upcoming-video-show';
|
|
45
|
+
const ima = this.player().ima;
|
|
46
|
+
const adsManager = ima === 'object' && ima.getAdsManager();
|
|
47
|
+
|
|
48
|
+
if (adsManager) {
|
|
49
|
+
if (!adsManager.getCurrentAd() || adsManager.getCurrentAd().isLinear()) {
|
|
50
|
+
this.addClass(videoShowClass);
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
this.addClass(videoShowClass);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setTitle(source) {
|
|
58
|
+
const title = this.getChild('upcomingVideoOverlayContent')
|
|
59
|
+
.getChild('upcomingVideoOverlayBar')
|
|
60
|
+
.getChild('upcomingVideoOverlayTitle');
|
|
61
|
+
|
|
62
|
+
title.setContent(source.info().title || source.publicId());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
setItem(source) {
|
|
66
|
+
this._source = source;
|
|
67
|
+
|
|
68
|
+
const maxWidth = parseInt(window.getComputedStyle(this.el(), null).getPropertyValue('max-width'), 10);
|
|
69
|
+
const maxHeight = Math.round(maxWidth * (9 / 16.0));
|
|
70
|
+
|
|
71
|
+
const transformation = { crop: 'pad', background: 'auto:predominant', width: maxWidth, height: maxHeight };
|
|
72
|
+
const content = this.getChild('upcomingVideoOverlayContent');
|
|
73
|
+
|
|
74
|
+
this.setTitle(source);
|
|
75
|
+
|
|
76
|
+
content.el().style.backgroundImage = `url(${this._source.poster().url({ transformation })})`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
handleClick() {
|
|
80
|
+
super.handleClick(event);
|
|
81
|
+
|
|
82
|
+
this.player().cloudinary.playlist().playNext();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
createEl() {
|
|
86
|
+
return super.createEl('div', {
|
|
87
|
+
className: 'vjs-upcoming-video'
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class UpcomingVideoOverlayContent extends Component {
|
|
93
|
+
createEl() {
|
|
94
|
+
// Content wraps image and bar
|
|
95
|
+
return super.createEl('div', {
|
|
96
|
+
className: 'aspect-ratio-content'
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
class UpcomingVideoOverlayTitle extends Component {
|
|
102
|
+
|
|
103
|
+
setContent(title) {
|
|
104
|
+
this._contentSpan.innerText = title;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
createEl() {
|
|
108
|
+
const el = super.createEl('div', {
|
|
109
|
+
className: 'vjs-control vjs-upcoming-video-title'
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const container = dom.createEl('div', {
|
|
113
|
+
className: 'vjs-upcoming-video-title-display',
|
|
114
|
+
innerHTML: '<span class="vjs-control-text">Next up</span>Next up: '
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this._contentSpan = dom.createEl('span', {
|
|
118
|
+
className: 'vjs-upcoming-video-title-display-label'
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
container.appendChild(this._contentSpan);
|
|
122
|
+
|
|
123
|
+
el.appendChild(container);
|
|
124
|
+
|
|
125
|
+
return el;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
class UpcomingVideoOverlayBar extends Component {
|
|
130
|
+
|
|
131
|
+
createEl() {
|
|
132
|
+
return super.createEl('div', {
|
|
133
|
+
className: 'vjs-upcoming-video-bar'
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
UpcomingVideoOverlay.prototype.options_ = { children: ['upcomingVideoOverlayContent'] };
|
|
139
|
+
videojs.registerComponent('upcomingVideoOverlay', UpcomingVideoOverlay);
|
|
140
|
+
|
|
141
|
+
UpcomingVideoOverlayContent.prototype.options_ = { children: ['upcomingVideoOverlayBar'] };
|
|
142
|
+
videojs.registerComponent('upcomingVideoOverlayContent', UpcomingVideoOverlayContent);
|
|
143
|
+
|
|
144
|
+
UpcomingVideoOverlayBar.prototype.options_ = { children: ['upcomingVideoOverlayTitle', 'playlistNextButton'] };
|
|
145
|
+
videojs.registerComponent('upcomingVideoOverlayBar', UpcomingVideoOverlayBar);
|
|
146
|
+
|
|
147
|
+
videojs.registerComponent('upcomingVideoOverlayTitle', UpcomingVideoOverlayTitle);
|
|
148
|
+
|
|
149
|
+
export default UpcomingVideoOverlay;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
@import '../../../assets/styles/mixins/aspect-ratio';
|
|
2
|
+
|
|
3
|
+
$upcoming-video-max-width: 30em;
|
|
4
|
+
// $_upcoming-video-transition: visibility 1s, opacity 1s, background 1.1s 0s step-end;
|
|
5
|
+
$_upcoming-video-transition: visibility 0.4s, opacity 0.4s;
|
|
6
|
+
|
|
7
|
+
.vjs-upcoming-video {
|
|
8
|
+
opacity: 0;
|
|
9
|
+
transition: bottom 0.1s, $_upcoming-video-transition;
|
|
10
|
+
@include aspect-ratio(16, 9);
|
|
11
|
+
|
|
12
|
+
visibility: hidden;
|
|
13
|
+
position: absolute;
|
|
14
|
+
bottom: 4.5em;
|
|
15
|
+
right: 0.75em;
|
|
16
|
+
width: 38.7%;
|
|
17
|
+
max-width: $upcoming-video-max-width;
|
|
18
|
+
border: 1px solid #E8E8E9;
|
|
19
|
+
|
|
20
|
+
.aspect-ratio-content {
|
|
21
|
+
background-size: cover;
|
|
22
|
+
|
|
23
|
+
.vjs-upcoming-video-bar {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex: auto;
|
|
26
|
+
justify-content: space-between;
|
|
27
|
+
|
|
28
|
+
position: absolute;
|
|
29
|
+
height: 3.0em;
|
|
30
|
+
line-height: 3.0em;
|
|
31
|
+
width: 100%;
|
|
32
|
+
bottom: 0px;
|
|
33
|
+
|
|
34
|
+
.vjs-upcoming-video-title {
|
|
35
|
+
flex: auto;
|
|
36
|
+
text-align: left;
|
|
37
|
+
display: block;
|
|
38
|
+
width: auto;
|
|
39
|
+
max-width: 80%;
|
|
40
|
+
padding-left: 1em;
|
|
41
|
+
padding-right: 1em;
|
|
42
|
+
|
|
43
|
+
.vjs-upcoming-video-title-display {
|
|
44
|
+
text-overflow: ellipsis;
|
|
45
|
+
overflow: hidden;
|
|
46
|
+
white-space: nowrap;
|
|
47
|
+
|
|
48
|
+
.vjs-upcoming-video-title-display-label {
|
|
49
|
+
font-weight: 400;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.vjs-has-started.vjs-user-inactive.vjs-playing & {
|
|
57
|
+
transition: bottom 1s, $_upcoming-video-transition;
|
|
58
|
+
bottom: 0.7em;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
&.vjs-upcoming-video-show {
|
|
62
|
+
transition: $_upcoming-video-transition;
|
|
63
|
+
opacity: 1;
|
|
64
|
+
visibility: visible;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
&.disable-transition {
|
|
68
|
+
transition: visibility 0s;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@media only screen and (max-width: 768px) {
|
|
73
|
+
&:before {
|
|
74
|
+
display: none;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.cld-video-player.cld-video-player-skin-dark &,
|
|
78
|
+
.cld-video-player.cld-video-player-skin-light & {
|
|
79
|
+
border: none;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.aspect-ratio-content {
|
|
83
|
+
background-image: none !important;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import PlaylistLayout from './playlist-layout';
|
|
2
|
+
|
|
3
|
+
class PlaylistLayoutCustom extends PlaylistLayout {
|
|
4
|
+
|
|
5
|
+
getCls() {
|
|
6
|
+
let cls = super.getCls();
|
|
7
|
+
cls.push('cld-plw-custom');
|
|
8
|
+
|
|
9
|
+
return cls;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
createEl() {
|
|
13
|
+
const el = super.createEl();
|
|
14
|
+
this.options_.renderTo.appendChild(el);
|
|
15
|
+
|
|
16
|
+
return el;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
export default PlaylistLayoutCustom;
|