mirror of
https://github.com/Alvin-Zilverstand/carsite.git
synced 2026-03-06 02:56:38 +01:00
ds
This commit is contained in:
16
index.html
16
index.html
@@ -787,14 +787,14 @@
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/bootstrap.bundle.min.js"></script>
|
||||
<script src="js/jarallax.js"></script>
|
||||
<script src="https://r.mobirisesite.com/849385/assets/smoothscroll/smooth-scroll.js?rnd=1730245704585"></script>
|
||||
<script src="https://r.mobirisesite.com/849385/assets/ytplayer/index.js?rnd=1730245704585"></script>
|
||||
<script src="https://r.mobirisesite.com/849385/assets/dropdown/js/navbar-dropdown.js?rnd=1730245704585"></script>
|
||||
<script src="https://r.mobirisesite.com/849385/assets/vimeoplayer/player.js?rnd=1730245704585"></script>
|
||||
<script src="https://r.mobirisesite.com/849385/assets/mbr-switch-arrow/mbr-switch-arrow.js?rnd=1730245704585"></script>
|
||||
<script src="https://r.mobirisesite.com/849385/assets/scrollgallery/scroll-gallery.js?rnd=1730245704585"></script>
|
||||
<script src="https://r.mobirisesite.com/849385/assets/theme/js/script.js?rnd=1730245704585"></script>
|
||||
<script src="https://r.mobirisesite.com/849385/assets/formoid/formoid.min.js?rnd=1730245704585"></script>
|
||||
<script src="js/smooth-scroll.js"></script>
|
||||
<script src="js/yt-player.js"></script>
|
||||
<script src="js/nav-dropdown.js"></script>
|
||||
<script src="js/player.js"></script>
|
||||
<script src="js/arrow-switch.js"></script>
|
||||
<script src="js/scroll-galary.js"></script>
|
||||
<script src="js/script.js"></script>
|
||||
<script src="js/formoid.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
154
js/arrow-switch.js
Normal file
154
js/arrow-switch.js
Normal file
@@ -0,0 +1,154 @@
|
||||
// var $,
|
||||
// isJQuery = typeof jQuery == 'function';
|
||||
// if (isJQuery) $ = jQuery;
|
||||
// var isBuilder = document.querySelector('html').classList.contains('is-builder');
|
||||
// function ready(fn) {
|
||||
// if (document.readyState != 'loading'){
|
||||
// fn();
|
||||
// } else {
|
||||
// document.addEventListener('DOMContentLoaded', fn);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /*
|
||||
// if (!isBuilder) {
|
||||
// if (typeof window.initSwitchArrowPlugin === 'undefined') {
|
||||
// window.initSwitchArrowPlugin = true;
|
||||
// ready(function () {
|
||||
// if (document.querySelectorAll('.accordionStyles').length != 0) {
|
||||
// document.querySelectorAll('.accordionStyles .card-header a[role="button"]').forEach(function (el) {
|
||||
// if (!el.classList.contains('collapsed')) {
|
||||
// el.classList.add('collapsed');
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// document.querySelectorAll('.accordionStyles .card-header a[role="button"]').forEach(element => {
|
||||
// element.addEventListener('click', function (event) {
|
||||
// var el = event.target,
|
||||
// id = el.closest('.accordionStyles').getAttribute('id'),
|
||||
// iscollapsing = el.closest('.card').querySelector('.panel-collapse'),
|
||||
// card = el.closest('.card'),
|
||||
// sign = card.querySelector('span.mbr-iconfont'),
|
||||
// button = card.querySelector('a[role="button"]');
|
||||
// if (!iscollapsing.classList.contains('collapsing')) {
|
||||
// if (id.indexOf('toggle') != -1) {
|
||||
// if (button.classList.contains('collapsed')) {
|
||||
// sign.classList.remove('mbri-arrow-down');
|
||||
// sign.classList.add('mbri-arrow-up');
|
||||
// button.classList.remove('collapsed');
|
||||
// } else {
|
||||
// sign.classList.remove('mbri-arrow-up');
|
||||
// sign.classList.add('mbri-arrow-down');
|
||||
// button.classList.add('collapsed');
|
||||
// }
|
||||
// }
|
||||
// else if (id.indexOf('accordion') != -1) {
|
||||
// var accordion = el.closest('.accordionStyles ');
|
||||
// if (accordion) {
|
||||
// if (card.querySelector('.collapsed')) {
|
||||
// sign.classList.remove('mbri-arrow-down');
|
||||
// sign.classList.add('mbri-arrow-up');
|
||||
// button.classList.remove('collapsed');
|
||||
// } else {
|
||||
// sign.classList.remove('mbri-arrow-up');
|
||||
// sign.classList.add('mbri-arrow-down');
|
||||
// button.classList.add('collapsed');
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// };
|
||||
// */
|
||||
|
||||
|
||||
// if (!isBuilder) {
|
||||
// if (typeof window.initSwitchArrowPlugin === 'undefined'){
|
||||
// window.initSwitchArrowPlugin = true;
|
||||
// ready(function () {
|
||||
// if (document.querySelectorAll('.accordionStyles').length!=0) {
|
||||
// document.querySelectorAll('.accordionStyles > .card > .card-header > a[role="button"]').forEach(function(el){
|
||||
// if(!el.classList.contains('collapsed')){
|
||||
// el.classList.add('collapsed');
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// document.querySelectorAll('.accordionStyles > .card > .card-header > a[role="button"]').forEach(el => {
|
||||
// el.addEventListener('click', () => {
|
||||
// var id = el.closest('.accordionStyles').getAttribute('id'),
|
||||
// iscollapsing = el.closest('.card').querySelector('.panel-collapse'),
|
||||
// sign = el.querySelector('span.sign');
|
||||
|
||||
// if (iscollapsing.classList.contains('collapsing')) {
|
||||
// if (id.indexOf('toggle')!= -1 || id.indexOf('accordion')!= -1) {
|
||||
// if (el.classList.contains('collapsed')) {
|
||||
// sign.classList.remove('mbri-arrow-up');
|
||||
// sign.classList.add('mbri-arrow-down');
|
||||
// } else {
|
||||
// sign.classList.remove('mbri-arrow-down');
|
||||
// sign.classList.add('mbri-arrow-up');
|
||||
// }
|
||||
// if (id.indexOf('accordion')!= -1) {
|
||||
// var accordion = el.closest('.accordionStyles');
|
||||
// Array.from(accordion.children).filter(el => el.querySelector('span.sign') !== sign)
|
||||
// .forEach(child => {
|
||||
// child.querySelector('span.sign').classList.remove('mbri-arrow-up');
|
||||
// child.querySelector('span.sign').classList.add('mbri-arrow-down');
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// };
|
||||
|
||||
|
||||
// /*
|
||||
// var isBuilder = $('html').hasClass('is-builder');
|
||||
// if (!isBuilder) {
|
||||
// if (typeof window.initSwitchArrowPlugin === 'undefined'){
|
||||
// window.initSwitchArrowPlugin = true;
|
||||
// $(document).ready(function() {
|
||||
// if ($('.accordionStyles').length!=0) {
|
||||
// $('.accordionStyles .card-header a[role="button"]').each(function(){
|
||||
// if(!$(this).hasClass('collapsed')){
|
||||
// $(this).addClass('collapsed');
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
|
||||
// $('.accordionStyles .card-header a[role="button"]').click(function(){
|
||||
// var $id = $(this).closest('.accordionStyles').attr('id'),
|
||||
// $iscollapsing = $(this).closest('.card').find('.panel-collapse');
|
||||
|
||||
// if (!$iscollapsing.hasClass('collapsing')) {
|
||||
// if ($id.indexOf('toggle') != -1){
|
||||
// if ($(this).hasClass('collapsed')) {
|
||||
// $(this).find('span.sign').removeClass('mbri-arrow-down').addClass('mbri-arrow-up');
|
||||
// }
|
||||
// else{
|
||||
// $(this).find('span.sign').removeClass('mbri-arrow-up').addClass('mbri-arrow-down');
|
||||
// }
|
||||
// }
|
||||
// else if ($id.indexOf('accordion')!=-1) {
|
||||
// var $accordion = $(this).closest('.accordionStyles ');
|
||||
|
||||
// $accordion.children('.card').each(function() {
|
||||
// $(this).find('span.sign').removeClass('mbri-arrow-up').addClass('mbri-arrow-down');
|
||||
// });
|
||||
// if ($(this).hasClass('collapsed')) {
|
||||
// $(this).find('span.sign').removeClass('mbri-arrow-down').addClass('mbri-arrow-up');
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
// */
|
||||
//# sourceMappingURL=mbr-switch-arrow.js.map
|
||||
243
js/formoid.js
Normal file
243
js/formoid.js
Normal file
@@ -0,0 +1,243 @@
|
||||
var Formoid = (function () {
|
||||
|
||||
var API_URL = 'https://formoid.net/api/push';
|
||||
|
||||
function $ajax(url, settings) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open(settings.type, url);
|
||||
xhr.onload = function () {
|
||||
if (xhr.status !== 200) {
|
||||
return reject(new Error('Incorrect server response.'));
|
||||
}
|
||||
resolve(xhr.responseText);
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
var message = 'Failed to query the server. ';
|
||||
if ('onLine' in navigator && !navigator.onLine) {
|
||||
message += 'No connection to the Internet.';
|
||||
} else {
|
||||
message += 'Check the connection and try again.';
|
||||
}
|
||||
reject(new Error(message));
|
||||
};
|
||||
xhr.send(settings.data);
|
||||
})
|
||||
};
|
||||
|
||||
var prop = function (name, args) {
|
||||
name = '__' + name + '__';
|
||||
if (args.length) {
|
||||
this[name] = args[0];
|
||||
return this;
|
||||
}
|
||||
return this[name];
|
||||
};
|
||||
|
||||
var Form = function (settings) {
|
||||
settings = settings || {};
|
||||
this.__email__ = settings.email || '';
|
||||
this.__title__ = settings.title || '';
|
||||
this.__data__ = settings.data || [];
|
||||
};
|
||||
|
||||
Form.prototype.email = function (value) {
|
||||
return prop.call(this, 'email', arguments);
|
||||
};
|
||||
|
||||
Form.prototype.title = function (value) {
|
||||
return prop.call(this, 'title', arguments);
|
||||
};
|
||||
|
||||
Form.prototype.data = function (value) {
|
||||
return prop.call(this, 'data', arguments);
|
||||
};
|
||||
|
||||
Form.prototype.send = function (data) {
|
||||
return $ajax(API_URL, {
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
email: this.__email__,
|
||||
form: {
|
||||
title: this.__title__,
|
||||
data: (arguments.length ? data : this.__data__)
|
||||
}
|
||||
})
|
||||
})
|
||||
.then(function(responseText) {
|
||||
var data;
|
||||
try {
|
||||
data = JSON.parse(responseText);
|
||||
} catch (e) {
|
||||
throw new Error('Incorrect server response.');
|
||||
}
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
return data.response;
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
Form: function (settings) {
|
||||
return new Form(settings);
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
const formModalDOM = document.createElement('div');
|
||||
let formModal;
|
||||
|
||||
formModalDOM.classList.add('modal');
|
||||
formModalDOM.setAttribute('tabindex', -1);
|
||||
formModalDOM.style.overflow = 'hidden';
|
||||
|
||||
if (typeof bootstrap !== 'undefined') {
|
||||
if (bootstrap.Tooltip.VERSION.startsWith(5)) {
|
||||
//bs5
|
||||
formModalDOM.innerHTML = `
|
||||
<div class="modal-dialog d-flex align-items-center" style="">
|
||||
<div class="modal-content" style="height:auto;border-radius:0;border:none;box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.25);">
|
||||
<div class="modal-body d-flex justify-content-end flex-column align-items-end">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<p class="display-7" style="text-align:center;width:100%;">Modal body text goes here.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
} else {
|
||||
// bs4
|
||||
formModalDOM.innerHTML = `
|
||||
<div class="modal-dialog d-flex align-items-center" style="">
|
||||
<div class="modal-content" style="height:auto;border-radius:0;border:none;box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.25);">
|
||||
<div class="modal-body d-flex justify-content-end flex-column align-items-end">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<p class="display-7" style="text-align:center;width:100%;">Modal body text goes here.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
} else if ($.fn.Tooltip) {
|
||||
// bs3
|
||||
formModalDOM.innerHTML = `
|
||||
<div class="modal-dialog d-flex align-items-center" style="">
|
||||
<div class="modal-content" style="height:auto;border-radius:0;border:none;box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.25);">
|
||||
<div class="modal-body d-flex justify-content-end flex-column align-items-end">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<p class="display-7" style="text-align:center;width:100%;">Modal body text goes here.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
if (bootstrap) {
|
||||
formModal = new bootstrap.Modal(formModalDOM);
|
||||
}
|
||||
|
||||
var isValidEmail = function (input) {
|
||||
return input.value ? /^([^@]+?)@(([a-z0-9]-*)*[a-z0-9]+\.)+([a-z0-9]+)$/i.test(input.value) : true;
|
||||
};
|
||||
|
||||
var formComponents = document.querySelectorAll('[data-form-type="formoid"]');
|
||||
|
||||
formComponents.forEach(function (component) {
|
||||
var formData,
|
||||
form = component.tagName === 'FORM' ? component : component.querySelector('form'),
|
||||
alert = component.querySelector('[data-form-alert]'),
|
||||
title = component.getAttribute('data-form-title') ? component : component.querySelector('[data-form-title]'),
|
||||
submit = component.querySelector('[type="submit"]'),
|
||||
inputArr = component.querySelectorAll('[data-form-field]'),
|
||||
alertSuccess = alert.innerHTML;
|
||||
|
||||
form.addEventListener('change', function (event) {
|
||||
if (event.target.type === 'file') {
|
||||
if (event.target.files[0].size > 1000000) {
|
||||
formModal._element.querySelector('.modal-body p').innerText = 'File size must be less than 1mb';
|
||||
formModal._element.querySelector('.modal-content').classList.add('alert-danger');
|
||||
formModal._element.querySelector('.modal-content').style.backgroundColor = '#ff9966';
|
||||
formModal.show();
|
||||
submit.classList.add('btn-loading');
|
||||
submit.setAttribute('disabled', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
form.addEventListener('submit', function (event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if (submit.classList.contains('btn-loading')) return;
|
||||
|
||||
var inputs = inputArr;
|
||||
|
||||
form.classList.add('form-active');
|
||||
submit.classList.add('btn-loading');
|
||||
submit.setAttribute('disabled', true);
|
||||
alert.innerHTML = '';
|
||||
|
||||
formData = formData || Formoid.Form({
|
||||
email: component.querySelector('[data-form-email]').value,
|
||||
title: title.getAttribute('data-form-title') || title.innerText
|
||||
});
|
||||
|
||||
function parseInput(input) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
// valide email
|
||||
if (input.getAttribute('name') === 'email' && !isValidEmail(input)) {
|
||||
return reject(new Error('Form is not valid'));
|
||||
}
|
||||
var name = input.getAttribute('data-form-field') || input.getAttribute('name');
|
||||
switch (input.getAttribute('type')) {
|
||||
case 'file':
|
||||
var file = input.files[0];
|
||||
if (!file) return resolve();
|
||||
var reader = new FileReader()
|
||||
reader.onloadend = function () {
|
||||
resolve([name, reader.result]);
|
||||
};
|
||||
reader.onerror = function () {
|
||||
reject(reader.error);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
break;
|
||||
case 'checkbox':
|
||||
resolve([name, input.checked ? input.value : 'No']);
|
||||
break;
|
||||
case 'radio':
|
||||
resolve(input.checked && [name, input.value]);
|
||||
break;
|
||||
default:
|
||||
resolve([name, input.value]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Promise
|
||||
.all(Array.prototype.map.call(inputs, function (input) { return parseInput(input); }))
|
||||
.then(function (data) { return formData.send(data.filter(function (v) { return v; })); })
|
||||
.then(function (message) {
|
||||
form.reset();
|
||||
form.classList.remove('form-active');
|
||||
formModal._element.querySelector('.modal-body p').innerText = alertSuccess || message;
|
||||
formModal._element.querySelector('.modal-content').classList.add('alert-success');
|
||||
formModal._element.querySelector('.modal-content').style.backgroundColor = '#70c770'
|
||||
formModal.show();
|
||||
}, function (err) {
|
||||
formModal._element.querySelector('.modal-body p').innerText = err.message;
|
||||
formModal._element.querySelector('.modal-content').classList.add('alert-danger');
|
||||
formModal._element.querySelector('.modal-content').style.backgroundColor = '#ff9966'
|
||||
})
|
||||
.then(function () {
|
||||
submit.classList.remove('btn-loading');
|
||||
submit.removeAttribute('disabled');
|
||||
});
|
||||
});
|
||||
|
||||
inputArr.forEach(function (elem) {
|
||||
elem.addEventListener('focus', function () {
|
||||
submit.classList.remove('btn-loading')
|
||||
submit.removeAttribute('disabled');
|
||||
});
|
||||
});
|
||||
})
|
||||
//# sourceMappingURL=formoid.min.js.map
|
||||
112
js/nav-dropdown.js
Normal file
112
js/nav-dropdown.js
Normal file
@@ -0,0 +1,112 @@
|
||||
(function () {
|
||||
|
||||
function _dataApiHandler(event) {
|
||||
|
||||
if (event.type === 'resize') {
|
||||
document.body.classList.remove('navbar-dropdown-open');
|
||||
document.querySelector('.navbar-dropdown').querySelector('.navbar-collapse').classList.remove('show');
|
||||
document.querySelector('.navbar-dropdown').classList.remove('opened');
|
||||
Array.from(document.querySelector('.navbar-dropdown').querySelectorAll('.navbar-toggler[aria-expanded="true"]')).forEach(el => {
|
||||
let target = el.querySelector(el.getAttribute('data-target'));
|
||||
if (target) {
|
||||
target.classList.remove('in');
|
||||
target.setAttribute('aria-expanded', 'false');
|
||||
el.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
let scrollTop = document.documentElement.scrollTop;
|
||||
|
||||
Array.from(document.querySelectorAll('.navbar-dropdown')).forEach(el => {
|
||||
|
||||
if (!el.matches('.navbar-fixed-top')) return;
|
||||
|
||||
if (el.matches('.transparent') && !el.classList.contains('opened')) {
|
||||
if (scrollTop > 0) {
|
||||
el.classList.remove('bg-color');
|
||||
} else {
|
||||
el.classList.add('bg-color');
|
||||
}
|
||||
}
|
||||
|
||||
if (scrollTop > 0) {
|
||||
el.classList.add('navbar-short');
|
||||
} else {
|
||||
el.classList.remove('navbar-short');
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var _timeout;
|
||||
var windowEvents = ['scroll', 'resize'];
|
||||
windowEvents.forEach(eventName => {
|
||||
document.addEventListener(eventName, event => {
|
||||
clearTimeout(_timeout);
|
||||
_timeout = setTimeout(function () {
|
||||
_dataApiHandler(event);
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
const dropdownEvents = ['show.bs.collapse', 'hide.bs.collapse'];
|
||||
dropdownEvents.forEach(eventName => {
|
||||
document.addEventListener(eventName, ({ target }) => {
|
||||
const dropDown = target.closest('.navbar-dropdown');
|
||||
|
||||
if (!dropDown) return;
|
||||
|
||||
if (eventName === 'show.bs.collapse') {
|
||||
document.body.classList.add('navbar-dropdown-open')
|
||||
dropDown.classList.add('opened')
|
||||
} else {
|
||||
document.body.classList.remove('navbar-dropdown-open');
|
||||
dropDown.classList.remove('opened');
|
||||
window.dispatchEvent(new Event('scroll.bs.navbar-dropdown.data-api'));
|
||||
dropDown.dispatchEvent(new Event('collapse.bs.navbar-dropdown'));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const isBuilder = document.querySelector('html').classList.contains('is-builder');
|
||||
|
||||
if (!isBuilder){
|
||||
document.addEventListener('click', function(event) {
|
||||
const target = event.target;
|
||||
if (target.classList.contains('nav-link') || target.parentNode.classList.contains('nav-link')) return;
|
||||
const navbarContent = document.querySelector('#navbarSupportedContent');
|
||||
const navbarDropdown = document.querySelector('.navbar-dropdown');
|
||||
const isNavbarExpanded = navbarContent.classList.contains('show');
|
||||
const isNavItemLink = target.closest('.nav-item a:not(.dropdown-toggle)');
|
||||
const isNavbarDropdownCollapsed = navbarDropdown.classList.contains('collapsed');
|
||||
if (window.matchMedia('(max-width: 991px)').matches || isNavbarDropdownCollapsed) {
|
||||
if (isNavbarExpanded && !target.closest('.navbar-collapse') || isNavItemLink) {
|
||||
new bootstrap.Collapse(navbarContent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('collapse.bs.nav-dropdown', event => {
|
||||
let dropDown = event.relatedTarget.closest('.navbar-dropdown');
|
||||
if (dropDown) {
|
||||
let toggler = dropDown.querySelector('.navbar-toggler[aria-expanded="true"]');
|
||||
if (toggler) {
|
||||
toggler.dispatchEvent(new Event('click'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const dropdowns = document.querySelectorAll('.nav-link.dropdown-toggle')
|
||||
dropdowns.forEach(item => {
|
||||
item.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
e.target.parentNode.classList.toggle('open');
|
||||
})
|
||||
})
|
||||
})();
|
||||
//# sourceMappingURL=navbar-dropdown.js.map
|
||||
2472
js/player.js
Normal file
2472
js/player.js
Normal file
File diff suppressed because it is too large
Load Diff
1692
js/script.js
Normal file
1692
js/script.js
Normal file
File diff suppressed because it is too large
Load Diff
211
js/scroll-galary.js
Normal file
211
js/scroll-galary.js
Normal file
@@ -0,0 +1,211 @@
|
||||
(() => {
|
||||
if (document.querySelector('html').classList.contains('is-builder')) return;
|
||||
|
||||
function applyScrollAnimation() {
|
||||
const galleryWrappers = document.querySelectorAll('.gallery-wrapper');
|
||||
|
||||
if (!galleryWrappers.length) return;
|
||||
|
||||
galleryWrappers.forEach((galleryWrapper) => {
|
||||
const gridContainer1 = galleryWrapper.querySelector('.grid-container-1');
|
||||
const gridContainer2 = galleryWrapper.querySelector('.grid-container-2');
|
||||
const gridContainer3 = galleryWrapper.querySelector('.grid-container-3');
|
||||
|
||||
const initialTransform1 = gridContainer1 ? getComputedStyle(gridContainer1).transform : null;
|
||||
const initialTransform2 = gridContainer2 ? getComputedStyle(gridContainer2).transform : null;
|
||||
const initialTransform3 = gridContainer3 ? getComputedStyle(gridContainer3).transform : null;
|
||||
|
||||
function updateScrollAnimation() {
|
||||
const scrollPosition = window.scrollY;
|
||||
const screenHeight = window.innerHeight;
|
||||
const galleryBlockTop = galleryWrapper.getBoundingClientRect().top + window.scrollY;
|
||||
const distanceFromTop = galleryBlockTop - screenHeight;
|
||||
|
||||
if (scrollPosition >= distanceFromTop) {
|
||||
const matrix1 = initialTransform1 ? new DOMMatrix(initialTransform1) : null;
|
||||
const matrix2 = initialTransform2 ? new DOMMatrix(initialTransform2) : null;
|
||||
const matrix3 = initialTransform3 ? new DOMMatrix(initialTransform3) : null;
|
||||
|
||||
if (matrix1) {
|
||||
const gridItems1 = gridContainer1.querySelectorAll('.grid-item');
|
||||
const numItems1 = gridItems1.length;
|
||||
|
||||
//fix for mbr-ai
|
||||
gridItems1.forEach(item => {
|
||||
const img = item.querySelector('img');
|
||||
img.classList.remove('hidden');
|
||||
});
|
||||
|
||||
if (numItems1 >= 8 && !gridContainer1.classList.contains('moving-left')) {
|
||||
matrix1.m41 = -2000;
|
||||
}
|
||||
|
||||
if (numItems1 >= 8 && !gridContainer1.classList.contains('moving-left') && window.innerWidth < 1200) {
|
||||
matrix1.m41 = -900;
|
||||
}
|
||||
|
||||
function cloneGalleryItems() {
|
||||
for (let i = 0; i < numItems1; i++) {
|
||||
const clonedItem = gridItems1[i].cloneNode(true);
|
||||
gridContainer1.appendChild(clonedItem);
|
||||
}
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case (numItems1 < 8):
|
||||
cloneGalleryItems();
|
||||
case (gridContainer1.classList.contains('moving-right')):
|
||||
translateX1 = matrix1.m41 + (scrollPosition - distanceFromTop) * 0.9;
|
||||
break;
|
||||
case (gridContainer1.classList.contains('moving-left')):
|
||||
translateX1 = matrix1.m41 - (scrollPosition - distanceFromTop) * 0.9;
|
||||
break;
|
||||
default:
|
||||
if (window.innerWidth >= 2560) {
|
||||
translateX1 = matrix1.m41 + (scrollPosition - distanceFromTop) * 0.9;
|
||||
} else if (window.innerWidth >= 1440) {
|
||||
translateX1 = matrix1.m41 + (scrollPosition - distanceFromTop) * 0.55;
|
||||
} else if (window.innerWidth >= 1024) {
|
||||
translateX1 = matrix1.m41 + (scrollPosition - distanceFromTop) * 0.45;
|
||||
} else if (window.innerWidth >= 768) {
|
||||
translateX1 = matrix1.m41 + (scrollPosition - distanceFromTop) * 0.35;
|
||||
} else if (window.innerWidth >= 320) {
|
||||
translateX1 = matrix1.m41 + (scrollPosition - distanceFromTop) * 0.25;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
gridContainer1.style.transform = `translate3d(${translateX1}px, 0, 0)`;
|
||||
}
|
||||
|
||||
if (matrix2) {
|
||||
const gridItems2 = gridContainer2.querySelectorAll('.grid-item');
|
||||
const numItems2 = gridItems2.length;
|
||||
|
||||
//fix for mbr-ai
|
||||
gridItems2.forEach(item => {
|
||||
const img = item.querySelector('img');
|
||||
img.classList.remove('hidden');
|
||||
});
|
||||
|
||||
if (numItems2 >= 8 && gridContainer2.classList.contains('moving-left')) {
|
||||
matrix2.m41 = -2000;
|
||||
}
|
||||
|
||||
if (numItems2 >= 8 && !gridContainer2.classList.contains('moving-left') && window.innerWidth < 1200) {
|
||||
matrix2.m41 = 0;
|
||||
}
|
||||
|
||||
function cloneGalleryItems() {
|
||||
for (let i = 0; i < numItems2; i++) {
|
||||
const clonedItem = gridItems2[i].cloneNode(true);
|
||||
gridContainer2.appendChild(clonedItem);
|
||||
}
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case (numItems2 < 8):
|
||||
cloneGalleryItems();
|
||||
case (gridContainer2.classList.contains('moving-right')):
|
||||
translateX2 = matrix2.m41 + (scrollPosition - distanceFromTop) * 0.9;
|
||||
break;
|
||||
case (gridContainer2.classList.contains('moving-left')):
|
||||
translateX2 = matrix2.m41 - (scrollPosition - distanceFromTop) * 0.9;
|
||||
break;
|
||||
default:
|
||||
if (window.innerWidth >= 2560) {
|
||||
translateX2 = matrix2.m41 - (scrollPosition - distanceFromTop) * 0.9;
|
||||
} else if (window.innerWidth >= 1440) {
|
||||
translateX2 = matrix2.m41 - (scrollPosition - distanceFromTop) * 0.55;
|
||||
} else if (window.innerWidth >= 1024) {
|
||||
translateX2 = matrix2.m41 - (scrollPosition - distanceFromTop) * 0.45;
|
||||
} else if (window.innerWidth >= 768) {
|
||||
translateX2 = matrix2.m41 - (scrollPosition - distanceFromTop) * 0.35;
|
||||
} else if (window.innerWidth >= 320) {
|
||||
translateX2 = matrix2.m41 - (scrollPosition - distanceFromTop) * 0.25;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
gridContainer2.style.transform = `translate3d(${translateX2}px, 0, 0)`;
|
||||
}
|
||||
|
||||
if (matrix3) {
|
||||
const gridItems3 = gridContainer3.querySelectorAll('.grid-item');
|
||||
const numItems3 = gridItems3.length;
|
||||
|
||||
//fix for mbr-ai
|
||||
gridItems3.forEach(item => {
|
||||
const img = item.querySelector('img');
|
||||
img.classList.remove('hidden');
|
||||
});
|
||||
|
||||
if (numItems3 >= 8 && !gridContainer3.classList.contains('moving-left')) {
|
||||
matrix3.m41 = -2000
|
||||
}
|
||||
|
||||
if (numItems3 >= 8 && !gridContainer3.classList.contains('moving-left') && window.innerWidth < 1200) {
|
||||
matrix3.m41 = -900;
|
||||
}
|
||||
|
||||
let translateX3 = matrix3.m41 + (scrollPosition - distanceFromTop) * 0.9;
|
||||
|
||||
function cloneGalleryItems() {
|
||||
for (let i = 0; i < numItems3; i++) {
|
||||
const clonedItem = gridItems3[i].cloneNode(true);
|
||||
gridContainer3.appendChild(clonedItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (numItems3 < 8) {
|
||||
cloneGalleryItems()
|
||||
}
|
||||
|
||||
if (!gridContainer3.classList.contains('moving-left')) {
|
||||
if (window.innerWidth >= 2560) {
|
||||
translateX3 = matrix3.m41 + (scrollPosition - distanceFromTop) * 0.9;
|
||||
} else if (window.innerWidth >= 1440) {
|
||||
translateX3 = matrix3.m41 + (scrollPosition - distanceFromTop) * 0.55;
|
||||
} else if (window.innerWidth >= 1024) {
|
||||
translateX3 = matrix3.m41 + (scrollPosition - distanceFromTop) * 0.45;
|
||||
} else if (window.innerWidth >= 768) {
|
||||
translateX3 = matrix3.m41 + (scrollPosition - distanceFromTop) * 0.35;
|
||||
} else if (window.innerWidth >= 320) {
|
||||
translateX3 = matrix3.m41 + (scrollPosition - distanceFromTop) * 0.25;
|
||||
}
|
||||
} else if (gridContainer3.classList.contains('moving-left')) {
|
||||
if (numItems3 < 8) {
|
||||
cloneGalleryItems()
|
||||
}
|
||||
if (window.innerWidth >= 2560) {
|
||||
translateX3 = matrix3.m41 - (scrollPosition - distanceFromTop) * 0.9;
|
||||
} else if (window.innerWidth >= 1440) {
|
||||
translateX3 = matrix3.m41 - (scrollPosition - distanceFromTop) * 0.55;
|
||||
} else if (window.innerWidth >= 1024) {
|
||||
translateX3 = matrix3.m41 - (scrollPosition - distanceFromTop) * 0.45;
|
||||
} else if (window.innerWidth >= 768) {
|
||||
translateX3 = matrix3.m41 - (scrollPosition - distanceFromTop) * 0.35;
|
||||
} else if (window.innerWidth >= 320) {
|
||||
translateX3 = matrix3.m41 - (scrollPosition - distanceFromTop) * 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
gridContainer3.style.transform = `translate3d(${translateX3}px, 0, 0)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
requestAnimationFrame(updateScrollAnimation);
|
||||
});
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
requestAnimationFrame(updateScrollAnimation);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
applyScrollAnimation();
|
||||
})();
|
||||
|
||||
//# sourceMappingURL=scroll-gallery.js.map
|
||||
|
||||
793
js/smooth-scroll.js
Normal file
793
js/smooth-scroll.js
Normal file
@@ -0,0 +1,793 @@
|
||||
//
|
||||
// SmoothScroll for websites v1.4.10 (Balazs Galambosi)
|
||||
// http://www.smoothscroll.net/
|
||||
//
|
||||
// Licensed under the terms of the MIT license.
|
||||
//
|
||||
// You may use it in your theme if you credit me.
|
||||
// It is also free to use on any individual website.
|
||||
//
|
||||
// Exception:
|
||||
// The only restriction is to not publish any
|
||||
// extension for browsers or native application
|
||||
// without getting a written permission first.
|
||||
//
|
||||
|
||||
(function () {
|
||||
|
||||
// Scroll Variables (tweakable)
|
||||
var defaultOptions = {
|
||||
|
||||
// Scrolling Core
|
||||
frameRate : 150, // [Hz]
|
||||
animationTime : 400, // [ms]
|
||||
stepSize : 100, // [px]
|
||||
|
||||
// Pulse (less tweakable)
|
||||
// ratio of "tail" to "acceleration"
|
||||
pulseAlgorithm : true,
|
||||
pulseScale : 4,
|
||||
pulseNormalize : 1,
|
||||
|
||||
// Acceleration
|
||||
accelerationDelta : 50, // 50
|
||||
accelerationMax : 3, // 3
|
||||
|
||||
// Keyboard Settings
|
||||
keyboardSupport : true, // option
|
||||
arrowScroll : 50, // [px]
|
||||
|
||||
// Other
|
||||
fixedBackground : true,
|
||||
excluded : ''
|
||||
};
|
||||
|
||||
var options = defaultOptions;
|
||||
|
||||
|
||||
// Other Variables
|
||||
var isExcluded = false;
|
||||
var isFrame = false;
|
||||
var direction = { x: 0, y: 0 };
|
||||
var initDone = false;
|
||||
var root = document.documentElement;
|
||||
var activeElement;
|
||||
var observer;
|
||||
var refreshSize;
|
||||
var deltaBuffer = [];
|
||||
var deltaBufferTimer;
|
||||
var isMac = /^Mac/.test(navigator.platform);
|
||||
|
||||
var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32,
|
||||
pageup: 33, pagedown: 34, end: 35, home: 36 };
|
||||
var arrowKeys = { 37: 1, 38: 1, 39: 1, 40: 1 };
|
||||
|
||||
/***********************************************
|
||||
* INITIALIZE
|
||||
***********************************************/
|
||||
|
||||
/**
|
||||
* Tests if smooth scrolling is allowed. Shuts down everything if not.
|
||||
*/
|
||||
function initTest() {
|
||||
if (options.keyboardSupport) {
|
||||
addEvent('keydown', keydown);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up scrolls array, determines if frames are involved.
|
||||
*/
|
||||
function init() {
|
||||
|
||||
if (initDone || !document.body) return;
|
||||
|
||||
initDone = true;
|
||||
|
||||
var body = document.body;
|
||||
var html = document.documentElement;
|
||||
var windowHeight = window.innerHeight;
|
||||
var scrollHeight = body.scrollHeight;
|
||||
|
||||
// check compat mode for root element
|
||||
root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
|
||||
activeElement = body;
|
||||
|
||||
initTest();
|
||||
|
||||
// Checks if this script is running in a frame
|
||||
if (top != self) {
|
||||
isFrame = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safari 10 fixed it, Chrome fixed it in v45:
|
||||
* This fixes a bug where the areas left and right to
|
||||
* the content does not trigger the onmousewheel event
|
||||
* on some pages. e.g.: html, body { height: 100% }
|
||||
*/
|
||||
else if (isOldSafari &&
|
||||
scrollHeight > windowHeight &&
|
||||
(body.offsetHeight <= windowHeight ||
|
||||
html.offsetHeight <= windowHeight)) {
|
||||
|
||||
var fullPageElem = document.createElement('div');
|
||||
fullPageElem.style.cssText = 'position:absolute; z-index:-10000; ' +
|
||||
'top:0; left:0; right:0; height:' +
|
||||
root.scrollHeight + 'px';
|
||||
document.body.appendChild(fullPageElem);
|
||||
|
||||
// DOM changed (throttled) to fix height
|
||||
var pendingRefresh;
|
||||
refreshSize = function () {
|
||||
if (pendingRefresh) return; // could also be: clearTimeout(pendingRefresh);
|
||||
pendingRefresh = setTimeout(function () {
|
||||
if (isExcluded) return; // could be running after cleanup
|
||||
fullPageElem.style.height = '0';
|
||||
fullPageElem.style.height = root.scrollHeight + 'px';
|
||||
pendingRefresh = null;
|
||||
}, 500); // act rarely to stay fast
|
||||
};
|
||||
|
||||
setTimeout(refreshSize, 10);
|
||||
|
||||
addEvent('resize', refreshSize);
|
||||
|
||||
// TODO: attributeFilter?
|
||||
var config = {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
characterData: false
|
||||
// subtree: true
|
||||
};
|
||||
|
||||
observer = new MutationObserver(refreshSize);
|
||||
observer.observe(body, config);
|
||||
|
||||
if (root.offsetHeight <= windowHeight) {
|
||||
var clearfix = document.createElement('div');
|
||||
clearfix.style.clear = 'both';
|
||||
body.appendChild(clearfix);
|
||||
}
|
||||
}
|
||||
|
||||
// disable fixed background
|
||||
if (!options.fixedBackground && !isExcluded) {
|
||||
body.style.backgroundAttachment = 'scroll';
|
||||
html.style.backgroundAttachment = 'scroll';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes event listeners and other traces left on the page.
|
||||
*/
|
||||
function cleanup() {
|
||||
observer && observer.disconnect();
|
||||
removeEvent(wheelEvent, wheel);
|
||||
removeEvent('mousedown', mousedown);
|
||||
removeEvent('keydown', keydown);
|
||||
removeEvent('resize', refreshSize);
|
||||
removeEvent('load', init);
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
* SCROLLING
|
||||
************************************************/
|
||||
|
||||
var que = [];
|
||||
var pending = false;
|
||||
var lastScroll = Date.now();
|
||||
|
||||
/**
|
||||
* Pushes scroll actions to the scrolling queue.
|
||||
*/
|
||||
function scrollArray(elem, left, top) {
|
||||
|
||||
directionCheck(left, top);
|
||||
|
||||
if (options.accelerationMax != 1) {
|
||||
var now = Date.now();
|
||||
var elapsed = now - lastScroll;
|
||||
if (elapsed < options.accelerationDelta) {
|
||||
var factor = (1 + (50 / elapsed)) / 2;
|
||||
if (factor > 1) {
|
||||
factor = Math.min(factor, options.accelerationMax);
|
||||
left *= factor;
|
||||
top *= factor;
|
||||
}
|
||||
}
|
||||
lastScroll = Date.now();
|
||||
}
|
||||
|
||||
// push a scroll command
|
||||
que.push({
|
||||
x: left,
|
||||
y: top,
|
||||
lastX: (left < 0) ? 0.99 : -0.99,
|
||||
lastY: (top < 0) ? 0.99 : -0.99,
|
||||
start: Date.now()
|
||||
});
|
||||
|
||||
// don't act if there's a pending queue
|
||||
if (pending) {
|
||||
return;
|
||||
}
|
||||
|
||||
var scrollRoot = getScrollRoot();
|
||||
var isWindowScroll = (elem === scrollRoot || elem === document.body);
|
||||
|
||||
// if we haven't already fixed the behavior,
|
||||
// and it needs fixing for this sesh
|
||||
if (elem.$scrollBehavior == null && isScrollBehaviorSmooth(elem)) {
|
||||
elem.$scrollBehavior = elem.style.scrollBehavior;
|
||||
elem.style.scrollBehavior = 'auto';
|
||||
}
|
||||
|
||||
var step = function (time) {
|
||||
|
||||
var now = Date.now();
|
||||
var scrollX = 0;
|
||||
var scrollY = 0;
|
||||
|
||||
for (var i = 0; i < que.length; i++) {
|
||||
|
||||
var item = que[i];
|
||||
var elapsed = now - item.start;
|
||||
var finished = (elapsed >= options.animationTime);
|
||||
|
||||
// scroll position: [0, 1]
|
||||
var position = (finished) ? 1 : elapsed / options.animationTime;
|
||||
|
||||
// easing [optional]
|
||||
if (options.pulseAlgorithm) {
|
||||
position = pulse(position);
|
||||
}
|
||||
|
||||
// only need the difference
|
||||
var x = (item.x * position - item.lastX) >> 0;
|
||||
var y = (item.y * position - item.lastY) >> 0;
|
||||
|
||||
// add this to the total scrolling
|
||||
scrollX += x;
|
||||
scrollY += y;
|
||||
|
||||
// update last values
|
||||
item.lastX += x;
|
||||
item.lastY += y;
|
||||
|
||||
// delete and step back if it's over
|
||||
if (finished) {
|
||||
que.splice(i, 1); i--;
|
||||
}
|
||||
}
|
||||
|
||||
// scroll left and top
|
||||
if (isWindowScroll) {
|
||||
window.scrollBy(scrollX, scrollY);
|
||||
}
|
||||
else {
|
||||
if (scrollX) elem.scrollLeft += scrollX;
|
||||
if (scrollY) elem.scrollTop += scrollY;
|
||||
}
|
||||
|
||||
// clean up if there's nothing left to do
|
||||
if (!left && !top) {
|
||||
que = [];
|
||||
}
|
||||
|
||||
if (que.length) {
|
||||
requestFrame(step, elem, (1000 / options.frameRate + 1));
|
||||
} else {
|
||||
pending = false;
|
||||
// restore default behavior at the end of scrolling sesh
|
||||
if (elem.$scrollBehavior != null) {
|
||||
elem.style.scrollBehavior = elem.$scrollBehavior;
|
||||
elem.$scrollBehavior = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// start a new queue of actions
|
||||
requestFrame(step, elem, 0);
|
||||
pending = true;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************
|
||||
* EVENTS
|
||||
***********************************************/
|
||||
|
||||
/**
|
||||
* Mouse wheel handler.
|
||||
* @param {Object} event
|
||||
*/
|
||||
function wheel(event) {
|
||||
|
||||
if (!initDone) {
|
||||
init();
|
||||
}
|
||||
|
||||
var target = event.target;
|
||||
|
||||
// leave early if default action is prevented
|
||||
// or it's a zooming event with CTRL
|
||||
if (event.defaultPrevented || event.ctrlKey) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// leave embedded content alone (flash & pdf)
|
||||
if (isNodeName(activeElement, 'embed') ||
|
||||
(isNodeName(target, 'embed') && /\.pdf/i.test(target.src)) ||
|
||||
isNodeName(activeElement, 'object') ||
|
||||
target.shadowRoot) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var deltaX = -event.wheelDeltaX || event.deltaX || 0;
|
||||
var deltaY = -event.wheelDeltaY || event.deltaY || 0;
|
||||
|
||||
if (isMac) {
|
||||
if (event.wheelDeltaX && isDivisible(event.wheelDeltaX, 120)) {
|
||||
deltaX = -120 * (event.wheelDeltaX / Math.abs(event.wheelDeltaX));
|
||||
}
|
||||
if (event.wheelDeltaY && isDivisible(event.wheelDeltaY, 120)) {
|
||||
deltaY = -120 * (event.wheelDeltaY / Math.abs(event.wheelDeltaY));
|
||||
}
|
||||
}
|
||||
|
||||
// use wheelDelta if deltaX/Y is not available
|
||||
if (!deltaX && !deltaY) {
|
||||
deltaY = -event.wheelDelta || 0;
|
||||
}
|
||||
|
||||
// line based scrolling (Firefox mostly)
|
||||
if (event.deltaMode === 1) {
|
||||
deltaX *= 40;
|
||||
deltaY *= 40;
|
||||
}
|
||||
|
||||
var overflowing = overflowingAncestor(target);
|
||||
|
||||
// nothing to do if there's no element that's scrollable
|
||||
if (!overflowing) {
|
||||
// except Chrome iframes seem to eat wheel events, which we need to
|
||||
// propagate up, if the iframe has nothing overflowing to scroll
|
||||
if (isFrame && isChrome) {
|
||||
// change target to iframe element itself for the parent frame
|
||||
Object.defineProperty(event, "target", {value: window.frameElement});
|
||||
return parent.wheel(event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if it's a touchpad scroll that should be ignored
|
||||
if (isTouchpad(deltaY)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// scale by step size
|
||||
// delta is 120 most of the time
|
||||
// synaptics seems to send 1 sometimes
|
||||
if (Math.abs(deltaX) > 1.2) {
|
||||
deltaX *= options.stepSize / 120;
|
||||
}
|
||||
if (Math.abs(deltaY) > 1.2) {
|
||||
deltaY *= options.stepSize / 120;
|
||||
}
|
||||
|
||||
scrollArray(overflowing, deltaX, deltaY);
|
||||
event.preventDefault();
|
||||
scheduleClearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keydown event handler.
|
||||
* @param {Object} event
|
||||
*/
|
||||
function keydown(event) {
|
||||
|
||||
var target = event.target;
|
||||
var modifier = event.ctrlKey || event.altKey || event.metaKey ||
|
||||
(event.shiftKey && event.keyCode !== key.spacebar);
|
||||
|
||||
// our own tracked active element could've been removed from the DOM
|
||||
if (!document.body.contains(activeElement)) {
|
||||
activeElement = document.activeElement;
|
||||
}
|
||||
|
||||
// do nothing if user is editing text
|
||||
// or using a modifier key (except shift)
|
||||
// or in a dropdown
|
||||
// or inside interactive elements
|
||||
var inputNodeNames = /^(textarea|select|embed|object)$/i;
|
||||
var buttonTypes = /^(button|submit|radio|checkbox|file|color|image)$/i;
|
||||
if ( event.defaultPrevented ||
|
||||
inputNodeNames.test(target.nodeName) ||
|
||||
isNodeName(target, 'input') && !buttonTypes.test(target.type) ||
|
||||
isNodeName(activeElement, 'video') ||
|
||||
isInsideYoutubeVideo(event) ||
|
||||
target.isContentEditable ||
|
||||
modifier ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// [spacebar] should trigger button press, leave it alone
|
||||
if ((isNodeName(target, 'button') ||
|
||||
isNodeName(target, 'input') && buttonTypes.test(target.type)) &&
|
||||
event.keyCode === key.spacebar) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// [arrwow keys] on radio buttons should be left alone
|
||||
if (isNodeName(target, 'input') && target.type == 'radio' &&
|
||||
arrowKeys[event.keyCode]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var shift, x = 0, y = 0;
|
||||
var overflowing = overflowingAncestor(activeElement);
|
||||
|
||||
if (!overflowing) {
|
||||
// Chrome iframes seem to eat key events, which we need to
|
||||
// propagate up, if the iframe has nothing overflowing to scroll
|
||||
return (isFrame && isChrome) ? parent.keydown(event) : true;
|
||||
}
|
||||
|
||||
var clientHeight = overflowing.clientHeight;
|
||||
|
||||
if (overflowing == document.body) {
|
||||
clientHeight = window.innerHeight;
|
||||
}
|
||||
|
||||
switch (event.keyCode) {
|
||||
case key.up:
|
||||
y = -options.arrowScroll;
|
||||
break;
|
||||
case key.down:
|
||||
y = options.arrowScroll;
|
||||
break;
|
||||
case key.spacebar: // (+ shift)
|
||||
shift = event.shiftKey ? 1 : -1;
|
||||
y = -shift * clientHeight * 0.9;
|
||||
break;
|
||||
case key.pageup:
|
||||
y = -clientHeight * 0.9;
|
||||
break;
|
||||
case key.pagedown:
|
||||
y = clientHeight * 0.9;
|
||||
break;
|
||||
case key.home:
|
||||
if (overflowing == document.body && document.scrollingElement)
|
||||
overflowing = document.scrollingElement;
|
||||
y = -overflowing.scrollTop;
|
||||
break;
|
||||
case key.end:
|
||||
var scroll = overflowing.scrollHeight - overflowing.scrollTop;
|
||||
var scrollRemaining = scroll - clientHeight;
|
||||
y = (scrollRemaining > 0) ? scrollRemaining + 10 : 0;
|
||||
break;
|
||||
case key.left:
|
||||
x = -options.arrowScroll;
|
||||
break;
|
||||
case key.right:
|
||||
x = options.arrowScroll;
|
||||
break;
|
||||
default:
|
||||
return true; // a key we don't care about
|
||||
}
|
||||
|
||||
scrollArray(overflowing, x, y);
|
||||
event.preventDefault();
|
||||
scheduleClearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mousedown event only for updating activeElement
|
||||
*/
|
||||
function mousedown(event) {
|
||||
activeElement = event.target;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************
|
||||
* OVERFLOW
|
||||
***********************************************/
|
||||
|
||||
var uniqueID = (function () {
|
||||
var i = 0;
|
||||
return function (el) {
|
||||
return el.uniqueID || (el.uniqueID = i++);
|
||||
};
|
||||
})();
|
||||
|
||||
var cacheX = {}; // cleared out after a scrolling session
|
||||
var cacheY = {}; // cleared out after a scrolling session
|
||||
var clearCacheTimer;
|
||||
var smoothBehaviorForElement = {};
|
||||
|
||||
//setInterval(function () { cache = {}; }, 10 * 1000);
|
||||
|
||||
function scheduleClearCache() {
|
||||
clearTimeout(clearCacheTimer);
|
||||
clearCacheTimer = setInterval(function () {
|
||||
cacheX = cacheY = smoothBehaviorForElement = {};
|
||||
}, 1*1000);
|
||||
}
|
||||
|
||||
function setCache(elems, overflowing, x) {
|
||||
var cache = x ? cacheX : cacheY;
|
||||
for (var i = elems.length; i--;)
|
||||
cache[uniqueID(elems[i])] = overflowing;
|
||||
return overflowing;
|
||||
}
|
||||
|
||||
function getCache(el, x) {
|
||||
return (x ? cacheX : cacheY)[uniqueID(el)];
|
||||
}
|
||||
|
||||
// (body) (root)
|
||||
// | hidden | visible | scroll | auto |
|
||||
// hidden | no | no | YES | YES |
|
||||
// visible | no | YES | YES | YES |
|
||||
// scroll | no | YES | YES | YES |
|
||||
// auto | no | YES | YES | YES |
|
||||
|
||||
function overflowingAncestor(el) {
|
||||
var elems = [];
|
||||
var body = document.body;
|
||||
var rootScrollHeight = root.scrollHeight;
|
||||
do {
|
||||
var cached = getCache(el, false);
|
||||
if (cached) {
|
||||
return setCache(elems, cached);
|
||||
}
|
||||
elems.push(el);
|
||||
if (rootScrollHeight === el.scrollHeight) {
|
||||
var topOverflowsNotHidden = overflowNotHidden(root) && overflowNotHidden(body);
|
||||
var isOverflowCSS = topOverflowsNotHidden || overflowAutoOrScroll(root);
|
||||
if (isFrame && isContentOverflowing(root) ||
|
||||
!isFrame && isOverflowCSS) {
|
||||
return setCache(elems, getScrollRoot());
|
||||
}
|
||||
} else if (isContentOverflowing(el) && overflowAutoOrScroll(el)) {
|
||||
return setCache(elems, el);
|
||||
}
|
||||
} while ((el = el.parentElement));
|
||||
}
|
||||
|
||||
function isContentOverflowing(el) {
|
||||
return (el.clientHeight + 10 < el.scrollHeight);
|
||||
}
|
||||
|
||||
// typically for <body> and <html>
|
||||
function overflowNotHidden(el) {
|
||||
var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
|
||||
return (overflow !== 'hidden');
|
||||
}
|
||||
|
||||
// for all other elements
|
||||
function overflowAutoOrScroll(el) {
|
||||
var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
|
||||
return (overflow === 'scroll' || overflow === 'auto');
|
||||
}
|
||||
|
||||
// for all other elements
|
||||
function isScrollBehaviorSmooth(el) {
|
||||
var id = uniqueID(el);
|
||||
if (smoothBehaviorForElement[id] == null) {
|
||||
var scrollBehavior = getComputedStyle(el, '')['scroll-behavior'];
|
||||
smoothBehaviorForElement[id] = ('smooth' == scrollBehavior);
|
||||
}
|
||||
return smoothBehaviorForElement[id];
|
||||
}
|
||||
|
||||
|
||||
/***********************************************
|
||||
* HELPERS
|
||||
***********************************************/
|
||||
|
||||
function addEvent(type, fn, arg) {
|
||||
window.addEventListener(type, fn, arg || false);
|
||||
}
|
||||
|
||||
function removeEvent(type, fn, arg) {
|
||||
window.removeEventListener(type, fn, arg || false);
|
||||
}
|
||||
|
||||
function isNodeName(el, tag) {
|
||||
return el && (el.nodeName||'').toLowerCase() === tag.toLowerCase();
|
||||
}
|
||||
|
||||
function directionCheck(x, y) {
|
||||
x = (x > 0) ? 1 : -1;
|
||||
y = (y > 0) ? 1 : -1;
|
||||
if (direction.x !== x || direction.y !== y) {
|
||||
direction.x = x;
|
||||
direction.y = y;
|
||||
que = [];
|
||||
lastScroll = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (window.localStorage && localStorage.SS_deltaBuffer) {
|
||||
try { // #46 Safari throws in private browsing for localStorage
|
||||
deltaBuffer = localStorage.SS_deltaBuffer.split(',');
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
function isTouchpad(deltaY) {
|
||||
if (!deltaY) return;
|
||||
if (!deltaBuffer.length) {
|
||||
deltaBuffer = [deltaY, deltaY, deltaY];
|
||||
}
|
||||
deltaY = Math.abs(deltaY);
|
||||
deltaBuffer.push(deltaY);
|
||||
deltaBuffer.shift();
|
||||
clearTimeout(deltaBufferTimer);
|
||||
deltaBufferTimer = setTimeout(function () {
|
||||
try { // #46 Safari throws in private browsing for localStorage
|
||||
localStorage.SS_deltaBuffer = deltaBuffer.join(',');
|
||||
} catch (e) { }
|
||||
}, 1000);
|
||||
var dpiScaledWheelDelta = deltaY > 120 && allDeltasDivisableBy(deltaY); // win64
|
||||
var tp = !allDeltasDivisableBy(120) && !allDeltasDivisableBy(100) && !dpiScaledWheelDelta;
|
||||
if (deltaY < 50) return true;
|
||||
return tp;
|
||||
}
|
||||
|
||||
function isDivisible(n, divisor) {
|
||||
return (Math.floor(n / divisor) == n / divisor);
|
||||
}
|
||||
|
||||
function allDeltasDivisableBy(divisor) {
|
||||
return (isDivisible(deltaBuffer[0], divisor) &&
|
||||
isDivisible(deltaBuffer[1], divisor) &&
|
||||
isDivisible(deltaBuffer[2], divisor));
|
||||
}
|
||||
|
||||
function isInsideYoutubeVideo(event) {
|
||||
var elem = event.target;
|
||||
var isControl = false;
|
||||
if (document.URL.indexOf ('www.youtube.com/watch') != -1) {
|
||||
do {
|
||||
isControl = (elem.classList &&
|
||||
elem.classList.contains('html5-video-controls'));
|
||||
if (isControl) break;
|
||||
} while ((elem = elem.parentNode));
|
||||
}
|
||||
return isControl;
|
||||
}
|
||||
|
||||
var requestFrame = (function () {
|
||||
return (window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
function (callback, element, delay) {
|
||||
window.setTimeout(callback, delay || (1000/60));
|
||||
});
|
||||
})();
|
||||
|
||||
var MutationObserver = (window.MutationObserver ||
|
||||
window.WebKitMutationObserver ||
|
||||
window.MozMutationObserver);
|
||||
|
||||
var getScrollRoot = (function() {
|
||||
var SCROLL_ROOT = document.scrollingElement;
|
||||
return function() {
|
||||
if (!SCROLL_ROOT) {
|
||||
var dummy = document.createElement('div');
|
||||
dummy.style.cssText = 'height:10000px;width:1px;';
|
||||
document.body.appendChild(dummy);
|
||||
var bodyScrollTop = document.body.scrollTop;
|
||||
var docElScrollTop = document.documentElement.scrollTop;
|
||||
window.scrollBy(0, 3);
|
||||
if (document.body.scrollTop != bodyScrollTop)
|
||||
(SCROLL_ROOT = document.body);
|
||||
else
|
||||
(SCROLL_ROOT = document.documentElement);
|
||||
window.scrollBy(0, -3);
|
||||
document.body.removeChild(dummy);
|
||||
}
|
||||
return SCROLL_ROOT;
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
/***********************************************
|
||||
* PULSE (by Michael Herf)
|
||||
***********************************************/
|
||||
|
||||
/**
|
||||
* Viscous fluid with a pulse for part and decay for the rest.
|
||||
* - Applies a fixed force over an interval (a damped acceleration), and
|
||||
* - Lets the exponential bleed away the velocity over a longer interval
|
||||
* - Michael Herf, http://stereopsis.com/stopping/
|
||||
*/
|
||||
function pulse_(x) {
|
||||
var val, start, expx;
|
||||
// test
|
||||
x = x * options.pulseScale;
|
||||
if (x < 1) { // acceleartion
|
||||
val = x - (1 - Math.exp(-x));
|
||||
} else { // tail
|
||||
// the previous animation ended here:
|
||||
start = Math.exp(-1);
|
||||
// simple viscous drag
|
||||
x -= 1;
|
||||
expx = 1 - Math.exp(-x);
|
||||
val = start + (expx * (1 - start));
|
||||
}
|
||||
return val * options.pulseNormalize;
|
||||
}
|
||||
|
||||
function pulse(x) {
|
||||
if (x >= 1) return 1;
|
||||
if (x <= 0) return 0;
|
||||
|
||||
if (options.pulseNormalize == 1) {
|
||||
options.pulseNormalize /= pulse_(1);
|
||||
}
|
||||
return pulse_(x);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************
|
||||
* FIRST RUN
|
||||
***********************************************/
|
||||
|
||||
var userAgent = window.navigator.userAgent;
|
||||
var isEdge = /Edge/.test(userAgent); // thank you MS
|
||||
var isChrome = /chrome/i.test(userAgent) && !isEdge;
|
||||
var isSafari = /safari/i.test(userAgent) && !isEdge;
|
||||
var isMobile = /mobile/i.test(userAgent);
|
||||
var isIEWin7 = /Windows NT 6.1/i.test(userAgent) && /rv:11/i.test(userAgent);
|
||||
var isOldSafari = isSafari && (/Version\/8/i.test(userAgent) || /Version\/9/i.test(userAgent));
|
||||
var isEnabledForBrowser = (isChrome || isSafari || isIEWin7) && !isMobile;
|
||||
|
||||
var supportsPassive = false;
|
||||
try {
|
||||
window.addEventListener("test", null, Object.defineProperty({}, 'passive', {
|
||||
get: function () {
|
||||
supportsPassive = true;
|
||||
}
|
||||
}));
|
||||
} catch(e) {}
|
||||
|
||||
var wheelOpt = supportsPassive ? { passive: false } : false;
|
||||
var wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel' : 'mousewheel';
|
||||
|
||||
if (wheelEvent && isEnabledForBrowser) {
|
||||
addEvent(wheelEvent, wheel, wheelOpt);
|
||||
addEvent('mousedown', mousedown);
|
||||
addEvent('load', init);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************
|
||||
* PUBLIC INTERFACE
|
||||
***********************************************/
|
||||
|
||||
function SmoothScroll(optionsToSet) {
|
||||
for (var key in optionsToSet)
|
||||
if (defaultOptions.hasOwnProperty(key))
|
||||
options[key] = optionsToSet[key];
|
||||
}
|
||||
SmoothScroll.destroy = cleanup;
|
||||
|
||||
if (window.SmoothScrollOptions) // async API
|
||||
SmoothScroll(window.SmoothScrollOptions);
|
||||
|
||||
if (typeof define === 'function' && define.amd)
|
||||
define(function() {
|
||||
return SmoothScroll;
|
||||
});
|
||||
else if ('object' == typeof exports)
|
||||
module.exports = SmoothScroll;
|
||||
else
|
||||
window.SmoothScroll = SmoothScroll;
|
||||
|
||||
})();
|
||||
|
||||
//# sourceMappingURL=smooth-scroll.js.map
|
||||
|
||||
738
js/yt-player.js
Normal file
738
js/yt-player.js
Normal file
@@ -0,0 +1,738 @@
|
||||
/*! yt-player. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
var EventEmitter = function () {
|
||||
this.events = {};
|
||||
};
|
||||
|
||||
EventEmitter.prototype.on = function (event, listener) {
|
||||
if (typeof this.events[event] !== 'object') {
|
||||
this.events[event] = [];
|
||||
}
|
||||
|
||||
this.events[event].push(listener);
|
||||
};
|
||||
|
||||
EventEmitter.prototype.removeListener = function (event, listener) {
|
||||
var idx;
|
||||
|
||||
if (typeof this.events[event] === 'object') {
|
||||
idx = this.indexOf(this.events[event], listener);
|
||||
|
||||
if (idx > -1) {
|
||||
this.events[event].splice(idx, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EventEmitter.prototype.emit = function (event) {
|
||||
var i, listeners, length, args = [].slice.call(arguments, 1);
|
||||
|
||||
if (typeof this.events[event] === 'object') {
|
||||
listeners = this.events[event].slice();
|
||||
length = listeners.length;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
listeners[i].apply(this, args);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EventEmitter.prototype.once = function (event, listener) {
|
||||
this.on(event, function g () {
|
||||
this.removeListener(event, g);
|
||||
listener.apply(this, arguments);
|
||||
});
|
||||
};
|
||||
|
||||
var loadScript = function (src, attrs, parentNode) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var script = document.createElement('script')
|
||||
script.async = true
|
||||
script.src = src
|
||||
|
||||
for (var [k, v] of Object.entries(attrs || {})) {
|
||||
script.setAttribute(k, v)
|
||||
}
|
||||
|
||||
script.onload = () => {
|
||||
script.onerror = script.onload = null
|
||||
resolve(script)
|
||||
}
|
||||
|
||||
script.onerror = () => {
|
||||
script.onerror = script.onload = null
|
||||
reject(new Error(`Failed to load ${src}`))
|
||||
}
|
||||
|
||||
var node = parentNode || document.head || document.getElementsByTagName('head')[0]
|
||||
node.appendChild(script)
|
||||
})
|
||||
}
|
||||
|
||||
var YOUTUBE_IFRAME_API_SRC = 'https://www.youtube.com/iframe_api'
|
||||
|
||||
var YOUTUBE_STATES = {
|
||||
'-1': 'unstarted',
|
||||
0: 'ended',
|
||||
1: 'playing',
|
||||
2: 'paused',
|
||||
3: 'buffering',
|
||||
5: 'cued'
|
||||
}
|
||||
|
||||
var YOUTUBE_ERROR = {
|
||||
// The request contains an invalid parameter value. For example, this error
|
||||
// occurs if you specify a videoId that does not have 11 characters, or if the
|
||||
// videoId contains invalid characters, such as exclamation points or asterisks.
|
||||
INVALID_PARAM: 2,
|
||||
|
||||
// The requested content cannot be played in an HTML5 player or another error
|
||||
// related to the HTML5 player has occurred.
|
||||
HTML5_ERROR: 5,
|
||||
|
||||
// The video requested was not found. This error occurs when a video has been
|
||||
// removed (for any reason) or has been marked as private.
|
||||
NOT_FOUND: 100,
|
||||
|
||||
// The owner of the requested video does not allow it to be played in embedded
|
||||
// players.
|
||||
UNPLAYABLE_1: 101,
|
||||
|
||||
// This error is the same as 101. It's just a 101 error in disguise!
|
||||
UNPLAYABLE_2: 150
|
||||
}
|
||||
|
||||
var loadIframeAPICallbacks = []
|
||||
|
||||
/**
|
||||
* YouTube Player. Exposes a better API, with nicer events.
|
||||
* @param {HTMLElement|selector} element
|
||||
*/
|
||||
YouTubePlayer = class YouTubePlayer extends EventEmitter {
|
||||
constructor (element, opts) {
|
||||
super()
|
||||
|
||||
var elem = typeof element === 'string'
|
||||
? document.querySelector(element)
|
||||
: element
|
||||
|
||||
if (elem.id) {
|
||||
this._id = elem.id // use existing element id
|
||||
} else {
|
||||
this._id = elem.id = 'ytplayer-' + Math.random().toString(16).slice(2, 8)
|
||||
}
|
||||
|
||||
this._opts = Object.assign({
|
||||
width: 640,
|
||||
height: 360,
|
||||
autoplay: false,
|
||||
captions: undefined,
|
||||
controls: true,
|
||||
keyboard: true,
|
||||
fullscreen: true,
|
||||
annotations: true,
|
||||
modestBranding: false,
|
||||
related: true,
|
||||
timeupdateFrequency: 1000,
|
||||
playsInline: true,
|
||||
start: 0
|
||||
}, opts)
|
||||
|
||||
this.videoId = null
|
||||
this.destroyed = false
|
||||
|
||||
this._api = null
|
||||
this._autoplay = false // autoplay the first video?
|
||||
this._player = null
|
||||
this._ready = false // is player ready?
|
||||
this._queue = []
|
||||
this.replayInterval = []
|
||||
|
||||
this._interval = null
|
||||
|
||||
// Setup listeners for 'timeupdate' events. The YouTube Player does not fire
|
||||
// 'timeupdate' events, so they are simulated using a setInterval().
|
||||
this._startInterval = this._startInterval.bind(this)
|
||||
this._stopInterval = this._stopInterval.bind(this)
|
||||
|
||||
this.on('playing', this._startInterval)
|
||||
this.on('unstarted', this._stopInterval)
|
||||
this.on('ended', this._stopInterval)
|
||||
this.on('paused', this._stopInterval)
|
||||
this.on('buffering', this._stopInterval)
|
||||
|
||||
this._loadIframeAPI((err, api) => {
|
||||
if (err) return this._destroy(new Error('YouTube Iframe API failed to load'))
|
||||
this._api = api
|
||||
|
||||
// If load(videoId, [autoplay, [size]]) was called before Iframe API
|
||||
// loaded, ensure it gets called again now
|
||||
if (this.videoId) this.load(this.videoId, this._autoplay, this._start)
|
||||
})
|
||||
}
|
||||
|
||||
indexOf (haystack, needle) {
|
||||
var i = 0, length = haystack.length, idx = -1, found = false;
|
||||
|
||||
while (i < length && !found) {
|
||||
if (haystack[i] === needle) {
|
||||
idx = i;
|
||||
found = true;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
load (videoId, autoplay = false, start = 0) {
|
||||
if (this.destroyed) return
|
||||
|
||||
this._startOptimizeDisplayEvent()
|
||||
this._optimizeDisplayHandler('center, center')
|
||||
|
||||
this.videoId = videoId
|
||||
this._autoplay = autoplay
|
||||
this._start = start
|
||||
|
||||
// If the Iframe API is not ready yet, do nothing. Once the Iframe API is
|
||||
// ready, `load(this.videoId)` will be called.
|
||||
if (!this._api) return
|
||||
|
||||
// If there is no player instance, create one.
|
||||
if (!this._player) {
|
||||
this._createPlayer(videoId)
|
||||
return
|
||||
}
|
||||
|
||||
// If the player instance is not ready yet, do nothing. Once the player
|
||||
// instance is ready, `load(this.videoId)` will be called. This ensures that
|
||||
// the last call to `load()` is the one that takes effect.
|
||||
if (!this._ready) return
|
||||
|
||||
// If the player instance is ready, load the given `videoId`.
|
||||
if (autoplay) {
|
||||
this._player.loadVideoById(videoId, start)
|
||||
} else {
|
||||
this._player.cueVideoById(videoId, start)
|
||||
}
|
||||
}
|
||||
|
||||
play () {
|
||||
if (this._ready) this._player.playVideo()
|
||||
else this._queueCommand('play')
|
||||
}
|
||||
|
||||
replayFrom(num) {
|
||||
const find = this.replayInterval.find((obj) => {
|
||||
return obj.iframeParent === this._player.i.parentNode
|
||||
})
|
||||
if (find || !num) return
|
||||
this.replayInterval.push({
|
||||
iframeParent: this._player.i.parentNode,
|
||||
interval: setInterval(() => {
|
||||
if (this._player.getCurrentTime() >= this._player.getDuration() - Number(num)) {
|
||||
this.seek(0);
|
||||
for (const [key, val] of this.replayInterval.entries()) {
|
||||
if (Object.hasOwnProperty.call(this.replayInterval, key)) {
|
||||
clearInterval(this.replayInterval[key].interval)
|
||||
this.replayInterval.splice(key, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, Number(num) * 1000)
|
||||
})
|
||||
}
|
||||
|
||||
pause () {
|
||||
if (this._ready) this._player.pauseVideo()
|
||||
else this._queueCommand('pause')
|
||||
}
|
||||
|
||||
stop () {
|
||||
if (this._ready) this._player.stopVideo()
|
||||
else this._queueCommand('stop')
|
||||
}
|
||||
|
||||
seek (seconds) {
|
||||
if (this._ready) this._player.seekTo(seconds, true)
|
||||
else this._queueCommand('seek', seconds)
|
||||
}
|
||||
|
||||
_optimizeDisplayHandler(anchor) {
|
||||
if (!this._player) return
|
||||
const YTPlayer = this._player.i
|
||||
const YTPAlign = anchor.split(",");
|
||||
if (YTPlayer) {
|
||||
const win = {},
|
||||
el = YTPlayer.parentElement;
|
||||
|
||||
if (el) {
|
||||
const computedStyle = window.getComputedStyle(el),
|
||||
outerHeight = el.clientHeight + parseFloat(computedStyle.marginTop, 10) + parseFloat(computedStyle.marginBottom, 10) + parseFloat(computedStyle.borderTopWidth, 10) + parseFloat(computedStyle.borderBottomWidth, 10),
|
||||
outerWidth = el.clientWidth + parseFloat(computedStyle.marginLeft, 10) + parseFloat(computedStyle.marginRight, 10) + parseFloat(computedStyle.borderLeftWidth, 10) + parseFloat(computedStyle.borderRightWidth, 10),
|
||||
ratio = 1.7,
|
||||
vid = YTPlayer;
|
||||
|
||||
win.width = outerWidth;
|
||||
win.height = outerHeight + 80;
|
||||
|
||||
vid.style.width = win.width + 'px';
|
||||
vid.style.height = Math.ceil(parseFloat(vid.style.width, 10) / ratio) + 'px';
|
||||
vid.style.marginTop = Math.ceil(-((parseFloat(vid.style.height, 10) - win.height) / 2)) + 'px';
|
||||
vid.style.marginLeft = 0;
|
||||
|
||||
const lowest = parseFloat(vid.style.height, 10) < win.height;
|
||||
|
||||
if (lowest) {
|
||||
vid.style.height = win.height + 'px',
|
||||
vid.style.width = Math.ceil(parseFloat(vid.style.height, 10) * ratio) + 'px',
|
||||
vid.style.marginTop = 0,
|
||||
vid.style.marginLeft = Math.ceil(-((parseFloat(vid.style.width, 10) - win.width) / 2)) + 'px'
|
||||
}
|
||||
for (const align in YTPAlign)
|
||||
if (YTPAlign.hasOwnProperty(align)) {
|
||||
const al = YTPAlign[align].replace(/ /g, "");
|
||||
switch (al) {
|
||||
case "top":
|
||||
vid.style.marginTop = lowest ? -((parseFloat(vid.style.height, 10) - win.height) / 2) + 'px' : 0;
|
||||
break;
|
||||
case "bottom":
|
||||
vid.style.marginTop = lowest ? 0 : -(parseFloat(vid.style.height, 10) - win.height) + 'px';
|
||||
break;
|
||||
case "left":
|
||||
vid.style.marginLeft = 0;
|
||||
break;
|
||||
case "right":
|
||||
vid.style.marginLeft = lowest ? -(parseFloat(vid.style.width, 10) - win.width) : 0 + 'px';
|
||||
break;
|
||||
default:
|
||||
parseFloat(vid.style.width, 10) > win.width && (vid.style.marginLeft = -((parseFloat(vid.style.width, 10) - win.width) / 2) + 'px')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopResize () {
|
||||
window.removeEventListener('resize', this._resizeListener)
|
||||
this._resizeListener = null
|
||||
}
|
||||
|
||||
stopReplay (iframeParent) {
|
||||
for (const [key, val] of this.replayInterval.entries()) {
|
||||
if (Object.hasOwnProperty.call(this.replayInterval, key)) {
|
||||
if (iframeParent === this.replayInterval[key].iframeParent) {
|
||||
clearInterval(this.replayInterval[key].interval);
|
||||
this.replayInterval.splice(key, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setVolume (volume) {
|
||||
if (this._ready) this._player.setVolume(volume)
|
||||
else this._queueCommand('setVolume', volume)
|
||||
}
|
||||
|
||||
loadPlaylist () {
|
||||
if (this._ready) this._player.loadPlaylist(this.videoId)
|
||||
else this._queueCommand('loadPlaylist', this.videoId)
|
||||
}
|
||||
|
||||
setLoop (bool) {
|
||||
if (this._ready) this._player.setLoop(bool)
|
||||
else this._queueCommand('setLoop', bool)
|
||||
}
|
||||
|
||||
getVolume () {
|
||||
return (this._ready && this._player.getVolume()) || 0
|
||||
}
|
||||
|
||||
mute () {
|
||||
if (this._ready) this._player.mute()
|
||||
else this._queueCommand('mute')
|
||||
}
|
||||
|
||||
unMute () {
|
||||
if (this._ready) this._player.unMute()
|
||||
else this._queueCommand('unMute')
|
||||
}
|
||||
|
||||
isMuted () {
|
||||
return (this._ready && this._player.isMuted()) || false
|
||||
}
|
||||
|
||||
setSize (width, height) {
|
||||
if (this._ready) this._player.setSize(width, height)
|
||||
else this._queueCommand('setSize', width, height)
|
||||
}
|
||||
|
||||
setPlaybackRate (rate) {
|
||||
if (this._ready) this._player.setPlaybackRate(rate)
|
||||
else this._queueCommand('setPlaybackRate', rate)
|
||||
}
|
||||
|
||||
setPlaybackQuality (suggestedQuality) {
|
||||
if (this._ready) this._player.setPlaybackQuality(suggestedQuality)
|
||||
else this._queueCommand('setPlaybackQuality', suggestedQuality)
|
||||
}
|
||||
|
||||
getPlaybackRate () {
|
||||
return (this._ready && this._player.getPlaybackRate()) || 1
|
||||
}
|
||||
|
||||
getAvailablePlaybackRates () {
|
||||
return (this._ready && this._player.getAvailablePlaybackRates()) || [1]
|
||||
}
|
||||
|
||||
getDuration () {
|
||||
return (this._ready && this._player.getDuration()) || 0
|
||||
}
|
||||
|
||||
getProgress () {
|
||||
return (this._ready && this._player.getVideoLoadedFraction()) || 0
|
||||
}
|
||||
|
||||
getState () {
|
||||
return (this._ready && YOUTUBE_STATES[this._player.getPlayerState()]) || 'unstarted'
|
||||
}
|
||||
|
||||
getCurrentTime () {
|
||||
return (this._ready && this._player.getCurrentTime()) || 0
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this._destroy()
|
||||
}
|
||||
|
||||
_destroy (err) {
|
||||
if (this.destroyed) return
|
||||
this.destroyed = true
|
||||
|
||||
if (this._player) {
|
||||
this._player.stopVideo && this._player.stopVideo()
|
||||
this._player.destroy()
|
||||
}
|
||||
|
||||
this.videoId = null
|
||||
|
||||
this._id = null
|
||||
this._opts = null
|
||||
this._api = null
|
||||
this._player = null
|
||||
this._ready = false
|
||||
this._queue = null
|
||||
|
||||
this._stopInterval()
|
||||
|
||||
this.removeListener('playing', this._startInterval)
|
||||
this.removeListener('paused', this._stopInterval)
|
||||
this.removeListener('buffering', this._stopInterval)
|
||||
this.removeListener('unstarted', this._stopInterval)
|
||||
this.removeListener('ended', this._stopInterval)
|
||||
|
||||
if (err) this.emit('error', err)
|
||||
}
|
||||
|
||||
_queueCommand (command, ...args) {
|
||||
if (this.destroyed) return
|
||||
this._queue.push([command, args])
|
||||
}
|
||||
|
||||
_flushQueue () {
|
||||
while (this._queue.length) {
|
||||
var command = this._queue.shift()
|
||||
this[command[0]].apply(this, command[1])
|
||||
}
|
||||
}
|
||||
|
||||
_loadIframeAPI (cb) {
|
||||
// If API is loaded, there is nothing else to do
|
||||
if (window.YT && typeof window.YT.Player === 'function') {
|
||||
return cb(null, window.YT)
|
||||
}
|
||||
|
||||
// Otherwise, queue callback until API is loaded
|
||||
loadIframeAPICallbacks.push(cb)
|
||||
|
||||
var scripts = Array.from(document.getElementsByTagName('script'))
|
||||
var isLoading = scripts.some(script => script.src === YOUTUBE_IFRAME_API_SRC)
|
||||
|
||||
// If API <script> tag is not present in the page, inject it. Ensures that
|
||||
// if user includes a hardcoded <script> tag in HTML for performance, another
|
||||
// one will not be added
|
||||
if (!isLoading) {
|
||||
loadScript(YOUTUBE_IFRAME_API_SRC).catch(err => {
|
||||
while (loadIframeAPICallbacks.length) {
|
||||
var loadCb = loadIframeAPICallbacks.shift()
|
||||
loadCb(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var prevOnYouTubeIframeAPIReady = window.onYouTubeIframeAPIReady
|
||||
window.onYouTubeIframeAPIReady = () => {
|
||||
if (typeof prevOnYouTubeIframeAPIReady === 'function') {
|
||||
prevOnYouTubeIframeAPIReady()
|
||||
}
|
||||
while (loadIframeAPICallbacks.length) {
|
||||
var loadCb = loadIframeAPICallbacks.shift()
|
||||
loadCb(null, window.YT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_createPlayer (videoId) {
|
||||
if (this.destroyed) return
|
||||
|
||||
var opts = this._opts
|
||||
|
||||
this._player = new this._api.Player(this._id, {
|
||||
width: opts.width,
|
||||
height: opts.height,
|
||||
videoId: videoId,
|
||||
|
||||
// (Not part of documented API) This parameter controls the hostname that
|
||||
// videos are loaded from. Set to `'https://www.youtube-nocookie.com'`
|
||||
// for enhanced privacy.
|
||||
host: opts.host,
|
||||
|
||||
playerVars: {
|
||||
// This parameter specifies whether the initial video will automatically
|
||||
// start to play when the player loads. Supported values are 0 or 1. The
|
||||
// default value is 0.
|
||||
autoplay: opts.autoplay ? 1 : 0,
|
||||
|
||||
mute: opts.mute ? 1 : 0,
|
||||
|
||||
// Setting the parameter's value to 1 causes closed captions to be shown
|
||||
// by default, even if the user has turned captions off. The default
|
||||
// behavior is based on user preference.
|
||||
// cc_load_policy: opts.captions != null
|
||||
// ? opts.captions !== false ? 1 : 0
|
||||
// : undefined, // default to not setting this option
|
||||
|
||||
// Sets the player's interface language. The parameter value is an ISO
|
||||
// 639-1 two-letter language code or a fully specified locale. For
|
||||
// example, fr and fr-ca are both valid values. Other language input
|
||||
// codes, such as IETF language tags (BCP 47) might also be handled
|
||||
// properly.
|
||||
hl: (opts.captions != null && opts.captions !== false)
|
||||
? opts.captions
|
||||
: undefined, // default to not setting this option
|
||||
|
||||
// This parameter specifies the default language that the player will
|
||||
// use to display captions. Set the parameter's value to an ISO 639-1
|
||||
// two-letter language code.
|
||||
cc_lang_pref: (opts.captions != null && opts.captions !== false)
|
||||
? opts.captions
|
||||
: undefined, // default to not setting this option
|
||||
|
||||
// This parameter indicates whether the video player controls are
|
||||
// displayed. For IFrame embeds that load a Flash player, it also defines
|
||||
// when the controls display in the player as well as when the player
|
||||
// will load. Supported values are:
|
||||
// - controls=0 – Player controls do not display in the player. For
|
||||
// IFrame embeds, the Flash player loads immediately.
|
||||
// - controls=1 – (default) Player controls display in the player. For
|
||||
// IFrame embeds, the controls display immediately and
|
||||
// the Flash player also loads immediately.
|
||||
// - controls=2 – Player controls display in the player. For IFrame
|
||||
// embeds, the controls display and the Flash player
|
||||
// loads after the user initiates the video playback.
|
||||
controls: opts.controls ? 2 : 0,
|
||||
|
||||
// Setting the parameter's value to 1 causes the player to not respond to
|
||||
// keyboard controls. The default value is 0, which means that keyboard
|
||||
// controls are enabled.
|
||||
// disablekb: opts.keyboard ? 0 : 1,
|
||||
|
||||
// Setting the parameter's value to 1 enables the player to be
|
||||
// controlled via IFrame or JavaScript Player API calls. The default
|
||||
// value is 0, which means that the player cannot be controlled using
|
||||
// those APIs.
|
||||
enablejsapi: 1,
|
||||
|
||||
// Setting this parameter to 0 prevents the fullscreen button from
|
||||
// displaying in the player. The default value is 1, which causes the
|
||||
// fullscreen button to display.
|
||||
allowfullscreen: true,
|
||||
|
||||
// Setting the parameter's value to 1 causes video annotations to be
|
||||
// shown by default, whereas setting to 3 causes video annotations to not
|
||||
// be shown by default. The default value is 1.
|
||||
iv_load_policy: opts.annotations ? 1 : 3,
|
||||
|
||||
// This parameter lets you use a YouTube player that does not show a
|
||||
// YouTube logo. Set the parameter value to 1 to prevent the YouTube logo
|
||||
// from displaying in the control bar. Note that a small YouTube text
|
||||
// label will still display in the upper-right corner of a paused video
|
||||
// when the user's mouse pointer hovers over the player.
|
||||
modestbranding: opts.modestBranding ? 1 : 0,
|
||||
|
||||
// This parameter provides an extra security measure for the IFrame API
|
||||
// and is only supported for IFrame embeds. If you are using the IFrame
|
||||
// API, which means you are setting the enablejsapi parameter value to 1,
|
||||
// you should always specify your domain as the origin parameter value.
|
||||
origin: '*',
|
||||
|
||||
// This parameter controls whether videos play inline or fullscreen in an
|
||||
// HTML5 player on iOS. Valid values are:
|
||||
// - 0: This value causes fullscreen playback. This is currently the
|
||||
// default value, though the default is subject to change.
|
||||
// - 1: This value causes inline playback for UIWebViews created with
|
||||
// the allowsInlineMediaPlayback property set to TRUE.
|
||||
// playsinline: opts.playsInline ? 1 : 0,
|
||||
|
||||
// This parameter indicates whether the player should show related
|
||||
// videos from the same channel (0) or from any channel (1) when
|
||||
// playback of the video ends. The default value is 1.
|
||||
rel: opts.related ? 1 : 0,
|
||||
|
||||
// (Not part of documented API) Allow html elements with higher z-index
|
||||
// to be shown on top of the YouTube player.
|
||||
mode: 'transparent',
|
||||
showinfo: 0,
|
||||
html5: 1,
|
||||
version: 3,
|
||||
playerapiid: 'iframe_YTP_1624972482514'
|
||||
// version=3&playerapiid=iframe_YTP_1624972482514
|
||||
// This parameter causes the player to begin playing the video at the given number
|
||||
// of seconds from the start of the video. The parameter value is a positive integer.
|
||||
// Note that similar to the seek function, the player will look for the closest
|
||||
// keyframe to the time you specify. This means that sometimes the play head may seek
|
||||
// to just before the requested time, usually no more than around two seconds.
|
||||
// start: opts.start
|
||||
},
|
||||
events: {
|
||||
onReady: () => this._onReady(videoId),
|
||||
onStateChange: (data) => this._onStateChange(data),
|
||||
onPlaybackQualityChange: (data) => this._onPlaybackQualityChange(data),
|
||||
onPlaybackRateChange: (data) => this._onPlaybackRateChange(data),
|
||||
onError: (data) => this._onError(data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* This event fires when the player has finished loading and is ready to begin
|
||||
* receiving API calls.
|
||||
*/
|
||||
_onReady (videoId) {
|
||||
if (this.destroyed) return
|
||||
|
||||
this._ready = true
|
||||
|
||||
// Once the player is ready, always call `load(videoId, [autoplay, [size]])`
|
||||
// to handle these possible cases:
|
||||
//
|
||||
// 1. `load(videoId, true)` was called before the player was ready. Ensure that
|
||||
// the selected video starts to play.
|
||||
//
|
||||
// 2. `load(videoId, false)` was called before the player was ready. Now the
|
||||
// player is ready and there's nothing to do.
|
||||
//
|
||||
// 3. `load(videoId, [autoplay])` was called multiple times before the player
|
||||
// was ready. Therefore, the player was initialized with the wrong videoId,
|
||||
// so load the latest videoId and potentially autoplay it.
|
||||
this.load(this.videoId, this._autoplay, this._start)
|
||||
|
||||
this._flushQueue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the player's state changes. We emit friendly events so the user
|
||||
* doesn't need to use YouTube's YT.PlayerState.* event constants.
|
||||
*/
|
||||
_onStateChange (data) {
|
||||
if (this.destroyed) return
|
||||
|
||||
var state = YOUTUBE_STATES[data.data]
|
||||
|
||||
if (state) {
|
||||
// Send a 'timeupdate' anytime the state changes. When the video halts for any
|
||||
// reason ('paused', 'buffering', or 'ended') no further 'timeupdate' events
|
||||
// should fire until the video unhalts.
|
||||
if (['paused', 'buffering', 'ended'].includes(state)) this._onTimeupdate()
|
||||
|
||||
this.emit(state)
|
||||
|
||||
// When the video changes ('unstarted' or 'cued') or starts ('playing') then a
|
||||
// 'timeupdate' should follow afterwards (never before!) to reset the time.
|
||||
if (['unstarted', 'playing', 'cued'].includes(state)) this._onTimeupdate()
|
||||
} else {
|
||||
throw new Error('Unrecognized state change: ' + data)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This event fires whenever the video playback quality changes. Possible
|
||||
* values are: 'small', 'medium', 'large', 'hd720', 'hd1080', 'highres'.
|
||||
*/
|
||||
_onPlaybackQualityChange (data) {
|
||||
if (this.destroyed) return
|
||||
this.emit('playbackQualityChange', data.data)
|
||||
}
|
||||
|
||||
/**
|
||||
* This event fires whenever the video playback rate changes.
|
||||
*/
|
||||
_onPlaybackRateChange (data) {
|
||||
if (this.destroyed) return
|
||||
this.emit('playbackRateChange', data.data)
|
||||
}
|
||||
|
||||
/**
|
||||
* This event fires if an error occurs in the player.
|
||||
*/
|
||||
_onError (data) {
|
||||
if (this.destroyed) return
|
||||
|
||||
var code = data.data
|
||||
|
||||
// The HTML5_ERROR error occurs when the YouTube player needs to switch from
|
||||
// HTML5 to Flash to show an ad. Ignore it.
|
||||
if (code === YOUTUBE_ERROR.HTML5_ERROR) return
|
||||
|
||||
// The remaining error types occur when the YouTube player cannot play the
|
||||
// given video. This is not a fatal error. Report it as unplayable so the user
|
||||
// has an opportunity to play another video.
|
||||
if (code === YOUTUBE_ERROR.UNPLAYABLE_1 ||
|
||||
code === YOUTUBE_ERROR.UNPLAYABLE_2 ||
|
||||
code === YOUTUBE_ERROR.NOT_FOUND ||
|
||||
code === YOUTUBE_ERROR.INVALID_PARAM) {
|
||||
return this.emit('unplayable', this.videoId)
|
||||
}
|
||||
|
||||
// Unexpected error, does not match any known type
|
||||
this._destroy(new Error('YouTube Player Error. Unknown error code: ' + code))
|
||||
}
|
||||
|
||||
_startOptimizeDisplayEvent () {
|
||||
if (this._resizeListener) return;
|
||||
this._resizeListener = () => this._optimizeDisplayHandler('center, center')
|
||||
window.addEventListener('resize', this._resizeListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* This event fires when the time indicated by the `getCurrentTime()` method
|
||||
* has been updated.
|
||||
*/
|
||||
_onTimeupdate () {
|
||||
this.emit('timeupdate', this.getCurrentTime())
|
||||
}
|
||||
|
||||
_startInterval () {
|
||||
this._interval = setInterval(() => this._onTimeupdate(), this._opts.timeupdateFrequency)
|
||||
}
|
||||
|
||||
_stopInterval () {
|
||||
clearInterval(this._interval)
|
||||
this._interval = null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//# sourceMappingURL=index.js.map
|
||||
Reference in New Issue
Block a user