TIF_E41211408/public/assets/assetLanding/js/timeline.js

392 lines
13 KiB
JavaScript

jQuery(document).ready(function ($) {
var timelines = $(".cd-horizontal-timeline"),
eventsMinDistance = 60;
timelines.length > 0 && initTimeline(timelines);
function initTimeline(timelines) {
timelines.each(function () {
var timeline = $(this),
timelineComponents = {};
//cache timeline components
timelineComponents["timelineWrapper"] = timeline.find(".events-wrapper");
timelineComponents["eventsWrapper"] =
timelineComponents["timelineWrapper"].children(".events");
timelineComponents["fillingLine"] =
timelineComponents["eventsWrapper"].children(".filling-line");
timelineComponents["timelineEvents"] =
timelineComponents["eventsWrapper"].find("a");
timelineComponents["timelineDates"] = parseDate(
timelineComponents["timelineEvents"]
);
timelineComponents["eventsMinLapse"] = minLapse(
timelineComponents["timelineDates"]
);
timelineComponents["timelineNavigation"] = timeline.find(
".cd-timeline-navigation"
);
timelineComponents["eventsContent"] =
timeline.children(".events-content");
//assign a left postion to the single events along the timeline
setDatePosition(timelineComponents, eventsMinDistance);
//assign a width to the timeline
var timelineTotWidth = setTimelineWidth(
timelineComponents,
eventsMinDistance
);
//the timeline has been initialize - show it
timeline.addClass("loaded");
//detect click on the next arrow
timelineComponents["timelineNavigation"].on(
"click",
".next",
function (event) {
event.preventDefault();
updateSlide(timelineComponents, timelineTotWidth, "next");
}
);
//detect click on the prev arrow
timelineComponents["timelineNavigation"].on(
"click",
".prev",
function (event) {
event.preventDefault();
updateSlide(timelineComponents, timelineTotWidth, "prev");
}
);
//detect click on the a single event - show new event content
timelineComponents["eventsWrapper"].on("click", "a", function (event) {
event.preventDefault();
timelineComponents["timelineEvents"].removeClass("selected");
$(this).addClass("selected");
updateOlderEvents($(this));
updateFilling(
$(this),
timelineComponents["fillingLine"],
timelineTotWidth
);
updateVisibleContent($(this), timelineComponents["eventsContent"]);
});
//on swipe, show next/prev event content
timelineComponents["eventsContent"].on("swipeleft", function () {
var mq = checkMQ();
mq == "mobile" &&
showNewContent(timelineComponents, timelineTotWidth, "next");
});
timelineComponents["eventsContent"].on("swiperight", function () {
var mq = checkMQ();
mq == "mobile" &&
showNewContent(timelineComponents, timelineTotWidth, "prev");
});
//keyboard navigation
$(document).keyup(function (event) {
if (event.which == "37" && elementInViewport(timeline.get(0))) {
showNewContent(timelineComponents, timelineTotWidth, "prev");
} else if (event.which == "39" && elementInViewport(timeline.get(0))) {
showNewContent(timelineComponents, timelineTotWidth, "next");
}
});
});
}
function updateSlide(timelineComponents, timelineTotWidth, string) {
//retrieve translateX value of timelineComponents['eventsWrapper']
var translateValue = getTranslateValue(timelineComponents["eventsWrapper"]),
wrapperWidth = Number(
timelineComponents["timelineWrapper"].css("width").replace("px", "")
);
//translate the timeline to the left('next')/right('prev')
string == "next"
? translateTimeline(
timelineComponents,
translateValue - wrapperWidth + eventsMinDistance,
wrapperWidth - timelineTotWidth
)
: translateTimeline(
timelineComponents,
translateValue + wrapperWidth - eventsMinDistance
);
}
function showNewContent(timelineComponents, timelineTotWidth, string) {
//go from one event to the next/previous one
var visibleContent = timelineComponents["eventsContent"].find(".selected"),
newContent =
string == "next" ? visibleContent.next() : visibleContent.prev();
if (newContent.length > 0) {
//if there's a next/prev event - show it
var selectedDate = timelineComponents["eventsWrapper"].find(".selected"),
newEvent =
string == "next"
? selectedDate.parent("li").next("li").children("a")
: selectedDate.parent("li").prev("li").children("a");
updateFilling(
newEvent,
timelineComponents["fillingLine"],
timelineTotWidth
);
updateVisibleContent(newEvent, timelineComponents["eventsContent"]);
newEvent.addClass("selected");
selectedDate.removeClass("selected");
updateOlderEvents(newEvent);
updateTimelinePosition(string, newEvent, timelineComponents);
}
}
function updateTimelinePosition(string, event, timelineComponents) {
//translate timeline to the left/right according to the position of the selected event
var eventStyle = window.getComputedStyle(event.get(0), null),
eventLeft = Number(eventStyle.getPropertyValue("left").replace("px", "")),
timelineWidth = Number(
timelineComponents["timelineWrapper"].css("width").replace("px", "")
),
timelineTotWidth = Number(
timelineComponents["eventsWrapper"].css("width").replace("px", "")
);
var timelineTranslate = getTranslateValue(
timelineComponents["eventsWrapper"]
);
if (
(string == "next" && eventLeft > timelineWidth - timelineTranslate) ||
(string == "prev" && eventLeft < -timelineTranslate)
) {
translateTimeline(
timelineComponents,
-eventLeft + timelineWidth / 2,
timelineWidth - timelineTotWidth
);
}
}
function translateTimeline(timelineComponents, value, totWidth) {
var eventsWrapper = timelineComponents["eventsWrapper"].get(0);
value = value > 0 ? 0 : value; //only negative translate value
value =
!(typeof totWidth === "undefined") && value < totWidth ? totWidth : value; //do not translate more than timeline width
setTransformValue(eventsWrapper, "translateX", value + "px");
//update navigation arrows visibility
value == 0
? timelineComponents["timelineNavigation"]
.find(".prev")
.addClass("inactive")
: timelineComponents["timelineNavigation"]
.find(".prev")
.removeClass("inactive");
value == totWidth
? timelineComponents["timelineNavigation"]
.find(".next")
.addClass("inactive")
: timelineComponents["timelineNavigation"]
.find(".next")
.removeClass("inactive");
}
function updateFilling(selectedEvent, filling, totWidth) {
//change .filling-line length according to the selected event
var eventStyle = window.getComputedStyle(selectedEvent.get(0), null),
eventLeft = eventStyle.getPropertyValue("left"),
eventWidth = eventStyle.getPropertyValue("width");
eventLeft =
Number(eventLeft.replace("px", "")) +
Number(eventWidth.replace("px", "")) / 2;
var scaleValue = eventLeft / totWidth;
setTransformValue(filling.get(0), "scaleX", scaleValue);
}
function setDatePosition(timelineComponents, min) {
for (i = 0; i < timelineComponents["timelineDates"].length; i++) {
var distance = daydiff(
timelineComponents["timelineDates"][0],
timelineComponents["timelineDates"][i]
),
distanceNorm =
Math.round(distance / timelineComponents["eventsMinLapse"]) + 2;
timelineComponents["timelineEvents"]
.eq(i)
.css("left", distanceNorm * min + "px");
}
}
function setTimelineWidth(timelineComponents, width) {
var timeSpan = daydiff(
timelineComponents["timelineDates"][0],
timelineComponents["timelineDates"][
timelineComponents["timelineDates"].length - 1
]
),
timeSpanNorm = timeSpan / timelineComponents["eventsMinLapse"],
timeSpanNorm = Math.round(timeSpanNorm) + 4,
totalWidth = timeSpanNorm * width;
timelineComponents["eventsWrapper"].css("width", totalWidth + "px");
updateFilling(
timelineComponents["eventsWrapper"].find("a.selected"),
timelineComponents["fillingLine"],
totalWidth
);
updateTimelinePosition(
"next",
timelineComponents["eventsWrapper"].find("a.selected"),
timelineComponents
);
return totalWidth;
}
function updateVisibleContent(event, eventsContent) {
var eventDate = event.data("date"),
visibleContent = eventsContent.find(".selected"),
selectedContent = eventsContent.find('[data-date="' + eventDate + '"]'),
selectedContentHeight = selectedContent.height();
if (selectedContent.index() > visibleContent.index()) {
var classEnetering = "selected enter-right",
classLeaving = "leave-left";
} else {
var classEnetering = "selected enter-left",
classLeaving = "leave-right";
}
selectedContent.attr("class", classEnetering);
visibleContent
.attr("class", classLeaving)
.one(
"webkitAnimationEnd oanimationend msAnimationEnd animationend",
function () {
visibleContent.removeClass("leave-right leave-left");
selectedContent.removeClass("enter-left enter-right");
}
);
eventsContent.css("height", selectedContentHeight + "px");
}
function updateOlderEvents(event) {
event
.parent("li")
.prevAll("li")
.children("a")
.addClass("older-event")
.end()
.end()
.nextAll("li")
.children("a")
.removeClass("older-event");
}
function getTranslateValue(timeline) {
var timelineStyle = window.getComputedStyle(timeline.get(0), null),
timelineTranslate =
timelineStyle.getPropertyValue("-webkit-transform") ||
timelineStyle.getPropertyValue("-moz-transform") ||
timelineStyle.getPropertyValue("-ms-transform") ||
timelineStyle.getPropertyValue("-o-transform") ||
timelineStyle.getPropertyValue("transform");
if (timelineTranslate.indexOf("(") >= 0) {
var timelineTranslate = timelineTranslate.split("(")[1];
timelineTranslate = timelineTranslate.split(")")[0];
timelineTranslate = timelineTranslate.split(",");
var translateValue = timelineTranslate[4];
} else {
var translateValue = 0;
}
return Number(translateValue);
}
function setTransformValue(element, property, value) {
element.style["-webkit-transform"] = property + "(" + value + ")";
element.style["-moz-transform"] = property + "(" + value + ")";
element.style["-ms-transform"] = property + "(" + value + ")";
element.style["-o-transform"] = property + "(" + value + ")";
element.style["transform"] = property + "(" + value + ")";
}
//based on http://stackoverflow.com/questions/542938/how-do-i-get-the-number-of-days-between-two-dates-in-javascript
function parseDate(events) {
var dateArrays = [];
events.each(function () {
var singleDate = $(this),
dateComp = singleDate.data("date").split("T");
if (dateComp.length > 1) {
//both DD/MM/YEAR and time are provided
var dayComp = dateComp[0].split("/"),
timeComp = dateComp[1].split(":");
} else if (dateComp[0].indexOf(":") >= 0) {
//only time is provide
var dayComp = ["2000", "0", "0"],
timeComp = dateComp[0].split(":");
} else {
//only DD/MM/YEAR
var dayComp = dateComp[0].split("/"),
timeComp = ["0", "0"];
}
var newDate = new Date(
dayComp[2],
dayComp[1] - 1,
dayComp[0],
timeComp[0],
timeComp[1]
);
dateArrays.push(newDate);
});
return dateArrays;
}
function daydiff(first, second) {
return Math.round(second - first);
}
function minLapse(dates) {
//determine the minimum distance among events
var dateDistances = [];
for (i = 1; i < dates.length; i++) {
var distance = daydiff(dates[i - 1], dates[i]);
dateDistances.push(distance);
}
return Math.min.apply(null, dateDistances);
}
/*
How to tell if a DOM element is visible in the current viewport?
http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
*/
function elementInViewport(el) {
var top = el.offsetTop;
var left = el.offsetLeft;
var width = el.offsetWidth;
var height = el.offsetHeight;
while (el.offsetParent) {
el = el.offsetParent;
top += el.offsetTop;
left += el.offsetLeft;
}
return (
top < window.pageYOffset + window.innerHeight &&
left < window.pageXOffset + window.innerWidth &&
top + height > window.pageYOffset &&
left + width > window.pageXOffset
);
}
function checkMQ() {
//check if mobile or desktop device
return window
.getComputedStyle(
document.querySelector(".cd-horizontal-timeline"),
"::before"
)
.getPropertyValue("content")
.replace(/'/g, "")
.replace(/"/g, "");
}
});