1jQuery(document).ready(function($){ 2 var timelines = $('.cd-horizontal-timeline'), 3 eventsMinDistance = 80; 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});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.