@knight-lab/timelinejs 3.8.21 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/.nvmrc +1 -1
  2. package/API_TEST.html +0 -5
  3. package/CHANGELOG.md +6 -0
  4. package/dist/css/fonts/font.amatic-andika.css +1 -1
  5. package/dist/css/fonts/font.bitter-raleway.css +4 -4
  6. package/dist/css/fonts/font.clicker-garamond.css +1 -1
  7. package/dist/css/fonts/font.dancing-ledger.css +2 -2
  8. package/dist/css/fonts/font.oldstandard.css +2 -2
  9. package/dist/css/fonts/font.opensans-gentiumbook.css +2 -2
  10. package/dist/css/fonts/font.playfair-faunaone.css +2 -2
  11. package/dist/css/fonts/font.playfair.css +2 -2
  12. package/dist/css/fonts/font.roboto-megrim.css +2 -2
  13. package/dist/css/fonts/font.unicaone-vollkorn.css +2 -2
  14. package/dist/css/icons/tl-icons.eot +0 -0
  15. package/dist/css/icons/tl-icons.svg +58 -55
  16. package/dist/css/icons/tl-icons.ttf +0 -0
  17. package/dist/css/icons/tl-icons.woff +0 -0
  18. package/dist/css/icons/tl-icons.woff2 +0 -0
  19. package/dist/css/themes/timeline.theme.contrast.css +157 -319
  20. package/dist/css/themes/timeline.theme.dark.css +146 -308
  21. package/dist/css/timeline.css +155 -317
  22. package/dist/css/timeline.css.map +1 -1
  23. package/dist/embed/compare.html +4 -4
  24. package/dist/embed/index.html +3 -6
  25. package/dist/embed/old-index.html +2 -5
  26. package/dist/embed/popular_timelines.json +5989 -1
  27. package/dist/js/locale/af.json +54 -56
  28. package/dist/js/locale/ar.json +54 -56
  29. package/dist/js/locale/be.json +71 -73
  30. package/dist/js/locale/bg.json +54 -56
  31. package/dist/js/locale/ca.json +54 -56
  32. package/dist/js/locale/cz.json +56 -63
  33. package/dist/js/locale/da.json +54 -56
  34. package/dist/js/locale/de.json +1 -3
  35. package/dist/js/locale/el.json +54 -56
  36. package/dist/js/locale/en-24hr.json +51 -57
  37. package/dist/js/locale/en-week.json +51 -57
  38. package/dist/js/locale/en.json +3 -8
  39. package/dist/js/locale/eo.json +54 -56
  40. package/dist/js/locale/es.json +55 -57
  41. package/dist/js/locale/et.json +54 -56
  42. package/dist/js/locale/eu.json +54 -56
  43. package/dist/js/locale/fa.json +52 -54
  44. package/dist/js/locale/fi.json +73 -75
  45. package/dist/js/locale/fo.json +54 -56
  46. package/dist/js/locale/fr.json +1 -8
  47. package/dist/js/locale/fy.json +71 -73
  48. package/dist/js/locale/ga.json +54 -56
  49. package/dist/js/locale/gl.json +54 -56
  50. package/dist/js/locale/he.json +54 -56
  51. package/dist/js/locale/hi.json +72 -74
  52. package/dist/js/locale/hr.json +54 -56
  53. package/dist/js/locale/hu.json +91 -93
  54. package/dist/js/locale/hy.json +54 -56
  55. package/dist/js/locale/id.json +54 -56
  56. package/dist/js/locale/is.json +54 -56
  57. package/dist/js/locale/it.json +18 -20
  58. package/dist/js/locale/iw.json +54 -56
  59. package/dist/js/locale/ja.json +54 -56
  60. package/dist/js/locale/ka.json +54 -56
  61. package/dist/js/locale/ko.json +54 -56
  62. package/dist/js/locale/lb.json +54 -56
  63. package/dist/js/locale/lt.json +54 -56
  64. package/dist/js/locale/lv.json +54 -56
  65. package/dist/js/locale/ms.json +54 -56
  66. package/dist/js/locale/my.json +31 -38
  67. package/dist/js/locale/nb.json +54 -56
  68. package/dist/js/locale/ne.json +54 -56
  69. package/dist/js/locale/nl.json +54 -56
  70. package/dist/js/locale/nn.json +54 -56
  71. package/dist/js/locale/no.json +54 -56
  72. package/dist/js/locale/pl.json +54 -56
  73. package/dist/js/locale/pt-br.json +45 -47
  74. package/dist/js/locale/pt.json +57 -59
  75. package/dist/js/locale/rm.json +54 -56
  76. package/dist/js/locale/ro.json +54 -56
  77. package/dist/js/locale/ru.json +54 -56
  78. package/dist/js/locale/si.json +53 -55
  79. package/dist/js/locale/sk.json +54 -56
  80. package/dist/js/locale/sl.json +54 -56
  81. package/dist/js/locale/sr-cy.json +54 -56
  82. package/dist/js/locale/sr.json +54 -56
  83. package/dist/js/locale/sv.json +11 -13
  84. package/dist/js/locale/ta.json +54 -56
  85. package/dist/js/locale/te.json +53 -55
  86. package/dist/js/locale/th.json +55 -62
  87. package/dist/js/locale/tl.json +54 -56
  88. package/dist/js/locale/tr.json +54 -56
  89. package/dist/js/locale/uk.json +54 -56
  90. package/dist/js/locale/ur.json +2 -9
  91. package/dist/js/locale/vi.json +33 -35
  92. package/dist/js/locale/zh-cn.json +1 -3
  93. package/dist/js/locale/zh-tw.json +54 -56
  94. package/dist/js/timeline.js +2 -3
  95. package/dist/js/timeline.js.LICENSE.txt +1 -0
  96. package/dist/js/timeline.js.map +1 -1
  97. package/package.json +20 -18
  98. package/src/css/icons/icons.html +49 -1
  99. package/src/css/icons/tl-icons.eot +0 -0
  100. package/src/css/icons/tl-icons.svg +58 -55
  101. package/src/css/icons/tl-icons.ttf +0 -0
  102. package/src/css/icons/tl-icons.woff +0 -0
  103. package/src/css/icons/tl-icons.woff2 +0 -0
  104. package/src/embed/compare.html +4 -4
  105. package/src/embed/index.html +3 -6
  106. package/src/embed/old-index.html +2 -5
  107. package/src/embed/popular_timelines.json +5989 -1
  108. package/src/js/core/TimelineConfig.js +0 -1
  109. package/src/js/dom/DOMUtil.js +12 -12
  110. package/src/js/language/I18NMixins.js +13 -3
  111. package/src/js/language/Language.js +50 -23
  112. package/src/js/language/__tests__/Language.test.js +32 -0
  113. package/src/js/language/locale/af.json +54 -56
  114. package/src/js/language/locale/ar.json +54 -56
  115. package/src/js/language/locale/be.json +71 -73
  116. package/src/js/language/locale/bg.json +54 -56
  117. package/src/js/language/locale/ca.json +54 -56
  118. package/src/js/language/locale/cz.json +56 -63
  119. package/src/js/language/locale/da.json +54 -56
  120. package/src/js/language/locale/de.json +1 -3
  121. package/src/js/language/locale/el.json +54 -56
  122. package/src/js/language/locale/en-24hr.json +51 -57
  123. package/src/js/language/locale/en-week.json +51 -57
  124. package/src/js/language/locale/en.json +3 -8
  125. package/src/js/language/locale/eo.json +54 -56
  126. package/src/js/language/locale/es.json +55 -57
  127. package/src/js/language/locale/et.json +54 -56
  128. package/src/js/language/locale/eu.json +54 -56
  129. package/src/js/language/locale/fa.json +52 -54
  130. package/src/js/language/locale/fi.json +73 -75
  131. package/src/js/language/locale/fo.json +54 -56
  132. package/src/js/language/locale/fr.json +1 -8
  133. package/src/js/language/locale/fy.json +71 -73
  134. package/src/js/language/locale/ga.json +54 -56
  135. package/src/js/language/locale/gl.json +54 -56
  136. package/src/js/language/locale/he.json +54 -56
  137. package/src/js/language/locale/hi.json +72 -74
  138. package/src/js/language/locale/hr.json +54 -56
  139. package/src/js/language/locale/hu.json +91 -93
  140. package/src/js/language/locale/hy.json +54 -56
  141. package/src/js/language/locale/id.json +54 -56
  142. package/src/js/language/locale/is.json +54 -56
  143. package/src/js/language/locale/it.json +18 -20
  144. package/src/js/language/locale/iw.json +54 -56
  145. package/src/js/language/locale/ja.json +54 -56
  146. package/src/js/language/locale/ka.json +54 -56
  147. package/src/js/language/locale/ko.json +54 -56
  148. package/src/js/language/locale/lb.json +54 -56
  149. package/src/js/language/locale/lt.json +54 -56
  150. package/src/js/language/locale/lv.json +54 -56
  151. package/src/js/language/locale/ms.json +54 -56
  152. package/src/js/language/locale/my.json +31 -38
  153. package/src/js/language/locale/nb.json +54 -56
  154. package/src/js/language/locale/ne.json +54 -56
  155. package/src/js/language/locale/nl.json +54 -56
  156. package/src/js/language/locale/nn.json +54 -56
  157. package/src/js/language/locale/no.json +54 -56
  158. package/src/js/language/locale/pl.json +54 -56
  159. package/src/js/language/locale/pt-br.json +45 -47
  160. package/src/js/language/locale/pt.json +57 -59
  161. package/src/js/language/locale/rm.json +54 -56
  162. package/src/js/language/locale/ro.json +54 -56
  163. package/src/js/language/locale/ru.json +54 -56
  164. package/src/js/language/locale/si.json +53 -55
  165. package/src/js/language/locale/sk.json +54 -56
  166. package/src/js/language/locale/sl.json +54 -56
  167. package/src/js/language/locale/sr-cy.json +54 -56
  168. package/src/js/language/locale/sr.json +54 -56
  169. package/src/js/language/locale/sv.json +11 -13
  170. package/src/js/language/locale/ta.json +54 -56
  171. package/src/js/language/locale/te.json +53 -55
  172. package/src/js/language/locale/th.json +55 -62
  173. package/src/js/language/locale/tl.json +54 -56
  174. package/src/js/language/locale/tr.json +54 -56
  175. package/src/js/language/locale/uk.json +54 -56
  176. package/src/js/language/locale/ur.json +2 -9
  177. package/src/js/language/locale/vi.json +33 -35
  178. package/src/js/language/locale/zh-cn.json +1 -3
  179. package/src/js/language/locale/zh-tw.json +54 -56
  180. package/src/js/media/Media.js +6 -2
  181. package/src/js/media/MediaType.js +1 -1
  182. package/src/js/media/types/DailyMotion.js +9 -1
  183. package/src/js/media/types/Text.js +33 -31
  184. package/src/js/media/types/Vimeo.js +58 -57
  185. package/src/js/slider/Slide.js +17 -2
  186. package/src/js/slider/SlideNav.js +12 -7
  187. package/src/js/slider/StorySlider.js +8 -12
  188. package/src/js/timeline/Timeline.js +47 -13
  189. package/src/js/timenav/TimeAxis.js +20 -1
  190. package/src/js/timenav/TimeMarker.js +48 -3
  191. package/src/js/timenav/TimeNav.js +140 -15
  192. package/src/js/ui/MenuBar.js +206 -149
  193. package/src/js/ui/Swipable.js +1 -1
  194. package/src/less/TL.Timeline.Base.less +38 -1
  195. package/src/less/Typography.less +2 -3
  196. package/src/less/Variables.less +4 -1
  197. package/src/less/icons/Icons.less +9 -0
  198. package/src/less/media/types/TL.Media.Text.less +5 -0
  199. package/src/less/slider/TL.Slide.less +40 -45
  200. package/src/less/slider/TL.SlideNav.less +82 -55
  201. package/src/less/themes/contrast/Variables.less +7 -4
  202. package/src/less/themes/dark/TL.Theme.Dark.less +2 -2
  203. package/src/less/themes/dark/Variables.less +4 -1
  204. package/src/less/timenav/TL.TimeMarker.less +16 -10
  205. package/src/less/timenav/TL.TimeNav.less +2 -50
  206. package/src/less/ui/TL.MenuBar.Button.less +8 -6
  207. package/src/template/all-media-types.json +52 -2
  208. package/src/template/index.html +2 -1
  209. package/webpack.common.js +13 -10
  210. package/webpack.dev.js +16 -6
  211. package/dist/js/timeline-min.js +0 -4
  212. package/dist/timeline3.zip +0 -0
  213. package/src/less/core/Reset.less +0 -115
@@ -1,4 +1,4 @@
1
- import { classMixin, mergeData, unlinkify } from "../core/Util"
1
+ import { classMixin, mergeData, trim, unlinkify } from "../core/Util"
2
2
  import Events from "../core/Events"
3
3
  import { DOMMixins } from "../dom/DOMMixins"
4
4
  import { DOMEvent } from "../dom/DOMEvent"
@@ -9,6 +9,7 @@ import { webkit as BROWSER_WEBKIT } from "../core/Browser";
9
9
  import { easeInSpline } from "../animation/Ease";
10
10
 
11
11
  import { lookupMediaType } from "../media/MediaType"
12
+ import { I18NMixins } from "../language/I18NMixins";
12
13
 
13
14
  export class TimeMarker {
14
15
  constructor(data, options) {
@@ -75,6 +76,9 @@ export class TimeMarker {
75
76
  // End date
76
77
  this.has_end_date = false;
77
78
 
79
+ // Alternative text
80
+ this.ariaLabel = '';
81
+
78
82
  // Merge Data and Options
79
83
  mergeData(this.options, options);
80
84
  mergeData(this.data, data);
@@ -107,8 +111,19 @@ export class TimeMarker {
107
111
  } else {
108
112
  this._el.container.className = 'tl-timemarker';
109
113
  }
114
+
115
+ this._el.container.ariaLabel = this.ariaLabel;
116
+ if (this.active) {
117
+ this._el.container.ariaLabel += ', shown';
118
+ } else {
119
+ this._el.container.ariaLabel += ', press space to show';
120
+ }
110
121
  }
111
122
 
123
+ setFocus(options = { preventScroll: true }) {
124
+ this._el.container.focus(options);
125
+ }
126
+
112
127
  addTo(container) {
113
128
  container.appendChild(this._el.container);
114
129
  }
@@ -216,17 +231,44 @@ export class TimeMarker {
216
231
  this._el.timespan.style.height = remainder + "px";
217
232
  }
218
233
 
234
+ getFormattedDate() {
235
+ if (trim(this.data.display_date).length > 0) {
236
+ return this.data.display_date;
237
+ }
238
+
239
+ let date_text = "";
240
+ if (this.data.end_date) {
241
+ date_text = " to " + this.data.end_date.getDisplayDate(this.getLanguage());
242
+ }
243
+ if (this.data.start_date) {
244
+ date_text = (date_text ? "from " : "") + this.data.start_date.getDisplayDate(this.getLanguage()) + date_text;
245
+ }
246
+ return date_text;
247
+ }
248
+
219
249
  /* Events
220
250
  ================================================== */
221
251
  _onMarkerClick(e) {
222
252
  this.fire("markerclick", { unique_id: this.data.unique_id });
223
253
  }
224
254
 
255
+ _onMarkerKeydown(e) {
256
+ if (/Space|Enter/.test(e.code)) {
257
+ this.fire("markerclick", { unique_id: this.data.unique_id });
258
+ }
259
+ }
260
+
261
+ _onMarkerBlur(e) {
262
+ this.fire("markerblur", { unique_id: this.data.unique_id });
263
+ }
264
+
225
265
  /* Private Methods
226
266
  ================================================== */
227
267
  _initLayout() {
228
268
  // Create Layout
229
269
  this._el.container = DOM.create("div", "tl-timemarker");
270
+ this._el.container.setAttribute('tabindex', '-1');
271
+
230
272
  if (this.data.unique_id) {
231
273
  this._el.container.id = this.data.unique_id + "-marker";
232
274
  }
@@ -277,7 +319,8 @@ export class TimeMarker {
277
319
  this._text.innerHTML = unlinkify(this.data.media.caption);
278
320
  }
279
321
 
280
-
322
+ const date = this.getFormattedDate();
323
+ this.ariaLabel = `${this._text.innerHTML}, ${date}`;
281
324
 
282
325
  // Fire event that the slide is loaded
283
326
  this.onLoaded();
@@ -286,6 +329,8 @@ export class TimeMarker {
286
329
 
287
330
  _initEvents() {
288
331
  DOMEvent.addListener(this._el.container, 'click', this._onMarkerClick, this);
332
+ DOMEvent.addListener(this._el.container, 'keydown', this._onMarkerKeydown, this);
333
+ DOMEvent.addListener(this._el.container, 'blur', this._onMarkerBlur, this);
289
334
  }
290
335
 
291
336
  // Update Display
@@ -304,4 +349,4 @@ export class TimeMarker {
304
349
  }
305
350
 
306
351
 
307
- classMixin(TimeMarker, Events, DOMMixins)
352
+ classMixin(TimeMarker, I18NMixins, Events, DOMMixins)
@@ -11,13 +11,14 @@ import { TimeAxis } from "./TimeAxis"
11
11
  import { TimeMarker } from "./TimeMarker"
12
12
  import Swipable from "../ui/Swipable"
13
13
  import { Animate } from "../animation/Animate"
14
+ import { I18NMixins } from "../language/I18NMixins"
14
15
 
15
16
 
16
17
 
17
18
  export class TimeNav {
18
19
 
19
20
  constructor(elem, timeline_config, options, language) {
20
- this.language = language // just passing through for TimeAxis. Is this a bad code smell?
21
+ this.language = language
21
22
  // DOM ELEMENTS
22
23
  this._el = {
23
24
  parent: {},
@@ -29,8 +30,7 @@ export class TimeNav {
29
30
  marker_container: {},
30
31
  marker_item_container: {},
31
32
  timeaxis: {},
32
- timeaxis_background: {},
33
- attribution: {}
33
+ timeaxis_background: {}
34
34
  };
35
35
 
36
36
  this.collapsed = false;
@@ -40,6 +40,14 @@ export class TimeNav {
40
40
  } else {
41
41
  this._el.container = DOM.get(elem);
42
42
  }
43
+ this._el.container.setAttribute('tabindex', '0');
44
+
45
+ // 'application' role supports predictable control of keyboard input in a complex component
46
+ this._el.container.setAttribute('role', 'application');
47
+ this._el.container.setAttribute('aria-label', this._('aria_label_timeline_navigation'));
48
+ this._el.container.setAttribute('aria-description',
49
+ 'Navigate between markers with arrow keys. Press "Home" for the first and "End" for the last markers'
50
+ );
43
51
 
44
52
  this.config = timeline_config;
45
53
 
@@ -81,6 +89,9 @@ export class TimeNav {
81
89
  // Current Marker
82
90
  this.current_id = "";
83
91
 
92
+ // Current Focused Marker
93
+ this.current_focused_id = "";
94
+
84
95
  // TimeScale
85
96
  this.timescale = {};
86
97
 
@@ -365,7 +376,13 @@ export class TimeNav {
365
376
  _resetMarkersActive() {
366
377
  for (var i = 0; i < this._markers.length; i++) {
367
378
  this._markers[i].setActive(false);
368
- };
379
+ }
380
+ }
381
+
382
+ _resetMarkersBlurListeners() {
383
+ for (var i = 0; i < this._markers.length; i++) {
384
+ this._markers[i].off('markerblur', this._onMarkerBlur, this);
385
+ }
369
386
  }
370
387
 
371
388
  _findMarkerIndex(n) {
@@ -459,6 +476,56 @@ export class TimeNav {
459
476
  if (n >= 0 && n < this._markers.length) {
460
477
  this._markers[n].setActive(true);
461
478
  }
479
+
480
+ this.animateMovement(_n, fast, css_animation, _duration, _ease);
481
+
482
+ if (n >= 0 && n < this._markers.length) {
483
+ this.current_id = this.current_focused_id = this._markers[n].data.unique_id;
484
+ } else {
485
+ this.current_id = this.current_focused_id = '';
486
+ }
487
+
488
+ this._setLabelWithCurrentMarker();
489
+ }
490
+
491
+ goToId(id, fast, css_animation) {
492
+ this.goTo(this._findMarkerIndex(id), fast, css_animation);
493
+ }
494
+
495
+ focusOn(n, fast, css_animation) {
496
+ const _ease = this.options.ease,
497
+ _duration = this.options.duration,
498
+ _n = (n < 0) ? 0 : n;
499
+
500
+ this.animateMovement(_n, fast, css_animation, _duration, _ease);
501
+
502
+ this._resetMarkersBlurListeners();
503
+ if (n >= 0 && n < this._markers.length) {
504
+ this._markers[n].setFocus();
505
+ this.current_focused_id = this._markers[n].data.unique_id;
506
+ this._markers[n].on('markerblur', this._onMarkerBlur, this);
507
+ }
508
+ }
509
+
510
+ focusNext() {
511
+ const n = this._findMarkerIndex(this.current_focused_id);
512
+ if ((n + 1) < this._markers.length) {
513
+ this.focusOn(n + 1);
514
+ } else {
515
+ this.focusOn(n);
516
+ }
517
+ }
518
+
519
+ focusPrevious() {
520
+ const n = this._findMarkerIndex(this.current_focused_id);
521
+ if (n - 1 >= 0) {
522
+ this.focusOn(n - 1);
523
+ } else {
524
+ this.focusOn(n);
525
+ }
526
+ }
527
+
528
+ animateMovement(n, fast, css_animation, duration, ease) {
462
529
  // Stop animation
463
530
  if (this.animator) {
464
531
  this.animator.stop();
@@ -466,18 +533,21 @@ export class TimeNav {
466
533
 
467
534
  if (fast) {
468
535
  this._el.slider.className = "tl-timenav-slider";
469
- this._el.slider.style.left = -this._markers[_n].getLeft() + (this.options.width / 2) + "px";
536
+ this._el.slider.style.left = -this._markers[n].getLeft() +
537
+ (this.options.width / 2) + "px";
470
538
  } else {
471
539
  if (css_animation) {
472
540
  this._el.slider.className = "tl-timenav-slider tl-timenav-slider-animate";
473
541
  this.animate_css = true;
474
- this._el.slider.style.left = -this._markers[_n].getLeft() + (this.options.width / 2) + "px";
542
+ this._el.slider.style.left = -this._markers[n].getLeft() +
543
+ (this.options.width / 2) + "px";
475
544
  } else {
476
545
  this._el.slider.className = "tl-timenav-slider";
477
546
  this.animator = Animate(this._el.slider, {
478
- left: -this._markers[_n].getLeft() + (this.options.width / 2) + "px",
479
- duration: _duration,
480
- easing: _ease
547
+ left: -this._markers[n].getLeft() +
548
+ (this.options.width / 2) + "px",
549
+ duration: duration,
550
+ easing: ease
481
551
  });
482
552
  }
483
553
  }
@@ -487,12 +557,29 @@ export class TimeNav {
487
557
  } else {
488
558
  this.current_id = '';
489
559
  }
560
+
561
+ this._dispatchVisibleTicksChange();
490
562
  }
491
563
 
492
564
  goToId(id, fast, css_animation) {
493
565
  this.goTo(this._findMarkerIndex(id), fast, css_animation);
494
566
  }
495
567
 
568
+ _dispatchVisibleTicksChange() {
569
+ /**
570
+ * The timeout is required to wait till the end of the animation
571
+ * and repositioning of the ticks on the screen
572
+ */
573
+ if (this.ticks_change_timeout) {
574
+ clearTimeout(this.ticks_change_timeout);
575
+ this.ticks_change_timeout = null;
576
+ }
577
+ this.ticks_change_timeout = setTimeout(() => {
578
+ const visible_ticks = this.timeaxis.getVisibleTicks();
579
+ this.fire("visible_ticks_change", { visible_ticks });
580
+ }, this.options.duration);
581
+ }
582
+
496
583
  /* Events
497
584
  ================================================== */
498
585
  _onLoaded() {
@@ -518,6 +605,12 @@ export class TimeNav {
518
605
  this.fire("change", { unique_id: e.unique_id });
519
606
  }
520
607
 
608
+ _onMarkerBlur(e) {
609
+ // Reset the focused marked to the active marker after it lost the focus
610
+ if (this.current_focused_id === this.current_id) return;
611
+ this.focusOn(this._findMarkerIndex(this.current_id));
612
+ }
613
+
521
614
  _onMouseScroll(e) {
522
615
 
523
616
  var delta = 0,
@@ -576,6 +669,35 @@ export class TimeNav {
576
669
 
577
670
  }
578
671
 
672
+ _onKeydown(e) {
673
+ DOMEvent.stopPropagation(e);
674
+
675
+ switch (e.key) {
676
+ case "ArrowUp":
677
+ case "ArrowRight":
678
+ {
679
+ this.focusNext();
680
+ break;
681
+ }
682
+ case "ArrowDown":
683
+ case "ArrowLeft":
684
+ {
685
+ this.focusPrevious();
686
+ break;
687
+ }
688
+ case "Home":
689
+ {
690
+ this.focusOn(0);
691
+ break;
692
+ }
693
+ case "End":
694
+ {
695
+ this.focusOn(this._markers.length - 1);
696
+ break;
697
+ }
698
+ }
699
+ }
700
+
579
701
  /* Private Methods
580
702
  ================================================== */
581
703
 
@@ -633,12 +755,18 @@ export class TimeNav {
633
755
 
634
756
  }
635
757
 
758
+ _setLabelWithCurrentMarker() {
759
+ const currentMarker = this._markers[this._findMarkerIndex(this.current_focused_id)];
760
+ const currentMarkerText = currentMarker && currentMarker.ariaLabel ?
761
+ `, ${currentMarker.ariaLabel}, shown` :
762
+ '';
763
+ this._el.container.setAttribute('aria-label', `Timeline navigation ${currentMarkerText}`);
764
+ }
636
765
 
637
766
  /* Init
638
767
  ================================================== */
639
768
  _initLayout() {
640
769
  // Create Layout
641
- this._el.attribution = DOM.create('div', 'tl-attribution', this._el.container);
642
770
  this._el.line = DOM.create('div', 'tl-timenav-line', this._el.container);
643
771
  this._el.slider = DOM.create('div', 'tl-timenav-slider', this._el.container);
644
772
  this._el.slider_background = DOM.create('div', 'tl-timenav-slider-background', this._el.slider);
@@ -648,10 +776,6 @@ export class TimeNav {
648
776
  this._el.timeaxis = DOM.create('div', 'tl-timeaxis', this._el.slider);
649
777
  this._el.timeaxis_background = DOM.create('div', 'tl-timeaxis-background', this._el.container);
650
778
 
651
-
652
- // Knight Lab Logo
653
- this._el.attribution.innerHTML = "<a href='http://timeline.knightlab.com' target='_blank' rel='noopener'><span class='tl-knightlab-logo'></span>TimelineJS</a>"
654
-
655
779
  // Time Axis
656
780
  this.timeaxis = new TimeAxis(this._el.timeaxis, this.options, this.language);
657
781
 
@@ -672,6 +796,7 @@ export class TimeNav {
672
796
  // Scroll Events
673
797
  DOMEvent.addListener(this._el.container, 'mousewheel', this._onMouseScroll, this);
674
798
  DOMEvent.addListener(this._el.container, 'DOMMouseScroll', this._onMouseScroll, this);
799
+ DOMEvent.addListener(this._el.container, 'keydown', this._onKeydown, this);
675
800
  }
676
801
 
677
802
  _initData() {
@@ -688,4 +813,4 @@ export class TimeNav {
688
813
  }
689
814
  }
690
815
 
691
- classMixin(TimeNav, Events, DOMMixins)
816
+ classMixin(TimeNav, Events, DOMMixins, I18NMixins)