Subversion-Projekte lars-tiefland.cienc

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
5 lars 1
jQuery(document).ready(function($){
2
	var timelines = $('.cd-horizontal-timeline'),
3
		eventsMinDistance = 60;
4
 
5
	(timelines.length > 0) && initTimeline(timelines);
6
 
7
	function initTimeline(timelines) {
8
		timelines.each(function(){
9
			var timeline = $(this),
10
				timelineComponents = {};
11
			//cache timeline components
12
			timelineComponents['timelineWrapper'] = timeline.find('.events-wrapper');
13
			timelineComponents['eventsWrapper'] = timelineComponents['timelineWrapper'].children('.events');
14
			timelineComponents['fillingLine'] = timelineComponents['eventsWrapper'].children('.filling-line');
15
			timelineComponents['timelineEvents'] = timelineComponents['eventsWrapper'].find('a');
16
			timelineComponents['timelineDates'] = parseDate(timelineComponents['timelineEvents']);
17
			timelineComponents['eventsMinLapse'] = minLapse(timelineComponents['timelineDates']);
18
			timelineComponents['timelineNavigation'] = timeline.find('.cd-timeline-navigation');
19
			timelineComponents['eventsContent'] = timeline.children('.events-content');
20
 
21
			//assign a left postion to the single events along the timeline
22
			setDatePosition(timelineComponents, eventsMinDistance);
23
			//assign a width to the timeline
24
			var timelineTotWidth = setTimelineWidth(timelineComponents, eventsMinDistance);
25
			//the timeline has been initialize - show it
26
			timeline.addClass('loaded');
27
 
28
			//detect click on the next arrow
29
			timelineComponents['timelineNavigation'].on('click', '.next', function(event){
30
				event.preventDefault();
31
				updateSlide(timelineComponents, timelineTotWidth, 'next');
32
			});
33
			//detect click on the prev arrow
34
			timelineComponents['timelineNavigation'].on('click', '.prev', function(event){
35
				event.preventDefault();
36
				updateSlide(timelineComponents, timelineTotWidth, 'prev');
37
			});
38
			//detect click on the a single event - show new event content
39
			timelineComponents['eventsWrapper'].on('click', 'a', function(event){
40
				event.preventDefault();
41
				timelineComponents['timelineEvents'].removeClass('selected');
42
				$(this).addClass('selected');
43
				updateOlderEvents($(this));
44
				updateFilling($(this), timelineComponents['fillingLine'], timelineTotWidth);
45
				updateVisibleContent($(this), timelineComponents['eventsContent']);
46
			});
47
 
48
			//on swipe, show next/prev event content
49
			timelineComponents['eventsContent'].on('swipeleft', function(){
50
				var mq = checkMQ();
51
				( mq == 'mobile' ) && showNewContent(timelineComponents, timelineTotWidth, 'next');
52
			});
53
			timelineComponents['eventsContent'].on('swiperight', function(){
54
				var mq = checkMQ();
55
				( mq == 'mobile' ) && showNewContent(timelineComponents, timelineTotWidth, 'prev');
56
			});
57
 
58
			//keyboard navigation
59
			$(document).keyup(function(event){
60
				if(event.which=='37' && elementInViewport(timeline.get(0)) ) {
61
					showNewContent(timelineComponents, timelineTotWidth, 'prev');
62
				} else if( event.which=='39' && elementInViewport(timeline.get(0))) {
63
					showNewContent(timelineComponents, timelineTotWidth, 'next');
64
				}
65
			});
66
		});
67
	}
68
 
69
	function updateSlide(timelineComponents, timelineTotWidth, string) {
70
		//retrieve translateX value of timelineComponents['eventsWrapper']
71
		var translateValue = getTranslateValue(timelineComponents['eventsWrapper']),
72
			wrapperWidth = Number(timelineComponents['timelineWrapper'].css('width').replace('px', ''));
73
		//translate the timeline to the left('next')/right('prev')
74
		(string == 'next')
75
			? translateTimeline(timelineComponents, translateValue - wrapperWidth + eventsMinDistance, wrapperWidth - timelineTotWidth)
76
			: translateTimeline(timelineComponents, translateValue + wrapperWidth - eventsMinDistance);
77
	}
78
 
79
	function showNewContent(timelineComponents, timelineTotWidth, string) {
80
		//go from one event to the next/previous one
81
		var visibleContent =  timelineComponents['eventsContent'].find('.selected'),
82
			newContent = ( string == 'next' ) ? visibleContent.next() : visibleContent.prev();
83
 
84
		if ( newContent.length > 0 ) { //if there's a next/prev event - show it
85
			var selectedDate = timelineComponents['eventsWrapper'].find('.selected'),
86
				newEvent = ( string == 'next' ) ? selectedDate.parent('li').next('li').children('a') : selectedDate.parent('li').prev('li').children('a');
87
 
88
			updateFilling(newEvent, timelineComponents['fillingLine'], timelineTotWidth);
89
			updateVisibleContent(newEvent, timelineComponents['eventsContent']);
90
			newEvent.addClass('selected');
91
			selectedDate.removeClass('selected');
92
			updateOlderEvents(newEvent);
93
			updateTimelinePosition(string, newEvent, timelineComponents);
94
		}
95
	}
96
 
97
	function updateTimelinePosition(string, event, timelineComponents) {
98
		//translate timeline to the left/right according to the position of the selected event
99
		var eventStyle = window.getComputedStyle(event.get(0), null),
100
			eventLeft = Number(eventStyle.getPropertyValue("left").replace('px', '')),
101
			timelineWidth = Number(timelineComponents['timelineWrapper'].css('width').replace('px', '')),
102
			timelineTotWidth = Number(timelineComponents['eventsWrapper'].css('width').replace('px', ''));
103
		var timelineTranslate = getTranslateValue(timelineComponents['eventsWrapper']);
104
 
105
        if( (string == 'next' && eventLeft > timelineWidth - timelineTranslate) || (string == 'prev' && eventLeft < - timelineTranslate) ) {
106
        	translateTimeline(timelineComponents, - eventLeft + timelineWidth/2, timelineWidth - timelineTotWidth);
107
        }
108
	}
109
 
110
	function translateTimeline(timelineComponents, value, totWidth) {
111
		var eventsWrapper = timelineComponents['eventsWrapper'].get(0);
112
		value = (value > 0) ? 0 : value; //only negative translate value
113
		value = ( !(typeof totWidth === 'undefined') &&  value < totWidth ) ? totWidth : value; //do not translate more than timeline width
114
		setTransformValue(eventsWrapper, 'translateX', value+'px');
115
		//update navigation arrows visibility
116
		(value == 0 ) ? timelineComponents['timelineNavigation'].find('.prev').addClass('inactive') : timelineComponents['timelineNavigation'].find('.prev').removeClass('inactive');
117
		(value == totWidth ) ? timelineComponents['timelineNavigation'].find('.next').addClass('inactive') : timelineComponents['timelineNavigation'].find('.next').removeClass('inactive');
118
	}
119
 
120
	function updateFilling(selectedEvent, filling, totWidth) {
121
		//change .filling-line length according to the selected event
122
		var eventStyle = window.getComputedStyle(selectedEvent.get(0), null),
123
			eventLeft = eventStyle.getPropertyValue("left"),
124
			eventWidth = eventStyle.getPropertyValue("width");
125
		eventLeft = Number(eventLeft.replace('px', '')) + Number(eventWidth.replace('px', ''))/2;
126
		var scaleValue = eventLeft/totWidth;
127
		setTransformValue(filling.get(0), 'scaleX', scaleValue);
128
	}
129
 
130
	function setDatePosition(timelineComponents, min) {
131
		for (i = 0; i < timelineComponents['timelineDates'].length; i++) {
132
		    var distance = daydiff(timelineComponents['timelineDates'][0], timelineComponents['timelineDates'][i]),
133
		    	distanceNorm = Math.round(distance/timelineComponents['eventsMinLapse']) + 2;
134
		    timelineComponents['timelineEvents'].eq(i).css('left', distanceNorm*min+'px');
135
		}
136
	}
137
 
138
	function setTimelineWidth(timelineComponents, width) {
139
		var timeSpan = daydiff(timelineComponents['timelineDates'][0], timelineComponents['timelineDates'][timelineComponents['timelineDates'].length-1]),
140
			timeSpanNorm = timeSpan/timelineComponents['eventsMinLapse'],
141
			timeSpanNorm = Math.round(timeSpanNorm) + 4,
142
			totalWidth = timeSpanNorm*width;
143
		timelineComponents['eventsWrapper'].css('width', totalWidth+'px');
144
		updateFilling(timelineComponents['eventsWrapper'].find('a.selected'), timelineComponents['fillingLine'], totalWidth);
145
		updateTimelinePosition('next', timelineComponents['eventsWrapper'].find('a.selected'), timelineComponents);
146
 
147
		return totalWidth;
148
	}
149
 
150
	function updateVisibleContent(event, eventsContent) {
151
		var eventDate = event.data('date'),
152
			visibleContent = eventsContent.find('.selected'),
153
			selectedContent = eventsContent.find('[data-date="'+ eventDate +'"]'),
154
			selectedContentHeight = selectedContent.height();
155
 
156
		if (selectedContent.index() > visibleContent.index()) {
157
			var classEnetering = 'selected enter-right',
158
				classLeaving = 'leave-left';
159
		} else {
160
			var classEnetering = 'selected enter-left',
161
				classLeaving = 'leave-right';
162
		}
163
 
164
		selectedContent.attr('class', classEnetering);
165
		visibleContent.attr('class', classLeaving).one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function(){
166
			visibleContent.removeClass('leave-right leave-left');
167
			selectedContent.removeClass('enter-left enter-right');
168
		});
169
		eventsContent.css('height', selectedContentHeight+'px');
170
	}
171
 
172
	function updateOlderEvents(event) {
173
		event.parent('li').prevAll('li').children('a').addClass('older-event').end().end().nextAll('li').children('a').removeClass('older-event');
174
	}
175
 
176
	function getTranslateValue(timeline) {
177
		var timelineStyle = window.getComputedStyle(timeline.get(0), null),
178
			timelineTranslate = timelineStyle.getPropertyValue("-webkit-transform") ||
179
         		timelineStyle.getPropertyValue("-moz-transform") ||
180
         		timelineStyle.getPropertyValue("-ms-transform") ||
181
         		timelineStyle.getPropertyValue("-o-transform") ||
182
         		timelineStyle.getPropertyValue("transform");
183
 
184
        if( timelineTranslate.indexOf('(') >=0 ) {
185
        	var timelineTranslate = timelineTranslate.split('(')[1];
186
    		timelineTranslate = timelineTranslate.split(')')[0];
187
    		timelineTranslate = timelineTranslate.split(',');
188
    		var translateValue = timelineTranslate[4];
189
        } else {
190
        	var translateValue = 0;
191
        }
192
 
193
        return Number(translateValue);
194
	}
195
 
196
	function setTransformValue(element, property, value) {
197
		element.style["-webkit-transform"] = property+"("+value+")";
198
		element.style["-moz-transform"] = property+"("+value+")";
199
		element.style["-ms-transform"] = property+"("+value+")";
200
		element.style["-o-transform"] = property+"("+value+")";
201
		element.style["transform"] = property+"("+value+")";
202
	}
203
 
204
	//based on http://stackoverflow.com/questions/542938/how-do-i-get-the-number-of-days-between-two-dates-in-javascript
205
	function parseDate(events) {
206
		var dateArrays = [];
207
		events.each(function(){
208
			var singleDate = $(this),
209
				dateComp = singleDate.data('date').split('T');
210
			if( dateComp.length > 1 ) { //both DD/MM/YEAR and time are provided
211
				var dayComp = dateComp[0].split('/'),
212
					timeComp = dateComp[1].split(':');
213
			} else if( dateComp[0].indexOf(':') >=0 ) { //only time is provide
214
				var dayComp = ["2000", "0", "0"],
215
					timeComp = dateComp[0].split(':');
216
			} else { //only DD/MM/YEAR
217
				var dayComp = dateComp[0].split('/'),
218
					timeComp = ["0", "0"];
219
			}
220
			var	newDate = new Date(dayComp[2], dayComp[1]-1, dayComp[0], timeComp[0], timeComp[1]);
221
			dateArrays.push(newDate);
222
		});
223
	    return dateArrays;
224
	}
225
 
226
	function daydiff(first, second) {
227
	    return Math.round((second-first));
228
	}
229
 
230
	function minLapse(dates) {
231
		//determine the minimum distance among events
232
		var dateDistances = [];
233
		for (i = 1; i < dates.length; i++) {
234
		    var distance = daydiff(dates[i-1], dates[i]);
235
		    dateDistances.push(distance);
236
		}
237
		return Math.min.apply(null, dateDistances);
238
	}
239
 
240
	/*
241
		How to tell if a DOM element is visible in the current viewport?
242
		http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
243
	*/
244
	function elementInViewport(el) {
245
		var top = el.offsetTop;
246
		var left = el.offsetLeft;
247
		var width = el.offsetWidth;
248
		var height = el.offsetHeight;
249
 
250
		while(el.offsetParent) {
251
		    el = el.offsetParent;
252
		    top += el.offsetTop;
253
		    left += el.offsetLeft;
254
		}
255
 
256
		return (
257
		    top < (window.pageYOffset + window.innerHeight) &&
258
		    left < (window.pageXOffset + window.innerWidth) &&
259
		    (top + height) > window.pageYOffset &&
260
		    (left + width) > window.pageXOffset
261
		);
262
	}
263
 
264
	function checkMQ() {
265
		//check if mobile or desktop device
266
		return window.getComputedStyle(document.querySelector('.cd-horizontal-timeline'), '::before').getPropertyValue('content').replace(/'/g, "").replace(/"/g, "");
267
	}
268
});