1const PLAYING_CLASS = 'playing'; 2const CLICKABLE_CLASS = 'mapcontainer-timeslider-clickable-container'; 3const PRE_MARGIN_CLASS = 'mapcontainer-timeslider-pre-margin'; 4const PRE_TOOLS_CLASS = 'mapcontainer-timeslider-pre-tools'; 5const FRAMES_CONTAINER_CLASS = 'mapcontainer-timeslider-frames-container'; 6const PLAY_BUTTON_CLASS = 'mapcontainer-timeslider-play-button'; 7const POST_TOOLS_CLASS = 'mapcontainer-timeslider-post-tools'; 8const POST_BUTTON_CLASS = 'mapcontainer-timeslider-step-button'; 9const POST_MARGIN_CLASS = 'mapcontainer-timeslider-post-margin'; 10const TIMEZONE_LABEL_CLASS = 'mapcontainer-timeslider-timezone'; 11const FRAME_TICK_CLASS = 'mapcontainer-timeslider-frame-tick'; 12const FRAME_TEXT_WRAPPER_CLASS = 'mapcontainer-timeslider-frame-text-wrapper'; 13const FRAME_TEXT_CLASS = 'mapcontainer-timeslider-frame-text'; 14const POINTER_CLASS = 'mapcontainer-timeslider-pointer'; 15const POINTER_WRAPPER_CLASS = 'mapcontainer-timeslider-pointer-wrapper'; 16const POINTER_TEXT_CLASS = 'mapcontainer-timeslider-pointer-text'; 17const POINTER_HANDLE_CLASS = 'mapcontainer-timeslider-pointer-handle'; 18const INDICATOR_CLASS = 'mapcontainer-timeslider-indicator'; 19const HIDDEN_CLASS = 'mapcontainer-timeslider-hidden'; 20const MENU_CLASS = 'mapcontainer-timeslider-menu'; 21const BEGIN_TIME_CLASS = 'mapcontainer-timeslider-begintime'; 22const END_TIME_CLASS = 'mapcontainer-timeslider-endtime'; 23const TIMESTEP_CLASS = 'mapcontainer-timeslider-timestep'; 24const TIMESTEP_BUTTON_CLASS = 'mapcontainer-timeslider-timestep-button'; 25const TIMESTEP_BUTTON_ACTIVE_CLASS = 'mapcontainer-timeslider-timestep-active-button'; 26const NO_SELECT_CLASS = 'noselect'; 27const BACKWARDS = -1; 28const FORWARDS = 1; 29 30let timeSliderContainer; 31let timeSliderVisible = false; 32let frames_ = []; 33let mouseListeners_ = []; 34// let resizeDetector; 35let paper_ = null; 36let visualPointer_ = null; 37let frameWidth_ = 0; 38let playButton_ = null; 39let frameStatusRects_ = []; 40let timeSlider_animationTime = null; 41let animationPlay_ = false; 42let animationTimer_; 43let previousTickTextTop_ = Number.POSITIVE_INFINITY; 44let previousTickTextRight_ = Number.NEGATIVE_INFINITY; 45let previousTickTextBottom_ = Number.NEGATIVE_INFINITY; 46let previousTickTextLeft_ = Number.POSITIVE_INFINITY; 47let previousTickValue_ = null; 48let previousTickIndex_ = -1; 49let config_; 50 51function timeSlider_initiate(controlConfig) { 52 timeSliderContainer = document.createElement('div'); 53 timeSliderContainer.className = 'mapcontainer-timeslider'; 54 jQuery(timeSliderContainer).appendTo(controlConfig.mapDiv); 55 timeSlider_animationTime = controlConfig.defaultAnimationTime; 56 config_ = controlConfig; 57} 58 59function timeSlider_showTimeSlider() { 60 if (!timeSliderVisible && config_.showTimeSlider) { 61 timeSliderVisible = true; 62 timeSliderContainer.innerHTML = ''; 63 createContainers(); 64 let moments = []; 65 let currentMoment = config_.beginTime; 66 while (currentMoment <= config_.endTime) { 67 moments.push(currentMoment); 68 currentMoment = currentMoment + config_.resolutionTime; 69 } 70 createFrames(moments); 71 createIndicators(); 72 timeSlider_createTicks(); 73 createPointer(); 74 timeSlider_setAnimationTime(timeSlider_animationTime); 75 document.addEventListener('keydown', arrowListener); 76 } 77} 78 79function timeSlider_hideTimeSlider() { 80 timeSliderVisible = false; 81 timeSliderContainer.innerHTML = ''; 82 document.removeEventListener('keydown', arrowListener); 83 mouseListeners_.forEach(mouseListener => { 84 mouseListener.destroy(); 85 }); 86} 87 88function createContainers() { 89 let clickableContainer = document.createElement('div'); 90 clickableContainer.classList.add(CLICKABLE_CLASS); 91 92 clickableContainer.appendChild(createPreMargin()); 93 clickableContainer.appendChild(createPreTools()); 94 95 let momentsContainer = document.createElement('div'); 96 momentsContainer.classList.add(FRAMES_CONTAINER_CLASS); 97 // this.mouseListeners_.push(listen(momentsContainer, 'wheel', event => { 98 // self.step(event.deltaY) 99 // event.preventDefault() 100 // })) 101 clickableContainer.appendChild(momentsContainer); 102 103 // clickableContainer.appendChild(this.createPostTools()); 104 105 let postMargin = document.createElement('div'); 106 postMargin.classList.add(POST_MARGIN_CLASS); 107 // this.mouseListeners_.push(listen(postMargin, 'click', event => { 108 // self.step(TimeSlider.FORWARDS) 109 // }))
110 clickableContainer.appendChild(postMargin); 111 112 timeSliderContainer.appendChild(clickableContainer); 113 114 timeSliderContainer.classList.add('noselect'); 115 116 // this.resizeDetector.listenTo(this.container_, function (element) { 117 // self.createTicks() 118 // }) 119} 120 121function createPreMargin() { 122 let preMargin = document.createElement('div'); 123 preMargin.classList.add(PRE_MARGIN_CLASS); 124 // this.mouseListeners_.push(listen(preMargin, 'click', () => { 125 // self.step(BACKWARDS) 126 // })) 127 return preMargin; 128} 129 130function createFrames(moments) { 131 let i; 132 let timePeriod; 133 const numMoments = moments.length; 134 const currentTime = Date.now(); 135 let beginTime; 136 let endTime; 137 let type; 138 let weight; 139 let timeFrame; 140 let framesContainer = timeSliderContainer.getElementsByClassName(FRAMES_CONTAINER_CLASS)[0]; 141 framesContainer.innerHTML = ''; 142 if (numMoments < 2) { 143 return; 144 } 145 timePeriod = moments[numMoments - 1] - moments[0]; 146 147 frames_ = []; 148 for (i = 0; i < numMoments; i++) { 149 beginTime = i === 0 ? 2 * moments[0] - moments[1] : moments[i - 1]; 150 endTime = moments[i]; 151 type = moments[i] <= currentTime ? 'history' : 'future'; 152 weight = (100 * (endTime - beginTime)) / timePeriod; 153 timeFrame = createFrame(beginTime, endTime, type, weight); 154 framesContainer.appendChild(timeFrame['element']); 155 frames_.push(timeFrame); 156 } 157} 158 159function createFrame(beginTime, endTime, type, weight) { 160 // DOM element 161 const element = document.createElement('div'); 162 element.style['flex-grow'] = weight; 163 element.classList.add(type); 164 element.dataset.time = endTime; 165 const dragListenerElement = document.createElement('div'); 166 dragListenerElement.classList.add('mapcontainer-timeslider-drag-listener'); 167 element.appendChild(dragListenerElement); 168 element.classList.add('mapcontainer-timeslider-frame'); 169 170 // click listener 171 function clickListener(event) { 172 timeSlider_setAnimationTime(endTime); 173 symbology_refreshTimeLayers(); 174 } 175 element.addEventListener('click', clickListener); 176 mouseListeners_.push({ 177 destroy: function () { 178 element.removeEventListener('click', clickListener); 179 } 180 }); 181 182 return { 183 beginTime: beginTime, 184 endTime: endTime, 185 useDateFormat: false, 186 element: element 187 }; 188} 189 190function createIndicators() { 191 frames_.forEach((frame, index, array) => { 192 let indicator = document.createElement('div'); 193 indicator.classList.add(INDICATOR_CLASS); 194 indicator.setAttribute('id', 'timeSliderIndicator_' + frame['endTime'].valueOf()); 195 if (index === 0) { 196 indicator.classList.add('first'); 197 } else if (index === array.length - 1) { 198 indicator.classList.add('last'); 199 } 200 frame.element.appendChild(indicator); 201 }); 202} 203 204function timeSlider_createTicks() { 205 let self = this; 206 let tick; 207 let maxTextWidth = 0; 208 let newTextWidth; 209 let useTimeStep = false; 210 let timeStep; 211 let framesContainer; 212 let previousTickTextRight_ = Number.NEGATIVE_INFINITY; 213 214 let clearFrame = frame => { 215 let removeChildrenByClass = className => { 216 Array.from(frame.element.getElementsByClassName(className)).forEach(element => { 217 element.parentElement.removeChild(element); 218 }); 219 }; 220 removeChildrenByClass(FRAME_TEXT_WRAPPER_CLASS); 221 removeChildrenByClass(FRAME_TICK_CLASS); 222 }; 223 224 frames_.forEach((frame, index, frames) => { 225 let tickText; 226 let textWrapperElement; 227 let textElement; 228 let textDayElement; 229 230 clearFrame(frame); 231 232 textWrapperElement = document.createElement('div'); 233 textWrapperElement.classList.add(FRAME_TEXT_WRAPPER_CLASS); 234 235 tickText = getTickText(frame['endTime']); 236 237 /* TODO: päivä yläpuolelle 238 textDayElement = document.createElement('span') 239 textDayElement.classList.add(TimeSlider.FRAME_TEXT_CLASS) 240 textDayElement.classList.add(constants.NO_SELECT_CLASS) 241 textDayElement.textContent = tickText['day'] 242 textWrapperElement.appendChild(textDayElement) 243 244 textWrapperElement.appendChild(document.createElement('br')) 245 */ 246 247 textElement = document.createElement('span'); 248 textElement.setAttribute('id', 'tickLabel_' + frame['endTime'].valueOf()); 249 textElement.classList.add(FRAME_TEXT_CLASS); 250 textElement.classList.add(NO_SELECT_CLASS); 251 let tickTextArray = tickText['content'].split(' '); 252 textElement.setAttribute('data-args', tickText['weekDay'] + ',' + tickTextArray[1]); 253 textElement.classList.add('translate'); 254 textElement.textContent = tickText['content']; 255 textWrapperElement.appendChild(textElement); 256 257 // click on label? 258 // let self = this; 259 // this.mouseListeners_.push(listen(textElement, 'click', (event) => { 260 // self.variableEvents.emitEvent('animationTime', [frame['endTime']]
260) 261 // })) 262 263 frame['useDateFormat'] = tickText['useDateFormat']; 264 265 frame.element.appendChild(textWrapperElement); 266 frame.element.style.display = 'none'; 267 }); 268 269 // Separate loops to prevent accessing textElement width before it is available 270 frames_.forEach((frame, index, frames) => { 271 let clientRect; 272 let localTimeStep; 273 let textElement; 274 let nextIndex = index + 1; 275 frame.element.style.display = ''; 276 if (nextIndex === frames.length) { 277 return; 278 } 279 textElement = frame.element.querySelector('span.' + FRAME_TEXT_CLASS); 280 clientRect = textElement.getBoundingClientRect(); 281 if (maxTextWidth < clientRect['width']) { 282 maxTextWidth = clientRect['width']; 283 } 284 285 localTimeStep = frames[nextIndex]['endTime'] - frame['endTime']; 286 if (timeStep == null) { 287 useTimeStep = true; 288 timeStep = localTimeStep; 289 } else if (useTimeStep && localTimeStep !== timeStep) { 290 useTimeStep = false; 291 } 292 }); 293 // Prevent common tick asynchrony 294 if (useTimeStep) { 295 timeStep *= 2; 296 } 297 298 newTextWidth = Math.round(maxTextWidth) + 'px'; 299 Array.from(document.getElementsByClassName(FRAME_TEXT_WRAPPER_CLASS)).forEach(element => { 300 element.style.width = newTextWidth; 301 }); 302 303 let createTick = (frame, index, rect, endTime) => { 304 previousTickTextTop_ = rect.top; 305 previousTickTextRight_ = rect.right; 306 previousTickTextBottom_ = rect.bottom; 307 previousTickTextLeft_ = rect.left; 308 previousTickValue_ = endTime; 309 previousTickIndex_ = index; 310 tick = document.createElement('div'); 311 tick.classList.add(FRAME_TICK_CLASS); 312 tick.classList.add(HIDDEN_CLASS); 313 frame.element.appendChild(tick); 314 }; 315 316 framesContainer = Array.from(document.getElementsByClassName(FRAMES_CONTAINER_CLASS)); 317 if (framesContainer.length > 0) { 318 framesContainer = framesContainer[0].getBoundingClientRect(); 319 } 320 321 frames_.forEach((frame, index, frames) => { 322 let textWrapper; 323 let clientRect; 324 let textElementArray = Array.from(frame.element.getElementsByClassName(FRAME_TEXT_WRAPPER_CLASS)); 325 if (textElementArray.length === 0) { 326 return; 327 } 328 textWrapper = textElementArray.shift(); 329 clientRect = textWrapper.getBoundingClientRect(); 330 331 // Prevent text overlapping, simply place those that fit 332 if ( 333 framesContainer.length === 0 || 334 (framesContainer.left <= clientRect.left && 335 framesContainer.right >= clientRect.right && 336 framesContainer.top <= clientRect.top && 337 framesContainer.bottom >= clientRect.bottom) 338 ) { 339 if ( 340 previousTickTextRight_ < clientRect.left || 341 previousTickTextLeft_ > clientRect.right || 342 previousTickTextBottom_ < clientRect.top || 343 previousTickTextTop_ > clientRect.bottom 344 ) { 345 createTick(frame, index, clientRect, frame['endTime']); 346 } else { 347 frame.element.removeChild(textWrapper); 348 // console.debug('Time slider tick measures:') 349 // console.debug(`${previousTickTextRight_} < ${clientRect.left}`); 350 // console.debug(`${previousTickTextLeft_} > ${clientRect.right}`); 351 // console.debug(`${previousTickTextBottom_} < ${clientRect.top}`); 352 // console.debug(`${previousTickTextTop_} > ${clientRect.bottom}`); 353 } 354 } else { 355 frame.element.removeChild(textWrapper); 356 } 357 }); 358 359 Array.from(document.getElementsByClassName(FRAME_TICK_CLASS)).forEach(element => { 360 element.classList.remove(HIDDEN_CLASS); 361 }); 362} 363 364/** 365 * Generate text presentation of the given time. 366 * @param {number} tickTime Time value. 367 * @param {boolean} showDate Show date information. 368 * @return {string} Generated text presentation. 369 */ 370function getTickText(tickTime, showDate = true, showWeekDay = true) { 371 let zTime; 372 let numFrames; 373 let i; 374 let frameTime; 375 let prevTime; 376 let zPrevTime; 377 let day; 378 let year;
379 let currentMoment; 380 let format = 'HH:mm'; 381 let dateFormat = 'D.M.'; 382 let dateFormatDay = 'dd'; 383 if (showWeekDay) { 384 dateFormat = 'dd D.M.'; 385 } 386 /* 387 if (showWeekDay) { 388 dateFormat = 'dd' 389 } 390 */ 391 let useDateFormat = false; 392 let beginTime = frames_.length > 0 ? frames_[0]['endTime'] : Number.NEGATIVE_INFINITY; 393 if (beginTime == null) { 394 return ''; 395 } 396 if (tickTime < beginTime) { 397 tickTime = beginTime; 398 } 399 //ei käytetä moment-kirjastoa 400 // moment.locale(jQuery.i18n().locale) 401 // zTime = moment(tickTime).tz(this.timeZone_) 402 // day = zTime.dayOfYear() 403 // year = zTime.year() 404 const tickDate = new Date(tickTime); 405 406 // JÃTETTY POIS, TARKOITUS EPÃSELVà (S.NEUVONEN/SYKE) 407 // if (showDate) { 408 // numFrames = frames_.length 409 // for (i = 0; i < numFrames; i++) { 410 // frameTime = frames_[i]['endTime'] 411 // if (frameTime >= tickTime) { 412 // break 413 // } 414 // if (Array.from(frames_[i].element.getElementsByClassName(TimeSlider.FRAME_TEXT_WRAPPER_CLASS)).length > 0) { 415 // prevTime = frameTime 416 // } 417 // } 418 // currentMoment = moment().tz(this.timeZone_) 419 // if (prevTime != null) { 420 // zPrevTime = moment(prevTime).tz(this.timeZone_) 421 // if ((day !== zPrevTime.dayOfYear()) || (year !== zPrevTime.year())) { 422 // useDateFormat = true 423 // } 424 // } else if ((tickTime === beginTime) && ((day !== currentMoment.tz(this.timeZone_).dayOfYear()) || (year !== currentMoment.tz(this.timeZone_).year()))) { 425 // useDateFormat = true 426 // } 427 // } 428 useDateFormat = true; 429 let weekDayArray = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; 430 switch (jQuery.i18n().locale) { 431 case 'fi': 432 weekDayArray = ['Su', 'Ma', 'Ti', 'Ke', 'To', 'Pe', 'La']; 433 break; 434 case 'sv': 435 weekDayArray = ['Sö', 'MÃ¥', 'Ti', 'On', 'Th', 'Fr', 'Lö']; 436 break; 437 default: 438 break; 439 } 440 return { 441 day: tickDate.getDate(), 442 content: showWeekDay 443 ? `${weekDayArray[tickDate.getDay()]} ${tickDate.getDate()}.${tickDate.getMonth() + 1}.` 444 : `${tickDate.getDate()}.${tickDate.getMonth() + 1}.`, 445 useDateFormat: useDateFormat, 446 weekDay: weekDayArray[tickDate.getDay()] 447 }; 448} 449 450function createPointer() { 451 let pointerContainer = document.createElement('div'); 452 pointerContainer.classList.add('pointerContainerDiv'); 453 454 let pointer = document.createElement('div'); 455 pointer.classList.add(POINTER_CLASS); 456 457 let textContainer = document.createElement('div'); 458 textContainer.classList.add(POINTER_WRAPPER_CLASS); 459 460 let textItem = document.createElement('span'); 461 textItem.classList.add(POINTER_TEXT_CLASS); 462 textItem.classList.add('noselect'); 463 textItem.innerHTML = ''; 464 465 textContainer.appendChild(textItem); 466 pointer.appendChild(textContainer); 467 468 let handle = document.createElement('div'); 469 handle.classList.add(POINTER_HANDLE_CLASS); 470 pointer.appendChild(handle); 471 // this.mouseListeners_.push(listen(pointer, 'mousedown', e => { 472 // self.dragging_ = true 473 // })) 474 // this.mouseListeners_.push(listen(pointer, 'touchstart', e => { 475 // self.dragging_ = true 476 // })) 477 478 pointerContainer.appendChild(pointer); 479 480 visualPointer_ = pointerContainer; 481} 482 483function createPreTools() { 484 let preTools = document.createElement('div'); 485 preTools.classList.add(PRE_TOOLS_CLASS); 486 487 let playButton = document.createElement('div'); 488 playButton.classList.add(PLAY_BUTTON_CLASS); 489 if (animationPlay_) { 490 playButton.classList.add(PLAYING_CLASS); 491 } 492 493 function clickListener(event) { 494 timeSlider_setAnimationPlay(!animationPlay_); 495 } 496 playButton.addEventListener('click', clickListener); 497 mouseListeners_.push({ 498 destroy: function () { 499 playButton.removeEventListener('click', clickListener); 500 } 501 });
502 503 playButton_ = playButton; 504 preTools.appendChild(playButton); 505 return preTools; 506} 507 508function timeSlider_setAnimationTime(animationTime) { 509 timeSlider_animationTime = animationTime; 510 updatePointer(animationTime); 511} 512 513function updatePointer(animationTime) { 514 if (visualPointer_ == null) { 515 return; 516 } 517 let numFrames = frames_.length; 518 let i; 519 let index; 520 let needsUpdate; 521 for (i = 0; i < numFrames; i++) { 522 if (animationTime <= frames_[i]['endTime']) { 523 index = i; 524 break; 525 } 526 } 527 if (index != null) { 528 if (visualPointer_.parentElement == null) { 529 needsUpdate = true; 530 } else if (Number.parseInt(visualPointer_.parentElement.dataset['time']) !== animationTime) { 531 visualPointer_.parentElement.removeChild(visualPointer_); 532 needsUpdate = true; 533 } 534 if (needsUpdate) { 535 frames_[index].element.appendChild(visualPointer_); 536 Array.from(visualPointer_.getElementsByClassName(POINTER_TEXT_CLASS)).forEach(textElement => { 537 //textElement.innerHTML = this.getTickText(frames_[index]['endTime'], false)['content'] 538 textElement.innerHTML = getTickText(frames_[index]['endTime'], controlConfig['showTickAsDate'], false)['content']; 539 console.debug(`timeslider pointer set to ${textElement.innerHTML}`); 540 }); 541 } 542 } 543} 544 545function step(direction) { 546 // get current frame index 547 let numFrames = frames_.length; 548 let i; 549 let index; 550 for (i = 0; i < numFrames; i++) { 551 if (timeSlider_animationTime <= frames_[i]['endTime']) { 552 index = i; 553 break; 554 } 555 } 556 if (index === null) { 557 for (i = 0; i < numFrames; i++) { 558 if (config_.defaultAnimationTime <= frames_[i]['endTime']) { 559 index = i; 560 break; 561 } 562 } 563 } 564 // step animation time 565 if (index !== null) { 566 if (direction > 0) { 567 index++; 568 index = index > numFrames - 1 ? 0 : index; // loop 569 } else if (direction < 0) { 570 index--; 571 index = index < 0 ? 0 : index; 572 } 573 timeSlider_setAnimationTime(frames_[index]['endTime']); 574 symbology_refreshTimeLayers(); 575 } 576} 577 578function arrowListener(event) { 579 let keyCode = event.which || event.keyCode; 580 switch (keyCode) { 581 case 39: //right arrow 582 step(1); 583 break; 584 case 37: //left arrow 585 step(-1); 586 break; 587 } 588} 589 590function timeSlider_setAnimationPlay(animationPlay) { 591 if (animationPlay_ === animationPlay) return; 592 try { 593 animationPlay_ = animationPlay; 594 if (animationPlay_) { 595 playButton_.classList.add(PLAYING_CLASS); 596 animationTimer_ = setInterval(function stepForward() { 597 step(1); 598 }, config_.frameRate); 599 } else { 600 playButton_.classList.remove(PLAYING_CLASS); 601 clearInterval(animationTimer_); 602 } 603 } catch (error) { 604 console.warn(`[timeSlider_setAnimationPlay] ${error.toString()}`); 605 } 606}
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.