define(["browser", "dom", "layoutManager", "css!./viewcontainer-lite"], function(browser, dom, layoutManager) { "use strict"; function enableAnimation() { return !browser.tv && browser.supportsCssAnimation() } function findLastView(parent, className) { for (var nodes = parent.childNodes, i = nodes.length - 1; i >= 0; i--) { var node = nodes[i], classList = node.classList; if (classList && classList.contains(className)) return node } } function findViewBefore(elem, className) { for (var node = elem.previousSibling; node;) { var classList = node.classList; if (classList && classList.contains(className)) return node; node = node.previousSibling } } function loadView(options) { if (!options.cancel) { cancelActiveAnimations(); var selected = selectedPageIndex, previousAnimatable = -1 === selected ? null : allPages[selected], pageIndex = selected + 1; pageIndex >= pageContainerCount && (pageIndex = 0); var viewHtml = options.view, properties = []; options.fullscreen && properties.push("fullscreen"); var view, currentPage = allPages[pageIndex]; return currentPage ? (triggerDestroy(currentPage), currentPage.insertAdjacentHTML("beforebegin", viewHtml), view = findViewBefore(currentPage, "view"), mainAnimatedPages.removeChild(currentPage)) : (mainAnimatedPages.insertAdjacentHTML("beforeend", viewHtml), view = findLastView(mainAnimatedPages, "view")), view.classList.add("mainAnimatedPage"), properties.length && view.setAttribute("data-properties", properties.join(",")), options.type && view.setAttribute("data-type", options.type), allPages[pageIndex] = view, onBeforeChange && onBeforeChange(view, !1, options), beforeAnimate(allPages, pageIndex, selected), animate(view, previousAnimatable, options.transition, options.isBack).then(function() { return selectedPageIndex = pageIndex, currentUrls[pageIndex] = options.url, !options.cancel && previousAnimatable && afterAnimate(allPages, pageIndex), view }) } } function beforeAnimate(allPages, newPageIndex, oldPageIndex) { for (var i = 0, length = allPages.length; i < length; i++) newPageIndex === i || oldPageIndex === i || allPages[i].classList.add("hide") } function afterAnimate(allPages, newPageIndex) { for (var i = 0, length = allPages.length; i < length; i++) newPageIndex === i || allPages[i].classList.add("hide") } function animate(newAnimatedPage, oldAnimatedPage, transition, isBack) { if (enableAnimation() && oldAnimatedPage) { if ("slide" === transition) return slide(newAnimatedPage, oldAnimatedPage, transition, isBack); if ("fade" === transition) return fade(newAnimatedPage, oldAnimatedPage, transition, isBack); clearAnimation(newAnimatedPage), oldAnimatedPage && clearAnimation(oldAnimatedPage) } return Promise.resolve() } function clearAnimation(elem) { setAnimation(elem, "none") } function slide(newAnimatedPage, oldAnimatedPage, transition, isBack) { return new Promise(function(resolve, reject) { var duration = layoutManager.tv ? 450 : 160, animations = []; oldAnimatedPage && (isBack ? setAnimation(oldAnimatedPage, "view-slideright-r " + duration + "ms ease-out normal both") : setAnimation(oldAnimatedPage, "view-slideleft-r " + duration + "ms ease-out normal both"), animations.push(oldAnimatedPage)), isBack ? setAnimation(newAnimatedPage, "view-slideright " + duration + "ms ease-out normal both") : setAnimation(newAnimatedPage, "view-slideleft " + duration + "ms ease-out normal both"), animations.push(newAnimatedPage), currentAnimations = animations; var onAnimationComplete = function() { dom.removeEventListener(newAnimatedPage, dom.whichAnimationEvent(), onAnimationComplete, { once: !0 }), resolve() }; dom.addEventListener(newAnimatedPage, dom.whichAnimationEvent(), onAnimationComplete, { once: !0 }) }) } function fade(newAnimatedPage, oldAnimatedPage, transition, isBack) { return new Promise(function(resolve, reject) { var duration = layoutManager.tv ? 450 : 270, animations = []; newAnimatedPage.style.opacity = 0, setAnimation(newAnimatedPage, "view-fadein " + duration + "ms ease-in normal both"), animations.push(newAnimatedPage), oldAnimatedPage && (setAnimation(oldAnimatedPage, "view-fadeout " + duration + "ms ease-out normal both"), animations.push(oldAnimatedPage)), currentAnimations = animations; var onAnimationComplete = function() { dom.removeEventListener(newAnimatedPage, dom.whichAnimationEvent(), onAnimationComplete, { once: !0 }), resolve() }; dom.addEventListener(newAnimatedPage, dom.whichAnimationEvent(), onAnimationComplete, { once: !0 }) }) } function setAnimation(elem, value) { requestAnimationFrame(function() { elem.style.animation = value }) } function cancelActiveAnimations() { for (var animations = currentAnimations, i = 0, length = animations.length; i < length; i++) animations[i].style.animation = "none" } function setOnBeforeChange(fn) { onBeforeChange = fn } function tryRestoreView(options) { var url = options.url, index = currentUrls.indexOf(url); if (-1 !== index) { var animatable = allPages[index], view = animatable; if (view) { if (options.cancel) return; cancelActiveAnimations(); var selected = selectedPageIndex, previousAnimatable = -1 === selected ? null : allPages[selected]; return onBeforeChange && onBeforeChange(view, !0, options), beforeAnimate(allPages, index, selected), animatable.classList.remove("hide"), animate(animatable, previousAnimatable, options.transition, options.isBack).then(function() { return selectedPageIndex = index, !options.cancel && previousAnimatable && afterAnimate(allPages, index), view }) } } return Promise.reject() } function triggerDestroy(view) { view.dispatchEvent(new CustomEvent("viewdestroy", { cancelable: !1 })) } function reset() { allPages = [], currentUrls = [], mainAnimatedPages.innerHTML = "", selectedPageIndex = -1 } var onBeforeChange, mainAnimatedPages = document.querySelector(".mainAnimatedPages"), allPages = [], currentUrls = [], pageContainerCount = 3, selectedPageIndex = -1, currentAnimations = []; return { loadView: loadView, tryRestoreView: tryRestoreView, reset: reset, setOnBeforeChange: setOnBeforeChange } });