jellyfin-web/dashboard-ui/components/viewcontainer-lite.js

406 lines
12 KiB
JavaScript
Raw Normal View History

2016-05-15 18:49:01 -07:00
define(['browser'], function (browser) {
2016-03-14 13:27:35 -07:00
2016-05-23 20:06:51 -07:00
var mainAnimatedPages = document.querySelector('.mainAnimatedPages');
var allPages = [];
2016-05-16 08:47:28 -07:00
var currentUrls = [];
2016-05-23 20:06:51 -07:00
var pageContainerCount = 3;
2016-05-15 18:49:01 -07:00
var selectedPageIndex = -1;
function enableAnimation() {
2016-05-20 10:15:07 -07:00
if (browser.tv) {
2016-05-15 18:49:01 -07:00
return false;
}
2016-05-20 10:15:07 -07:00
if (browser.safari) {
// Right now they don't look good. Haven't figured out why yet.
2016-05-15 18:49:01 -07:00
return false;
}
return true;
}
2016-03-14 13:27:35 -07:00
function loadView(options) {
2016-05-15 18:49:01 -07:00
if (options.cancel) {
return;
}
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
cancelActiveAnimations();
2016-03-14 13:27:35 -07:00
2016-05-23 20:06:51 -07:00
var selected = selectedPageIndex;
2016-05-15 18:49:01 -07:00
var previousAnimatable = selected == -1 ? null : allPages[selected];
var pageIndex = selected + 1;
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
if (pageIndex >= pageContainerCount) {
pageIndex = 0;
}
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
var newViewInfo = normalizeNewView(options);
var newView = newViewInfo.elem;
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
var dependencies = typeof (newView) == 'string' ? null : newView.getAttribute('data-require');
dependencies = dependencies ? dependencies.split(',') : [];
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
var isPluginpage = options.url.toLowerCase().indexOf('/configurationpage?') != -1;
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
if (isPluginpage) {
dependencies.push('jqmpopup');
dependencies.push('jqmcollapsible');
dependencies.push('jqmcheckbox');
dependencies.push('legacy/dashboard');
dependencies.push('legacy/selectmenu');
dependencies.push('jqmcontrolgroup');
}
2016-03-15 21:13:57 -07:00
2016-05-15 18:49:01 -07:00
if (isPluginpage || (newView.classList && newView.classList.contains('type-interior'))) {
dependencies.push('jqmlistview');
dependencies.push('scripts/notifications');
2016-06-01 21:41:12 -07:00
dependencies.push('dashboardcss');
2016-05-15 18:49:01 -07:00
}
2016-03-15 21:13:57 -07:00
2016-05-15 18:49:01 -07:00
return new Promise(function (resolve, reject) {
2016-03-15 21:13:57 -07:00
2016-03-15 11:03:25 -07:00
require(dependencies, function () {
2016-05-15 18:49:01 -07:00
2016-05-23 20:06:51 -07:00
var currentPage = allPages[pageIndex];
2016-03-15 11:03:25 -07:00
if (currentPage) {
triggerDestroy(currentPage);
}
2016-05-23 20:06:51 -07:00
var view = newView;
if (typeof (view) == 'string') {
view = document.createElement('div');
view.innerHTML = newView;
}
2016-03-14 13:27:35 -07:00
2016-06-03 22:51:33 -07:00
view.classList.add('mainAnimatedPage');
2016-05-23 20:06:51 -07:00
if (currentPage) {
2016-06-02 10:08:25 -07:00
if (newViewInfo.hasScript && window.$) {
2016-05-23 20:06:51 -07:00
// TODO: figure this out without jQuery
view = $(view).appendTo(mainAnimatedPages)[0];
mainAnimatedPages.removeChild(currentPage);
} else {
mainAnimatedPages.replaceChild(view, currentPage);
}
2016-03-14 22:25:47 -07:00
} else {
2016-06-02 10:08:25 -07:00
if (newViewInfo.hasScript && window.$) {
2016-03-15 22:33:31 -07:00
// TODO: figure this out without jQuery
2016-05-23 20:06:51 -07:00
view = $(view).appendTo(mainAnimatedPages)[0];
2016-03-15 22:33:31 -07:00
} else {
2016-05-23 20:06:51 -07:00
mainAnimatedPages.appendChild(view);
2016-03-15 22:33:31 -07:00
}
2016-03-14 22:25:47 -07:00
}
2016-03-14 13:27:35 -07:00
2016-05-23 20:06:51 -07:00
if (typeof (newView) != 'string') {
enhanceNewView(dependencies, view);
}
if (options.type) {
view.setAttribute('data-type', options.type);
}
var animatable = view;
allPages[pageIndex] = view;
2016-03-15 11:03:25 -07:00
if (onBeforeChange) {
onBeforeChange(view, false, options);
}
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
beforeAnimate(allPages, pageIndex, selected);
// animate here
animate(animatable, previousAnimatable, options.transition, options.isBack).then(function () {
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
selectedPageIndex = pageIndex;
2016-05-16 08:47:28 -07:00
currentUrls[pageIndex] = options.url;
2016-05-15 18:49:01 -07:00
if (!options.cancel && previousAnimatable) {
afterAnimate(allPages, pageIndex);
}
2016-05-16 11:20:08 -07:00
// Temporary hack
// If a view renders UI in viewbeforeshow the lazy image loader will think the images aren't visible and won't load images
// The views need to be updated to start loading data in beforeshow, but not render until show
document.dispatchEvent(new CustomEvent('scroll', {}));
2016-05-17 10:44:17 -07:00
if (window.$) {
$.mobile = $.mobile || {};
$.mobile.activePage = view;
}
2016-05-15 18:49:01 -07:00
resolve(view);
});
2016-03-15 11:03:25 -07:00
});
});
2016-03-14 13:27:35 -07:00
}
2016-03-15 21:13:57 -07:00
function enhanceNewView(dependencies, newView) {
var hasJqm = false;
for (var i = 0, length = dependencies.length; i < length; i++) {
if (dependencies[i].indexOf('jqm') == 0) {
hasJqm = true;
break;
}
}
2016-06-03 22:51:33 -07:00
if (hasJqm && window.$) {
2016-03-15 21:13:57 -07:00
$(newView).trigger('create');
}
}
2016-03-17 23:52:47 -07:00
function replaceAll(str, find, replace) {
return str.split(find).join(replace);
}
2016-03-18 09:23:55 -07:00
function parseHtml(html, hasScript) {
2016-03-15 22:33:31 -07:00
2016-03-18 09:23:55 -07:00
if (hasScript) {
html = replaceAll(html, '<!--<script', '<script');
html = replaceAll(html, '</script>-->', '</script>');
}
2016-03-17 23:52:47 -07:00
2016-03-15 22:33:31 -07:00
var wrapper = document.createElement('div');
wrapper.innerHTML = html;
return wrapper.querySelector('div[data-role="page"]');
}
2016-03-15 11:03:25 -07:00
function normalizeNewView(options) {
2016-03-14 13:27:35 -07:00
2016-03-15 11:03:25 -07:00
if (options.view.indexOf('data-role="page"') == -1) {
2016-05-23 20:06:51 -07:00
return options.view;
2016-03-15 11:03:25 -07:00
}
2016-03-14 13:27:35 -07:00
2016-03-18 09:23:55 -07:00
var hasScript = options.view.indexOf('<script') != -1;
2016-06-03 22:51:33 -07:00
var elem = parseHtml(options.view, hasScript);
if (hasScript) {
hasScript = elem.querySelector('script') != null;
}
2016-03-18 09:23:55 -07:00
2016-03-15 22:33:31 -07:00
return {
2016-06-03 22:51:33 -07:00
elem: elem,
2016-03-18 09:23:55 -07:00
hasScript: hasScript
2016-03-15 22:33:31 -07:00
};
2016-03-15 11:03:25 -07:00
}
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
function beforeAnimate(allPages, newPageIndex, oldPageIndex) {
for (var i = 0, length = allPages.length; i < length; i++) {
if (newPageIndex == i || oldPageIndex == i) {
//allPages[i].classList.remove('hide');
} else {
allPages[i].classList.add('hide');
}
}
2016-03-14 13:27:35 -07:00
}
2016-05-15 18:49:01 -07:00
function afterAnimate(allPages, newPageIndex) {
2016-03-14 22:25:47 -07:00
for (var i = 0, length = allPages.length; i < length; i++) {
2016-05-15 18:49:01 -07:00
if (newPageIndex == i) {
//allPages[i].classList.remove('hide');
} else {
allPages[i].classList.add('hide');
2016-03-14 22:25:47 -07:00
}
}
2016-05-15 18:49:01 -07:00
}
2016-03-14 22:25:47 -07:00
2016-05-15 18:49:01 -07:00
function animate(newAnimatedPage, oldAnimatedPage, transition, isBack) {
2016-05-19 22:36:57 -07:00
if (enableAnimation() && oldAnimatedPage && newAnimatedPage.animate) {
2016-05-15 18:49:01 -07:00
if (transition == 'slide') {
return slide(newAnimatedPage, oldAnimatedPage, transition, isBack);
} else if (transition == 'fade') {
return fade(newAnimatedPage, oldAnimatedPage, transition, isBack);
}
}
return Promise.resolve();
}
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
function slide(newAnimatedPage, oldAnimatedPage, transition, isBack) {
return new Promise(function (resolve, reject) {
2016-03-14 22:25:47 -07:00
// Do not use fill: both or the ability to swipe horizontally may be affected on Chrome 50
var timings = {
duration: 450,
iterations: 1,
easing: 'ease-out'
}
2016-05-15 18:49:01 -07:00
2016-05-24 00:40:45 -07:00
if (!browser.chrome) {
timings.fill = 'both';
}
var animations = [];
2016-05-15 18:49:01 -07:00
if (oldAnimatedPage) {
var destination = isBack ? '100%' : '-100%';
2016-05-15 18:49:01 -07:00
animations.push(oldAnimatedPage.animate([
2016-05-15 18:49:01 -07:00
{ transform: 'none', offset: 0 },
{ transform: 'translate3d(' + destination + ', 0, 0)', offset: 1 }
2016-05-15 18:49:01 -07:00
], timings));
}
2016-05-15 18:49:01 -07:00
var start = isBack ? '-100%' : '100%';
2016-05-15 18:49:01 -07:00
animations.push(newAnimatedPage.animate([
2016-05-15 18:49:01 -07:00
{ transform: 'translate3d(' + start + ', 0, 0)', offset: 0 },
{ transform: 'none', offset: 1 }
2016-05-15 18:49:01 -07:00
], timings));
currentAnimations = animations;
2016-05-15 18:49:01 -07:00
animations[animations.length - 1].onfinish = resolve;
});
2016-03-14 13:27:35 -07:00
}
2016-05-15 18:49:01 -07:00
function fade(newAnimatedPage, oldAnimatedPage, transition, isBack) {
return new Promise(function (resolve, reject) {
2016-05-15 18:49:01 -07:00
// Do not use fill: both or the ability to swipe horizontally may be affected on Chrome 50
var timings = {
duration: 140,
iterations: 1,
easing: 'ease-out'
}
2016-05-15 18:49:01 -07:00
2016-05-24 00:40:45 -07:00
if (!browser.chrome) {
timings.fill = 'both';
}
var animations = [];
2016-05-15 18:49:01 -07:00
if (oldAnimatedPage) {
animations.push(oldAnimatedPage.animate([
2016-05-15 18:49:01 -07:00
{ opacity: 1, offset: 0 },
{ opacity: 0, offset: 1 }
2016-05-15 18:49:01 -07:00
], timings));
}
2016-05-15 18:49:01 -07:00
animations.push(newAnimatedPage.animate([
2016-05-15 18:49:01 -07:00
{ opacity: 0, offset: 0 },
{ opacity: 1, offset: 1 }
2016-05-15 18:49:01 -07:00
], timings));
currentAnimations = animations;
2016-05-15 18:49:01 -07:00
animations[animations.length - 1].onfinish = resolve;
});
}
var currentAnimations = [];
function cancelActiveAnimations() {
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
var animations = currentAnimations;
for (var i = 0, length = animations.length; i < length; i++) {
cancelAnimation(animations[i]);
}
}
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
function cancelAnimation(animation) {
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
try {
animation.cancel();
} catch (err) {
console.log('Error canceling animation: ' + err);
}
}
var onBeforeChange;
function setOnBeforeChange(fn) {
onBeforeChange = fn;
}
function tryRestoreView(options) {
var url = options.url;
2016-05-16 08:47:28 -07:00
var index = currentUrls.indexOf(url);
2016-05-15 18:49:01 -07:00
2016-05-16 08:47:28 -07:00
if (index != -1) {
2016-05-23 20:06:51 -07:00
var animatable = allPages[index];
var view = animatable;
2016-03-14 13:27:35 -07:00
2016-05-16 08:47:28 -07:00
if (view) {
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
if (options.cancel) {
return;
}
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
cancelActiveAnimations();
2016-03-14 13:27:35 -07:00
2016-05-23 20:06:51 -07:00
var selected = selectedPageIndex;
2016-05-15 18:49:01 -07:00
var previousAnimatable = selected == -1 ? null : allPages[selected];
if (onBeforeChange) {
onBeforeChange(view, true, options);
}
beforeAnimate(allPages, index, selected);
2016-05-23 20:06:51 -07:00
animatable.classList.remove('hide');
2016-05-15 18:49:01 -07:00
return animate(animatable, previousAnimatable, options.transition, options.isBack).then(function () {
selectedPageIndex = index;
if (!options.cancel && previousAnimatable) {
afterAnimate(allPages, index);
2016-03-14 22:25:47 -07:00
}
2016-05-16 11:20:08 -07:00
// Temporary hack
// If a view renders UI in viewbeforeshow the lazy image loader will think the images aren't visible and won't load images
// The views need to be updated to start loading data in beforeshow, but not render until show
document.dispatchEvent(new CustomEvent('scroll', {}));
2016-05-17 10:44:17 -07:00
if (window.$) {
$.mobile = $.mobile || {};
$.mobile.activePage = view;
}
2016-05-15 18:49:01 -07:00
return view;
});
2016-03-14 13:27:35 -07:00
}
2016-05-15 18:49:01 -07:00
}
2016-03-14 13:27:35 -07:00
2016-05-15 18:49:01 -07:00
return Promise.reject();
2016-03-14 13:27:35 -07:00
}
function triggerDestroy(view) {
view.dispatchEvent(new CustomEvent("viewdestroy", {}));
}
function reset() {
2016-05-23 20:06:51 -07:00
allPages = [];
2016-05-16 08:47:28 -07:00
currentUrls = [];
2016-05-23 20:06:51 -07:00
mainAnimatedPages.innerHTML = '';
selectedPageIndex = -1;
2016-03-14 13:27:35 -07:00
}
2016-05-16 22:45:06 -07:00
if (enableAnimation() && !document.documentElement.animate) {
2016-05-16 11:20:08 -07:00
require(['webAnimations']);
2016-05-15 18:49:01 -07:00
}
2016-03-14 13:27:35 -07:00
2016-05-23 20:06:51 -07:00
reset();
mainAnimatedPages.classList.remove('hide');
2016-03-14 13:27:35 -07:00
return {
loadView: loadView,
tryRestoreView: tryRestoreView,
reset: reset,
2016-05-16 11:20:08 -07:00
setOnBeforeChange: setOnBeforeChange
2016-03-14 13:27:35 -07:00
};
});