Fix for iPad and YouTube issues
This commit is contained in:
parent
73a1391f2f
commit
a44c7ecc3a
@ -9,7 +9,7 @@
|
||||
// General functions
|
||||
;(function() {
|
||||
//document.body.addEventListener('ready', function(event) { console.log(event); });
|
||||
|
||||
|
||||
// Setup the player
|
||||
var instances = plyr.setup({
|
||||
debug: true,
|
||||
|
2
dist/plyr.css
vendored
2
dist/plyr.css
vendored
File diff suppressed because one or more lines are too long
4
dist/plyr.js
vendored
4
dist/plyr.js
vendored
File diff suppressed because one or more lines are too long
133
src/js/plyr.js
133
src/js/plyr.js
@ -25,7 +25,7 @@
|
||||
'use strict';
|
||||
|
||||
// Globals
|
||||
var fullscreen,
|
||||
var fullscreen,
|
||||
scroll = { x: 0, y: 0 },
|
||||
|
||||
// Default config
|
||||
@ -36,8 +36,8 @@
|
||||
loop: false,
|
||||
seekTime: 10,
|
||||
volume: 10,
|
||||
volumeMin: 0,
|
||||
volumeMax: 10,
|
||||
volumeMin: 0,
|
||||
volumeMax: 10,
|
||||
volumeStep: 1,
|
||||
duration: null,
|
||||
displayDuration: true,
|
||||
@ -257,6 +257,7 @@
|
||||
isChrome: isChrome,
|
||||
isSafari: isSafari,
|
||||
isIos: /(iPad|iPhone|iPod)/g.test(navigator.platform),
|
||||
isIphone: /(iPhone|iPod)/g.test(navigator.userAgent),
|
||||
isTouch: 'ontouchstart' in document.documentElement
|
||||
};
|
||||
}
|
||||
@ -493,9 +494,9 @@
|
||||
}
|
||||
|
||||
// Create and dispatch the event
|
||||
var event = new CustomEvent(type, {
|
||||
var event = new CustomEvent(type, {
|
||||
bubbles: bubbles,
|
||||
detail: properties
|
||||
detail: properties
|
||||
});
|
||||
|
||||
// Dispatch the event
|
||||
@ -568,7 +569,7 @@
|
||||
// Check variable types
|
||||
var _is = {
|
||||
object: function(input) {
|
||||
return input !== null && typeof(input) === 'object';
|
||||
return input !== null && typeof(input) === 'object';
|
||||
},
|
||||
array: function(input) {
|
||||
return input !== null && (typeof(input) === 'object' && input.constructor === Array);
|
||||
@ -713,7 +714,7 @@
|
||||
timers = {},
|
||||
api;
|
||||
|
||||
// Set media
|
||||
// Set media
|
||||
plyr.media = media;
|
||||
var original = media.cloneNode(true);
|
||||
|
||||
@ -732,7 +733,7 @@
|
||||
if (_is.string(config.logPrefix) && config.logPrefix.length) {
|
||||
args.unshift(config.logPrefix);
|
||||
}
|
||||
|
||||
|
||||
console[type].apply(console, args);
|
||||
}
|
||||
}
|
||||
@ -1037,7 +1038,7 @@
|
||||
var captions = [],
|
||||
caption,
|
||||
req = xhr.responseText;
|
||||
|
||||
|
||||
//According to webvtt spec, line terminator consists of one of the following
|
||||
// CRLF (U+000D U+000A), LF (U+000A) or CR (U+000D)
|
||||
var lineSeparator = '\r\n';
|
||||
@ -1048,7 +1049,7 @@
|
||||
lineSeparator = '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
captions = req.split(lineSeparator+lineSeparator);
|
||||
|
||||
for (var r = 0; r < captions.length; r++) {
|
||||
@ -1530,7 +1531,7 @@
|
||||
mediaId = _parseVimeoId(plyr.embedId);
|
||||
break;
|
||||
|
||||
default:
|
||||
default:
|
||||
mediaId = plyr.embedId;
|
||||
}
|
||||
|
||||
@ -1819,7 +1820,7 @@
|
||||
plyr.embed.stop();
|
||||
plyr.media.paused = true;
|
||||
};
|
||||
|
||||
|
||||
plyr.media.paused = true;
|
||||
plyr.media.currentTime = 0;
|
||||
|
||||
@ -1835,7 +1836,7 @@
|
||||
|
||||
plyr.embed.getDuration().then(function(value) {
|
||||
plyr.media.duration = value;
|
||||
|
||||
|
||||
// Trigger timeupdate
|
||||
_triggerEvent(plyr.media, 'durationchange');
|
||||
});
|
||||
@ -1898,7 +1899,7 @@
|
||||
plyr.embed = window.SC.Widget(this);
|
||||
|
||||
// Setup on ready
|
||||
plyr.embed.bind(window.SC.Widget.Events.READY, function() {
|
||||
plyr.embed.bind(window.SC.Widget.Events.READY, function() {
|
||||
// Create a faux HTML5 API using the Soundcloud API
|
||||
plyr.media.play = function() {
|
||||
plyr.embed.play();
|
||||
@ -2035,7 +2036,7 @@
|
||||
targetTime = duration;
|
||||
}
|
||||
|
||||
// Update seek range and progress
|
||||
// Update seek range and progress
|
||||
_updateSeekDisplay(targetTime);
|
||||
|
||||
// Set the current time
|
||||
@ -2126,7 +2127,7 @@
|
||||
function _toggleFullscreen(event) {
|
||||
// Check for native support
|
||||
var nativeSupport = fullscreen.supportsFullScreen;
|
||||
|
||||
|
||||
if (nativeSupport) {
|
||||
// If it's a fullscreen change event, update the UI
|
||||
if (event && event.type === fullscreen.fullScreenEventName) {
|
||||
@ -2424,7 +2425,7 @@
|
||||
if (!plyr.supported.full) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Default to 0
|
||||
if (_is.undefined(value)) {
|
||||
value = 0;
|
||||
@ -2516,7 +2517,7 @@
|
||||
_updateProgress(event);
|
||||
}
|
||||
|
||||
// Update seek range and progress
|
||||
// Update seek range and progress
|
||||
function _updateSeekDisplay(time) {
|
||||
// Default to 0
|
||||
if (!_is.number(time)) {
|
||||
@ -2526,7 +2527,7 @@
|
||||
var duration = _getDuration(),
|
||||
value = _getPercentage(time, duration);
|
||||
|
||||
// Update progress
|
||||
// Update progress
|
||||
if (plyr.progress && plyr.progress.played) {
|
||||
plyr.progress.played.value = value;
|
||||
}
|
||||
@ -2635,15 +2636,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
// If toggle is false or if we're playing (regardless of toggle),
|
||||
// then set the timer to hide the controls
|
||||
// If toggle is false or if we're playing (regardless of toggle),
|
||||
// then set the timer to hide the controls
|
||||
if (!show || !plyr.media.paused) {
|
||||
timers.hover = window.setTimeout(function() {
|
||||
// If the mouse is over the controls (and not entering fullscreen), bail
|
||||
if ((plyr.controls.pressed || plyr.controls.hover) && !isEnterFullscreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_toggleClass(plyr.container, config.classes.hideControls, true);
|
||||
}, delay);
|
||||
}
|
||||
@ -2720,7 +2721,7 @@
|
||||
_remove(plyr.videoContainer);
|
||||
}
|
||||
|
||||
// Reset class name
|
||||
// Reset class name
|
||||
if (plyr.container) {
|
||||
plyr.container.removeAttribute('class');
|
||||
}
|
||||
@ -2915,7 +2916,7 @@
|
||||
count = get().length;
|
||||
|
||||
// Only handle global key press if there's only one player
|
||||
// and the key is in the allowed keys
|
||||
// and the key is in the allowed keys
|
||||
// and if the focused element is not editable (e.g. text input)
|
||||
// and any that accept key input http://webaim.org/techniques/keyboard/
|
||||
if (count === 1 && _inArray(allowed, code) && (!_is.htmlElement(focused) || !_matches(focused, config.selectors.editable))) {
|
||||
@ -2949,7 +2950,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Divide the max duration into 10th's and times by the number value
|
||||
// Divide the max duration into 10th's and times by the number value
|
||||
_seek((duration / 10) * (code - 48));
|
||||
}
|
||||
|
||||
@ -2967,18 +2968,18 @@
|
||||
|
||||
switch(code) {
|
||||
// 0-9
|
||||
case 48:
|
||||
case 49:
|
||||
case 50:
|
||||
case 51:
|
||||
case 52:
|
||||
case 53:
|
||||
case 54:
|
||||
case 55:
|
||||
case 48:
|
||||
case 49:
|
||||
case 50:
|
||||
case 51:
|
||||
case 52:
|
||||
case 53:
|
||||
case 54:
|
||||
case 55:
|
||||
case 56:
|
||||
case 57: if (!held) { seekByKey(); } break;
|
||||
// Space and K key
|
||||
case 32:
|
||||
case 32:
|
||||
case 75: if (!held) { _togglePlay(); } break;
|
||||
// Arrow up
|
||||
case 38: _increaseVolume(); break;
|
||||
@ -2996,7 +2997,7 @@
|
||||
case 67: if (!held) { _toggleCaptions(); } break;
|
||||
}
|
||||
|
||||
// Escape is handle natively when in full screen
|
||||
// Escape is handle natively when in full screen
|
||||
// So we only need to worry about non native
|
||||
if (!fullscreen.supportsFullScreen && plyr.isFullscreen && code === 27) {
|
||||
_toggleFullscreen();
|
||||
@ -3075,12 +3076,12 @@
|
||||
_on(plyr.container, 'mouseenter mouseleave mousemove touchstart touchend touchcancel touchmove enterfullscreen', _toggleControls);
|
||||
|
||||
// Watch for cursor over controls so they don't hide when trying to interact
|
||||
_on(plyr.controls, 'mouseenter mouseleave', function(event) {
|
||||
_on(plyr.controls, 'mouseenter mouseleave', function(event) {
|
||||
plyr.controls.hover = event.type === 'mouseenter';
|
||||
});
|
||||
|
||||
// Watch for cursor over controls so they don't hide when trying to interact
|
||||
_on(plyr.controls, 'mousedown mouseup touchstart touchend touchcancel', function(event) {
|
||||
_on(plyr.controls, 'mousedown mouseup touchstart touchend touchcancel', function(event) {
|
||||
plyr.controls.pressed = _inArray(['mousedown', 'touchstart'], event.type);
|
||||
});
|
||||
|
||||
@ -3136,7 +3137,7 @@
|
||||
if (plyr.type === 'video') {
|
||||
_setCaption();
|
||||
}
|
||||
|
||||
|
||||
// Restart
|
||||
_seek();
|
||||
|
||||
@ -3238,7 +3239,7 @@
|
||||
|
||||
// Type specific stuff
|
||||
switch (plyr.type) {
|
||||
case 'youtube':
|
||||
case 'youtube':
|
||||
// Clear timers
|
||||
window.clearInterval(timers.buffering);
|
||||
window.clearInterval(timers.playing);
|
||||
@ -3248,11 +3249,11 @@
|
||||
|
||||
// Clean up
|
||||
cleanUp();
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case 'vimeo':
|
||||
// Destroy Vimeo API
|
||||
// Destroy Vimeo API
|
||||
// then clean up (wait, to prevent postmessage errors)
|
||||
plyr.embed.unload().then(cleanUp);
|
||||
|
||||
@ -3294,6 +3295,9 @@
|
||||
// Replace the container with the original element provided
|
||||
plyr.container.parentNode.replaceChild(original, plyr.container);
|
||||
|
||||
// Allow overflow (set on fullscreen)
|
||||
document.body.style.overflow = '';
|
||||
|
||||
// Event
|
||||
_triggerEvent(original, 'destroyed', true);
|
||||
}
|
||||
@ -3448,7 +3452,7 @@
|
||||
isMuted: function() { return plyr.media.muted; },
|
||||
isReady: function() { return _hasClass(plyr.container, config.classes.ready); },
|
||||
isLoading: function() { return _hasClass(plyr.container, config.classes.loading); },
|
||||
isPaused: function() { return plyr.media.paused; },
|
||||
isPaused: function() { return plyr.media.paused; },
|
||||
on: function(event, callback) { _on(plyr.container, event, callback); return this; },
|
||||
play: _play,
|
||||
pause: _pause,
|
||||
@ -3473,7 +3477,7 @@
|
||||
// Everything done
|
||||
function _ready() {
|
||||
// Ready event at end of execution stack
|
||||
window.setTimeout(function() {
|
||||
window.setTimeout(function() {
|
||||
_triggerEvent(plyr.media, 'ready');
|
||||
}, 0);
|
||||
|
||||
@ -3540,31 +3544,48 @@
|
||||
var browser = _browserSniff(),
|
||||
isOldIE = (browser.isIE && browser.version <= 9),
|
||||
isIos = browser.isIos,
|
||||
isIphone = /iPhone|iPod/i.test(navigator.userAgent),
|
||||
audio = !!document.createElement('audio').canPlayType,
|
||||
video = !!document.createElement('video').canPlayType,
|
||||
basic, full;
|
||||
isIphone = browser.isIphone,
|
||||
audioSupport = !!document.createElement('audio').canPlayType,
|
||||
videoSupport = !!document.createElement('video').canPlayType,
|
||||
basic = false,
|
||||
full = false;
|
||||
|
||||
switch (type) {
|
||||
case 'video':
|
||||
basic = video;
|
||||
basic = videoSupport;
|
||||
full = (basic && (!isOldIE && !isIphone));
|
||||
break;
|
||||
|
||||
case 'audio':
|
||||
basic = audio;
|
||||
basic = audioSupport;
|
||||
full = (basic && !isOldIE);
|
||||
break;
|
||||
|
||||
// Vimeo does not seem to be supported on iOS via API
|
||||
// Issue raised https://github.com/vimeo/player.js/issues/87
|
||||
case 'vimeo':
|
||||
basic = true;
|
||||
full = (!isOldIE && !isIos);
|
||||
break;
|
||||
|
||||
case 'youtube':
|
||||
basic = true;
|
||||
full = (!isOldIE && !isIos);
|
||||
|
||||
// YouTube seems to work on iOS 10+ on iPad
|
||||
if (isIos && !isIphone && browser.version >= 10) {
|
||||
full = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'soundcloud':
|
||||
basic = true;
|
||||
full = (!isOldIE && !isIos);
|
||||
full = (!isOldIE && !isIphone);
|
||||
break;
|
||||
|
||||
default:
|
||||
basic = (audio && video);
|
||||
basic = (audioSupport && videoSupport);
|
||||
full = (basic && !isOldIE);
|
||||
}
|
||||
|
||||
@ -3679,15 +3700,15 @@
|
||||
// Listen for events if debugging
|
||||
if (config.debug) {
|
||||
var events = config.events.concat(['setup', 'statechange', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled']);
|
||||
|
||||
_on(instance.getContainer(), events.join(' '), function(event) {
|
||||
|
||||
_on(instance.getContainer(), events.join(' '), function(event) {
|
||||
console.log([config.logPrefix, 'event:', event.type].join(' '), event.detail.plyr);
|
||||
});
|
||||
}
|
||||
|
||||
// Callback
|
||||
_event(instance.getContainer(), 'setup', true, {
|
||||
plyr: instance
|
||||
_event(instance.getContainer(), 'setup', true, {
|
||||
plyr: instance
|
||||
});
|
||||
|
||||
// Add to return array even if it's already setup
|
||||
@ -3718,7 +3739,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
return instances;
|
||||
return instances;
|
||||
}
|
||||
|
||||
return [];
|
||||
|
@ -61,14 +61,14 @@
|
||||
height: (@plyr-range-thumb-height * @plyr-range-thumb-active-scale);
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding: 0;
|
||||
vertical-align: middle;
|
||||
|
||||
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: transparent;
|
||||
|
||||
|
||||
// WebKit
|
||||
&::-webkit-slider-runnable-track {
|
||||
.plyr-range-track();
|
||||
@ -86,7 +86,7 @@
|
||||
&::-moz-range-thumb {
|
||||
.plyr-range-thumb();
|
||||
}
|
||||
|
||||
|
||||
// Microsoft
|
||||
&::-ms-track {
|
||||
height: @plyr-range-track-height;
|
||||
@ -104,7 +104,7 @@
|
||||
&::-ms-thumb {
|
||||
.plyr-range-thumb();
|
||||
// For some reason, Edge uses the -webkit margin above
|
||||
margin-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
&::-ms-tooltip {
|
||||
display: none;
|
||||
@ -116,11 +116,11 @@
|
||||
}
|
||||
&::-moz-focus-outer {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
&.tab-focus:focus {
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
|
||||
// Pressed styles
|
||||
&:active {
|
||||
&::-webkit-slider-thumb {
|
||||
@ -179,9 +179,10 @@
|
||||
.plyr__video-embed {
|
||||
padding-bottom: 56.25%; /* 16:9 */
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
border-radius: inherit;
|
||||
|
||||
// Require z-index to force border-radius
|
||||
// Require overflow and z-index to force border-radius
|
||||
overflow: hidden;
|
||||
z-index: 0;
|
||||
|
||||
iframe {
|
||||
@ -192,7 +193,6 @@
|
||||
height: 100%;
|
||||
border: 0;
|
||||
user-select: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// Vimeo hack
|
||||
@ -265,7 +265,7 @@
|
||||
// Playback controls
|
||||
.plyr__controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
|
||||
@ -329,7 +329,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hide controls
|
||||
// Hide controls
|
||||
.plyr--hide-controls .plyr__controls {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
@ -341,6 +341,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
padding: (@plyr-control-spacing * 5) @plyr-control-spacing @plyr-control-spacing;
|
||||
background: linear-gradient(fade(@plyr-video-controls-bg, 0%), fade(@plyr-video-controls-bg, 50%));
|
||||
border-bottom-left-radius: inherit;
|
||||
@ -476,7 +477,7 @@
|
||||
height: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
|
||||
// The background triangle
|
||||
bottom: -@plyr-tooltip-arrow-size;
|
||||
border-right: @plyr-tooltip-arrow-size solid transparent;
|
||||
@ -624,16 +625,16 @@
|
||||
}
|
||||
&::-moz-progress-bar {
|
||||
transition: width .2s ease;
|
||||
}
|
||||
}
|
||||
&::-ms-fill {
|
||||
transition: width .2s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
.plyr--video .plyr__progress--buffer,
|
||||
.plyr--video .plyr__volume--display {
|
||||
background: @plyr-video-range-track-bg;
|
||||
}
|
||||
.plyr--video .plyr__progress--buffer {
|
||||
.plyr--video .plyr__progress--buffer {
|
||||
color: @plyr-video-progress-buffered-bg;
|
||||
}
|
||||
.plyr--audio .plyr__progress--buffer,
|
||||
@ -740,6 +741,10 @@
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.plyr__video-embed {
|
||||
// Revert overflow change
|
||||
overflow: visible;
|
||||
}
|
||||
.plyr__controls {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
|
@ -61,14 +61,14 @@
|
||||
height: ($plyr-range-thumb-height * $plyr-range-thumb-active-scale);
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding: 0;
|
||||
vertical-align: middle;
|
||||
|
||||
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: transparent;
|
||||
|
||||
|
||||
// WebKit
|
||||
&::-webkit-slider-runnable-track {
|
||||
@include plyr-range-track();
|
||||
@ -86,7 +86,7 @@
|
||||
&::-moz-range-thumb {
|
||||
@include plyr-range-thumb();
|
||||
}
|
||||
|
||||
|
||||
// Microsoft
|
||||
&::-ms-track {
|
||||
height: $plyr-range-track-height;
|
||||
@ -104,7 +104,7 @@
|
||||
&::-ms-thumb {
|
||||
@include plyr-range-thumb();
|
||||
// For some reason, Edge uses the -webkit margin above
|
||||
margin-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
&::-ms-tooltip {
|
||||
display: none;
|
||||
@ -116,11 +116,11 @@
|
||||
}
|
||||
&::-moz-focus-outer {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
&.tab-focus:focus {
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
|
||||
// Pressed styles
|
||||
&:active {
|
||||
&::-webkit-slider-thumb {
|
||||
@ -178,10 +178,10 @@
|
||||
.plyr__video-embed {
|
||||
padding-bottom: 56.25%; /* 16:9 */
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
border-radius: inherit;
|
||||
|
||||
// Require z-index to force border-radius
|
||||
// Require overflow and z-index to force border-radius
|
||||
overflow: hidden;
|
||||
z-index: 0;
|
||||
|
||||
iframe {
|
||||
@ -264,7 +264,7 @@
|
||||
// Playback controls
|
||||
.plyr__controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
|
||||
@ -328,7 +328,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hide controls
|
||||
// Hide controls
|
||||
.plyr--hide-controls .plyr__controls {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
@ -475,7 +475,7 @@
|
||||
height: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
|
||||
// The background triangle
|
||||
bottom: -$plyr-tooltip-arrow-size;
|
||||
border-right: $plyr-tooltip-arrow-size solid transparent;
|
||||
@ -622,16 +622,16 @@
|
||||
}
|
||||
&::-moz-progress-bar {
|
||||
transition: width .2s ease;
|
||||
}
|
||||
}
|
||||
&::-ms-fill {
|
||||
transition: width .2s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
.plyr--video .plyr__progress--buffer,
|
||||
.plyr--video .plyr__volume--display {
|
||||
background: $plyr-video-range-track-bg;
|
||||
}
|
||||
.plyr--video .plyr__progress--buffer {
|
||||
.plyr--video .plyr__progress--buffer {
|
||||
color: $plyr-video-progress-buffered-bg;
|
||||
}
|
||||
.plyr--audio .plyr__progress--buffer,
|
||||
@ -738,6 +738,10 @@
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.plyr__video-embed {
|
||||
// Revert overflow change
|
||||
overflow: visible;
|
||||
}
|
||||
.plyr__controls {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user