@import url(https://fonts.googleapis.com/css?family=Lato:400,700|Montserrat:400,700);
@import url(https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css);
body {
font-family: 'Lato', sans-serif;
font-size: 15px;
width: 100%;
height: 100%;
background: #FFFAF0;
}
/*/ start about work experience /*/
.experience h4 {
text-align: center;
}
.experience {
margin-top: 5%;
width: 100%;
/* background-color: #3b8472;*/
height: auto;
}
.experience h5 {
color: #008080;
font-size: 22px;
text-transform: uppercase;
}
.experience ol,
.experience ul {
list-style: none;
}
.experience a {
text-decoration: none;
}
.experience img {
width: 20%;
float: right;
}
.experience .fa {
color: #40E0D0;
font-size: 35px;
}
.experience .fa-users {
font-size: 30px;
}
.cd-horizontal-timeline {
opacity: 0;
margin: 2em auto;
-webkit-transition: opacity 0.2s;
-moz-transition: opacity 0.2s;
transition: opacity 0.2s;
}
.cd-horizontal-timeline::before {
/* never visible - this is used in jQuery to check the current MQ */
content: 'mobile';
display: none;
}
.cd-horizontal-timeline.loaded {
/* show the timeline after events position has been set (using JavaScript) */
opacity: 1;
}
.cd-horizontal-timeline .timeline {
position: relative;
height: 100px;
width: 100%;
/* max-width: 425px; */
margin: 0 auto;
}
.cd-horizontal-timeline .events-wrapper {
position: relative;
height: 100%;
margin: 0 40px;
overflow: hidden;
}
.cd-horizontal-timeline .events-wrapper::after,
.cd-horizontal-timeline .events-wrapper::before {
/* these are used to create a shadow effect at the sides of the timeline */
content: '';
position: absolute;
z-index: 2;
top: 0;
height: 100%;
width: 20px;
}
.cd-horizontal-timeline .events-wrapper::before {
left: 0;
background-image: -webkit-linear-gradient( left, #f8f8f8, rgba(248, 248, 248, 0));
background-image: linear-gradient(to right, #f8f8f8, rgba(248, 248, 248, 0));
}
.cd-horizontal-timeline .events-wrapper::after {
right: 0;
background-image: -webkit-linear-gradient( right, #f8f8f8, rgba(248, 248, 248, 0));
background-image: linear-gradient(to left, #f8f8f8, rgba(248, 248, 248, 0));
}
.cd-horizontal-timeline .events {
/* this is the grey line/timeline */
position: absolute;
z-index: 1;
left: 0;
top: 49px;
height: 2px;
/* width will be set using JavaScript */
background: #dfdfdf;
-webkit-transition: -webkit-transform 0.4s;
-moz-transition: -moz-transform 0.4s;
transition: transform 0.4s;
}
.cd-horizontal-timeline .filling-line {
/* this is used to create the green line filling the timeline */
position: absolute;
z-index: 1;
left: 0;
top: 0;
height: 100%;
width: 100%;
background-color: #008080;
-webkit-transform: scaleX(0);
-moz-transform: scaleX(0);
-ms-transform: scaleX(0);
-o-transform: scaleX(0);
transform: scaleX(0);
-webkit-transform-origin: left center;
-moz-transform-origin: left center;
-ms-transform-origin: left center;
-o-transform-origin: left center;
transform-origin: left center;
-webkit-transition: -webkit-transform 0.3s;
-moz-transition: -moz-transform 0.3s;
transition: transform 0.3s;
}
.cd-horizontal-timeline .events a {
position: absolute;
bottom: 0;
z-index: 2;
text-align: center;
font-size: 1.3rem;
padding-bottom: 15px;
color: #383838;
/* fix bug on Safari - text flickering while timeline translates */
-webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-ms-transform: translateZ(0);
-o-transform: translateZ(0);
transform: translateZ(0);
}
.cd-horizontal-timeline .events a::after {
/* this is used to create the event spot */
content: '';
position: absolute;
left: 50%;
right: auto;
-webkit-transform: translateX(-50%);
-moz-transform: translateX(-50%);
-ms-transform: translateX(-50%);
-o-transform: translateX(-50%);
transform: translateX(-50%);
bottom: -5px;
height: 12px;
width: 12px;
border-radius: 50%;
border: 2px solid #dfdfdf;
background-color: #f8f8f8;
-webkit-transition: background-color 0.3s, border-color 0.3s;
-moz-transition: background-color 0.3s, border-color 0.3s;
transition: background-color 0.3s, border-color 0.3s;
}
.no-touch .cd-horizontal-timeline .events a:hover::after {
background-color: #008080;
border-color: #008080;
}
.cd-horizontal-timeline .events a.selected {
pointer-events: none;
}
.cd-horizontal-timeline .events a.selected::after {
background-color: #008080;
border-color: #008080;
}
.cd-horizontal-timeline .events a.older-event::after {
border-color: #008080;
}
@media only screen and (min-width: 1100px) {
.cd-horizontal-timeline {
margin: 3em auto;
}
.cd-horizontal-timeline::before {
/* never visible - this is used in jQuery to check the current MQ */
content: 'desktop';
}
}
.cd-timeline-navigation a {
/* these are the left/right arrows to navigate the timeline */
position: absolute;
z-index: 1;
top: 50%;
bottom: auto;
-webkit-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
-o-transform: translateY(-50%);
transform: translateY(-50%);
height: 34px;
width: 34px;
border-radius: 50%;
border: 2px solid #dfdfdf;
/* replace text with an icon */
overflow: hidden;
color: transparent;
text-indent: 100%;
white-space: nowrap;
-webkit-transition: border-color 0.3s;
-moz-transition: border-color 0.3s;
transition: border-color 0.3s;
}
.cd-timeline-navigation a::after {
/* arrow icon */
content: '';
position: absolute;
height: 16px;
width: 16px;
left: 50%;
top: 50%;
bottom: auto;
right: auto;
-webkit-transform: translateX(-50%) translateY(-50%);
-moz-transform: translateX(-50%) translateY(-50%);
-ms-transform: translateX(-50%) translateY(-50%);
-o-transform: translateX(-50%) translateY(-50%);
transform: translateX(-50%) translateY(-50%);
background: url(../img/cd-arrow.svg) no-repeat 0 0;
}
.cd-timeline-navigation a.prev {
left: 0;
-webkit-transform: translateY(-50%) rotate(180deg);
-moz-transform: translateY(-50%) rotate(180deg);
-ms-transform: translateY(-50%) rotate(180deg);
-o-transform: translateY(-50%) rotate(180deg);
transform: translateY(-50%) rotate(180deg);
}
.cd-timeline-navigation a.next {
right: 0;
}
.no-touch .cd-timeline-navigation a:hover {
border-color: #008080;
}
.cd-timeline-navigation a.inactive {
cursor: not-allowed;
}
.cd-timeline-navigation a.inactive::after {
background-position: 0 -16px;
}
.no-touch .cd-timeline-navigation a.inactive:hover {
border-color: #dfdfdf;
}
.cd-horizontal-timeline .events-content {
position: relative;
width: 100%;
margin: 2em 0;
overflow: hidden;
-webkit-transition: height 0.4s;
-moz-transition: height 0.4s;
transition: height 0.4s;
}
.cd-horizontal-timeline .events-content li {
position: absolute;
z-index: 1;
width: 100%;
left: 0;
top: 0;
-webkit-transform: translateX(-100%);
-moz-transform: translateX(-100%);
-ms-transform: translateX(-100%);
-o-transform: translateX(-100%);
transform: translateX(-100%);
padding: 0 5%;
opacity: 0;
-webkit-animation-duration: 0.4s;
-moz-animation-duration: 0.4s;
animation-duration: 0.4s;
-webkit-animation-timing-function: ease-in-out;
-moz-animation-timing-function: ease-in-out;
animation-timing-function: ease-in-out;
}
.cd-horizontal-timeline .events-content li.selected {
/* visible event content */
position: relative;
z-index: 2;
opacity: 1;
-webkit-transform: translateX(0);
-moz-transform: translateX(0);
-ms-transform: translateX(0);
-o-transform: translateX(0);
transform: translateX(0);
}
.cd-horizontal-timeline .events-content li.enter-right,
.cd-horizontal-timeline .events-content li.leave-right {
-webkit-animation-name: cd-enter-right;
-moz-animation-name: cd-enter-right;
animation-name: cd-enter-right;
}
.cd-horizontal-timeline .events-content li.enter-left,
.cd-horizontal-timeline .events-content li.leave-left {
-webkit-animation-name: cd-enter-left;
-moz-animation-name: cd-enter-left;
animation-name: cd-enter-left;
}
.cd-horizontal-timeline .events-content li.leave-right,
.cd-horizontal-timeline .events-content li.leave-left {
-webkit-animation-direction: reverse;
-moz-animation-direction: reverse;
animation-direction: reverse;
}
.cd-horizontal-timeline .events-content li > * {
max-width: 800px;
margin: 0 auto;
}
.cd-horizontal-timeline .events-content em {
display: block;
font-style: italic;
margin: 10px auto;
font-size: 16px;
color: #778899;
}
.cd-horizontal-timeline .events-content p {
font-size: 15px;
color: #333;
}
.cd-horizontal-timeline .events-content em,
.cd-horizontal-timeline .events-content p {
line-height: 1.6;
}
@media only screen and (min-width: 768px) {
.cd-horizontal-timeline .events-content h4 {
font-size: 22px;
}
.cd-horizontal-timeline .events-content em {
font-size: 16px;
}
.cd-horizontal-timeline .events-content p {
font-size: 15px;
}
}
@media only screen and (max-width: 535px) {
.experience img {
visibility: hidden;
}
.cd-horizontal-timeline .events-wrapper {
margin: 0 -280px;
}
}
@-webkit-keyframes cd-enter-right {
0% {
opacity: 0;
-webkit-transform: translateX(100%);
}
100% {
opacity: 1;
-webkit-transform: translateX(0%);
}
}
@-moz-keyframes cd-enter-right {
0% {
opacity: 0;
-moz-transform: translateX(100%);
}
100% {
opacity: 1;
-moz-transform: translateX(0%);
}
}
@keyframes cd-enter-right {
0% {
opacity: 0;
-webkit-transform: translateX(100%);
-moz-transform: translateX(100%);
-ms-transform: translateX(100%);
-o-transform: translateX(100%);
transform: translateX(100%);
}
100% {
opacity: 1;
-webkit-transform: translateX(0%);
-moz-transform: translateX(0%);
-ms-transform: translateX(0%);
-o-transform: translateX(0%);
transform: translateX(0%);
}
}
@-webkit-keyframes cd-enter-left {
0% {
opacity: 0;
-webkit-transform: translateX(-100%);
}
100% {
opacity: 1;
-webkit-transform: translateX(0%);
}
}
@-moz-keyframes cd-enter-left {
0% {
opacity: 0;
-moz-transform: translateX(-100%);
}
100% {
opacity: 1;
-moz-transform: translateX(0%);
}
}
@keyframes cd-enter-left {
0% {
opacity: 0;
-webkit-transform: translateX(-100%);
-moz-transform: translateX(-100%);
-ms-transform: translateX(-100%);
-o-transform: translateX(-100%);
transform: translateX(-100%);
}
100% {
opacity: 1;
-webkit-transform: translateX(0%);
-moz-transform: translateX(0%);
-ms-transform: translateX(0%);
-o-transform: translateX(0%);
transform: translateX(0%);
}
}
/*/ end about work experience /*/
jQuery(document).ready(function($) {
var timelines = $('.cd-horizontal-timeline'),
eventsMinDistance = 150;
(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, "");
}
});