@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
@@ -9,6 +9,7 @@ export class Text {
9
9
  container: { },
10
10
  content_container: { },
11
11
  content: { },
12
+ headline_container: { },
12
13
  headline: { },
13
14
  date: { }
14
15
  }
@@ -24,54 +25,54 @@ export class Text {
24
25
  }
25
26
 
26
27
  setData(this, data); // override defaults
27
-
28
+
28
29
  // Merge Options
29
30
  mergeData(this.options, options);
30
-
31
+
31
32
  this._el.container = DOM.create("div", "tl-text");
32
33
  this._el.container.id = this.data.unique_id;
33
-
34
+
34
35
  this._initLayout();
35
-
36
+
36
37
  if (add_to_container) {
37
38
  add_to_container.appendChild(this._el.container);
38
39
  };
39
-
40
+
40
41
  }
41
-
42
+
42
43
  /* Adding, Hiding, Showing etc
43
44
  ================================================== */
44
45
  show() {
45
-
46
+
46
47
  }
47
-
48
+
48
49
  hide() {
49
-
50
+
50
51
  }
51
-
52
+
52
53
  addTo(container) {
53
54
  container.appendChild(this._el.container);
54
55
  //this.onAdd();
55
56
  }
56
-
57
+
57
58
  removeFrom(container) {
58
59
  container.removeChild(this._el.container);
59
60
  }
60
-
61
+
61
62
  headlineHeight() {
62
63
  return this._el.headline.offsetHeight + 40;
63
64
  }
64
-
65
+
65
66
  addDateText(str) {
66
67
  this._el.date.innerHTML = str;
67
68
  }
68
-
69
+
69
70
  /* Events
70
71
  ================================================== */
71
72
  onLoaded() {
72
73
  this.fire("loaded", this.data);
73
74
  }
74
-
75
+
75
76
  onAdd() {
76
77
  this.fire("added", this.data);
77
78
  }
@@ -79,26 +80,27 @@ export class Text {
79
80
  onRemove() {
80
81
  this.fire("removed", this.data);
81
82
  }
82
-
83
+
83
84
  /* Private Methods
84
85
  ================================================== */
85
86
  _initLayout() {
86
-
87
+
87
88
  // Create Layout
88
89
  this._el.content_container = DOM.create("div", "tl-text-content-container", this._el.container);
89
-
90
- // Date
91
- this._el.date = DOM.create("h3", "tl-headline-date", this._el.content_container);
92
-
93
- // Headline
94
- if (this.data.headline != "") {
95
- var headline_class = "tl-headline";
96
- if (this.options.title) {
97
- headline_class = "tl-headline tl-headline-title";
98
- }
99
- this._el.headline = DOM.create("h2", headline_class, this._el.content_container);
100
- this._el.headline.innerHTML = this.data.headline;
101
- }
90
+ this._el.headline_container = DOM.create("div", "tl-text-headline-container", this._el.content_container);
91
+
92
+ // Headline
93
+ if (this.data.headline != "") {
94
+ var headline_class = "tl-headline";
95
+ if (this.options.title) {
96
+ headline_class = "tl-headline tl-headline-title";
97
+ }
98
+ this._el.headline = DOM.create("h2", headline_class, this._el.headline_container);
99
+ this._el.headline.innerHTML = this.data.headline;
100
+ }
101
+
102
+ // Date
103
+ this._el.date = DOM.create("h3", "tl-headline-date", this._el.headline_container);
102
104
 
103
105
  // Text
104
106
  if (this.data.text != "") {
@@ -115,4 +117,4 @@ export class Text {
115
117
 
116
118
  }
117
119
 
118
- classMixin(Text, Events)
120
+ classMixin(Text, Events)
@@ -3,60 +3,61 @@ import { Media } from "../Media";
3
3
 
4
4
  export default class Vimeo extends Media {
5
5
 
6
- _loadMedia() {
7
- var api_url,
8
- self = this;
9
-
10
- // Create Dom element
11
- this._el.content_item = this.domCreate("div", "tl-media-item tl-media-iframe tl-media-vimeo tl-media-shadow", this._el.content);
12
-
13
- // Get Media ID
14
- this.media_id = this.data.url.split(/video\/|\/\/vimeo\.com\//)[1].split(/[?&]/)[0];
15
- var start_time = null;
16
-
17
- // Get start time
18
- if (this.data.url.match(/#t=([^&]+).*/)) {
19
- start_time = this.data.url.match(/#t=([^&]+).*/)[1];
20
- }
21
-
22
- // API URL
23
- api_url = "https://player.vimeo.com/video/" + this.media_id + "?api=1&title=0&byline=0&portrait=0&color=ffffff";
24
- if (start_time) {
25
- api_url = api_url += '&#t=' + start_time;
26
- }
27
-
28
- this.player = this.domCreate("iframe", "", this._el.content_item);
29
-
30
- // Media Loaded Event
31
- this.player.addEventListener('load', function(e) {
32
- self.onMediaLoaded();
33
- });
34
-
35
- this.player.width = "100%";
36
- this.player.height = "100%";
37
- this.player.frameBorder = "0";
38
- this.player.src = api_url;
39
-
40
- this.player.setAttribute('allowfullscreen', '');
41
- this.player.setAttribute('webkitallowfullscreen', '');
42
- this.player.setAttribute('mozallowfullscreen', '');
43
-
44
- // After Loaded
45
- this.onLoaded();
46
- }
47
-
48
- // Update Media Display
49
- _updateMediaDisplay() {
50
- this._el.content_item.style.height = ratio.r16_9({w:this._el.content_item.offsetWidth}) + "px";
51
- }
52
-
53
- _stopMedia() {
54
-
55
- try {
56
- this.player.contentWindow.postMessage(JSON.stringify({method: "pause"}), "https://player.vimeo.com");
57
- }
58
- catch(err) {
59
- trace(err);
60
- }
61
- }
62
- }
6
+ _loadMedia() {
7
+ var api_url,
8
+ self = this;
9
+
10
+ // Create Dom element
11
+ this._el.content_item = this.domCreate("div", "tl-media-item tl-media-iframe tl-media-vimeo tl-media-shadow", this._el.content);
12
+
13
+ // Get Media ID
14
+ this.media_id = this.data.url.split(/video\/|\/\/vimeo\.com\//)[1].split(/[?&]/)[0];
15
+ var start_time = null;
16
+
17
+ // Get start time
18
+ if (this.data.url.match(/#t=([^&]+).*/)) {
19
+ start_time = this.data.url.match(/#t=([^&]+).*/)[1];
20
+ }
21
+
22
+ // API URL
23
+ api_url = "https://player.vimeo.com/video/" + this.media_id + "?api=1&title=0&byline=0&portrait=0&color=ffffff";
24
+ if (start_time) {
25
+ api_url = api_url += '&#t=' + start_time;
26
+ }
27
+
28
+ this.player = this.domCreate("iframe", "", this._el.content_item);
29
+
30
+ // Media Loaded Event
31
+ this.player.addEventListener('load', function(e) {
32
+ self.onMediaLoaded();
33
+ });
34
+
35
+ this.player.width = "100%";
36
+ this.player.height = "100%";
37
+ this.player.frameBorder = "0";
38
+ this.player.src = api_url;
39
+
40
+ this.player.setAttribute('allowfullscreen', '');
41
+ this.player.setAttribute('webkitallowfullscreen', '');
42
+ this.player.setAttribute('mozallowfullscreen', '');
43
+
44
+ // After Loaded
45
+ this.onLoaded();
46
+ }
47
+
48
+ // Update Media Display
49
+ _updateMediaDisplay() {
50
+ this._el.content_item.style.height = ratio.r16_9({ w: this._el.content_item.offsetWidth }) + "px";
51
+ }
52
+
53
+ _stopMedia() {
54
+
55
+ try {
56
+ if (this.player && this.player.contentWindow) {
57
+ this.player.contentWindow.postMessage(JSON.stringify({ method: "pause" }), "https://player.vimeo.com");
58
+ }
59
+ } catch (err) {
60
+ trace(err);
61
+ }
62
+ }
63
+ }
@@ -1,3 +1,5 @@
1
+ import "wicg-inert";
2
+
1
3
  import { addClass } from "../dom/DOMUtil"
2
4
  import { I18NMixins } from "../language/I18NMixins";
3
5
  import Events from "../core/Events";
@@ -112,9 +114,11 @@ export class Slide {
112
114
  if (this.data.background) {
113
115
  this.fire("background_change", this.has.background);
114
116
  }
117
+ this._setInteractive(true)
115
118
  this.loadMedia();
116
119
  } else {
117
120
  this.stopMedia();
121
+ this._setInteractive(false)
118
122
  }
119
123
  }
120
124
 
@@ -263,6 +267,10 @@ export class Slide {
263
267
  this.has.background.color_value = "#000";
264
268
  this._el.background.style.display = "block";
265
269
  }
270
+ if (this.data.background.alt) {
271
+ this._el.background.setAttribute('role', 'img');
272
+ this._el.background.setAttribute('aria-label', this.data.background.alt);
273
+ }
266
274
  }
267
275
  if (this.data.background.color) {
268
276
  this.has.background.color = true;
@@ -305,7 +313,7 @@ export class Slide {
305
313
 
306
314
  // Create Text
307
315
  if (this.has.text || this.has.headline) {
308
- this._text = new Text(this.data.text, { title: this.has.title, language: this.options.language, autolink: this.data.autolink });
316
+ this._text = new Text(this.data.text, { title: this.has.title, language: this.getLanguage(), autolink: this.data.autolink });
309
317
  this._text.addDateText(this.getFormattedDate());
310
318
  }
311
319
 
@@ -320,8 +328,8 @@ export class Slide {
320
328
  this._text.addTo(this._el.content);
321
329
  this._media.addTo(this._el.content);
322
330
  } else if (this.has.text && this.has.media) {
323
- this._media.addTo(this._el.content);
324
331
  this._text.addTo(this._el.content);
332
+ this._media.addTo(this._el.content);
325
333
  } else if (this.has.text || this.has.headline) {
326
334
  addClass(this._el.container, 'tl-slide-text-only');
327
335
  this._text.addTo(this._el.content);
@@ -342,5 +350,12 @@ export class Slide {
342
350
  }
343
351
  }
344
352
 
353
+ _setInteractive(is_interactive) {
354
+ if (is_interactive) {
355
+ this._el.container.removeAttribute('inert');
356
+ } else {
357
+ this._el.container.setAttribute('inert', true);
358
+ }
359
+ }
345
360
  }
346
361
  classMixin(Slide, I18NMixins, Events, DOMMixins)
@@ -39,7 +39,7 @@ export class SlideNav {
39
39
  mergeData(this.data, data);
40
40
 
41
41
 
42
- this._el.container = DOM.create("div", "tl-slidenav-" + this.options.direction);
42
+ this._el.container = DOM.create("button", "tl-slidenav-" + this.options.direction);
43
43
 
44
44
  if (Browser.mobile) {
45
45
  this._el.container.setAttribute("ontouchstart"," ");
@@ -93,12 +93,17 @@ export class SlideNav {
93
93
  _update(d) {
94
94
  // update data
95
95
  this.data = mergeData(this.data, d);
96
-
97
- // Title
98
- this._el.title.innerHTML = unlinkify(this.data.title);
99
-
100
- // Date
101
- this._el.description.innerHTML = unlinkify(this.data.date);
96
+
97
+ // Title
98
+ const title = unlinkify(this.data.title);
99
+ this._el.title.innerHTML = title;
100
+
101
+ // Date
102
+ const date = unlinkify(this.data.date);
103
+ this._el.description.innerHTML = date;
104
+
105
+ // Alternative text
106
+ this._el.container.setAttribute('aria-label', `${this.options.direction}, ${title}, ${date}`)
102
107
  }
103
108
 
104
109
  _initLayout () {
@@ -326,7 +326,7 @@ export class StorySlider {
326
326
  showNav(nav_obj, show) {
327
327
 
328
328
  if (this.options.width <= 500 && Browser.mobile) {
329
-
329
+ nav_obj.hide();
330
330
  } else {
331
331
  if (show) {
332
332
  nav_obj.show();
@@ -396,27 +396,23 @@ export class StorySlider {
396
396
 
397
397
  addClass(this._el.container, 'tl-storyslider');
398
398
 
399
+ // Create Navigation
400
+ this._nav.previous = new SlideNav({ title: "Previous", description: "description" }, { direction: "previous" }, this._el.container);
401
+ this._nav.next = new SlideNav({ title: "Next", description: "description" }, { direction: "next" }, this._el.container);
402
+
399
403
  // Create Layout
400
404
  this._el.slider_container_mask = DOM.create('div', 'tl-slider-container-mask', this._el.container);
401
405
  this._el.background = DOM.create('div', 'tl-slider-background tl-animate', this._el.container);
402
406
  this._el.slider_container = DOM.create('div', 'tl-slider-container tlanimate', this._el.slider_container_mask);
403
407
  this._el.slider_item_container = DOM.create('div', 'tl-slider-item-container', this._el.slider_container);
404
408
 
409
+ // Add aria-live polite to slide_container
410
+ this._el.slider_container.setAttribute('aria-live', 'polite');
405
411
 
406
412
  // Update Size
407
413
  this.options.width = this._el.container.offsetWidth;
408
414
  this.options.height = this._el.container.offsetHeight;
409
415
 
410
- // Create Navigation
411
- this._nav.previous = new SlideNav({ title: "Previous", description: "description" }, { direction: "previous" });
412
- this._nav.next = new SlideNav({ title: "Next", description: "description" }, { direction: "next" });
413
-
414
- // add the navigation to the dom
415
- this._nav.next.addTo(this._el.container);
416
- this._nav.previous.addTo(this._el.container);
417
-
418
-
419
-
420
416
  this._el.slider_container.style.left = "0px";
421
417
 
422
418
  if (Browser.touch) {
@@ -538,4 +534,4 @@ export class StorySlider {
538
534
 
539
535
  }
540
536
 
541
- classMixin(StorySlider, I18NMixins, Events)
537
+ classMixin(StorySlider, I18NMixins, Events)
@@ -193,6 +193,9 @@ class Timeline {
193
193
 
194
194
  // Apply base class to container
195
195
  addClass(this._el.container, 'tl-timeline');
196
+ this._el.container.setAttribute('tabindex', '0');
197
+ this._el.container.setAttribute('role', 'region');
198
+ this._el.container.setAttribute('aria-label', this._('aria_label_timeline'));
196
199
 
197
200
  if (this.options.is_embed) {
198
201
  addClass(this._el.container, 'tl-timeline-embed');
@@ -248,7 +251,6 @@ class Timeline {
248
251
  if (language) {
249
252
  this.language = language
250
253
  this.message.setLanguage(this.language)
251
- this.options.language = this.language // easiest way to make language available to I18NMixins
252
254
  this.showMessage(this._('loading_timeline'))
253
255
  } else {
254
256
  this.showMessage(`Error loading ${lang}`) // but we will carry on using the fallback
@@ -398,7 +400,6 @@ class Timeline {
398
400
  }
399
401
 
400
402
  _initLayout() {
401
- var self = this;
402
403
 
403
404
  this.message.removeFrom(this._el.container);
404
405
  this._el.container.innerHTML = "";
@@ -406,14 +407,17 @@ class Timeline {
406
407
  // Create Layout
407
408
  if (this.options.timenav_position == "top") {
408
409
  this._el.timenav = DOM.create('div', 'tl-timenav', this._el.container);
410
+ this._el.menubar = DOM.create('div', 'tl-menubar', this._el.container);
409
411
  this._el.storyslider = DOM.create('div', 'tl-storyslider', this._el.container);
410
412
  } else {
411
413
  this._el.storyslider = DOM.create('div', 'tl-storyslider', this._el.container);
412
414
  this._el.timenav = DOM.create('div', 'tl-timenav', this._el.container);
415
+ this._el.menubar = DOM.create('div', 'tl-menubar', this._el.container);
413
416
  }
414
417
 
415
- this._el.menubar = DOM.create('div', 'tl-menubar', this._el.container);
416
-
418
+ // Knight Lab Logo
419
+ this._el.attribution = DOM.create('div', 'tl-attribution', this._el.container);
420
+ this._el.attribution.innerHTML = "<a href='https://timeline.knightlab.com' target='_blank' rel='noopener'><span class='tl-knightlab-logo'></span>TimelineJS</a>"
417
421
 
418
422
  // Initial Default Layout
419
423
  this.options.width = this._el.container.offsetWidth;
@@ -437,11 +441,13 @@ class Timeline {
437
441
 
438
442
  // Create StorySlider
439
443
  this._storyslider = new StorySlider(this._el.storyslider, this.config, this.options, this.language);
444
+ this._el.storyslider.setAttribute('role', 'group');
445
+ this._el.storyslider.setAttribute('aria-label', this._('aria_label_timeline_content'));
440
446
  this._storyslider.on('loaded', this._onStorySliderLoaded, this);
441
447
  this._storyslider.init();
442
448
 
443
449
  // Create Menu Bar
444
- this._menubar = new MenuBar(this._el.menubar, this._el.container, this.options);
450
+ this._menubar = new MenuBar(this._el.menubar, this._el.container, this.options, this.getLanguage());
445
451
 
446
452
  // LAYOUT
447
453
  if (this.options.layout == "portrait") {
@@ -460,6 +466,7 @@ class Timeline {
460
466
  // TimeNav Events
461
467
  this._timenav.on('change', this._onTimeNavChange, this);
462
468
  this._timenav.on('zoomtoggle', this._onZoomToggle, this);
469
+ this._timenav.on('visible_ticks_change', this._onVisibleTicksChange, this);
463
470
 
464
471
  // StorySlider Events
465
472
  this._storyslider.on('change', this._onSlideChange, this);
@@ -470,6 +477,7 @@ class Timeline {
470
477
  // Menubar Events
471
478
  this._menubar.on('zoom_in', this._onZoomIn, this);
472
479
  this._menubar.on('zoom_out', this._onZoomOut, this);
480
+ this._menubar.on('forward_to_end', this._onForwardToEnd, this);
473
481
  this._menubar.on('back_to_start', this._onBackToStart, this);
474
482
 
475
483
  }
@@ -512,8 +520,17 @@ class Timeline {
512
520
  }
513
521
  }
514
522
 
523
+ _onVisibleTicksChange(e) {
524
+ this._menubar.changeVisibleTicks(e.visible_ticks);
525
+ }
526
+
527
+ _onForwardToEnd(e) {
528
+ this.goToEnd();
529
+ this.fire("forward_to_end", { unique_id: this.current_id }, this);
530
+ }
531
+
515
532
  _onBackToStart(e) {
516
- this._storyslider.goTo(0);
533
+ this.goToStart();
517
534
  this.fire("back_to_start", { unique_id: this.current_id }, this);
518
535
  }
519
536
 
@@ -650,7 +667,7 @@ class Timeline {
650
667
  this._timenav.updateDisplay(this.options.width, this.options.timenav_height, animate);
651
668
  this._storyslider.updateDisplay(this.options.width, this.options.storyslider_height, animate, this.options.layout);
652
669
 
653
- if (this.options.language.direction == 'rtl') {
670
+ if (this.language.direction == 'rtl') {
654
671
  display_class += ' tl-rtl';
655
672
  }
656
673
 
@@ -833,14 +850,25 @@ class Timeline {
833
850
 
834
851
  // Goto slide n
835
852
  goTo(n) {
836
- if (this.config.title) {
837
- if (n == 0) {
838
- this.goToId(this.config.title.unique_id);
853
+ if (n < 0) {
854
+ return;
855
+ }
856
+
857
+ try {
858
+ if (this.config.title) {
859
+ if (n === 0) {
860
+ this.goToId(this.config.title.unique_id);
861
+ } else {
862
+ this.goToId(this.config.events[n - 1].unique_id);
863
+ }
839
864
  } else {
840
- this.goToId(this.config.events[n - 1].unique_id);
865
+ this.goToId(this.config.events[n].unique_id);
841
866
  }
842
- } else {
843
- this.goToId(this.config.events[n].unique_id);
867
+ } catch {
868
+ // because n is interpreted differently depending on
869
+ // whether there's a title slide, easier to use catch
870
+ // to handle navigating beyond end instead of test before
871
+ return
844
872
  }
845
873
  }
846
874
 
@@ -858,11 +886,13 @@ class Timeline {
858
886
  // Goto previous slide
859
887
  goToPrev() {
860
888
  this.goTo(this._getSlideIndex(this.current_id) - 1);
889
+ this.focusContainer();
861
890
  }
862
891
 
863
892
  // Goto next slide
864
893
  goToNext() {
865
894
  this.goTo(this._getSlideIndex(this.current_id) + 1);
895
+ this.focusContainer();
866
896
  }
867
897
 
868
898
  /* Event manipulation
@@ -958,6 +988,10 @@ class Timeline {
958
988
  }
959
989
  }
960
990
 
991
+ focusContainer() {
992
+ this._el.container.focus();
993
+ }
994
+
961
995
  _initGoogleAnalytics() {
962
996
  (function(i, s, o, g, r, a, m) {
963
997
  i['GoogleAnalyticsObject'] = r;
@@ -5,6 +5,14 @@ import { I18NMixins } from "../language/I18NMixins"
5
5
  import { easeInSpline } from "../animation/Ease";
6
6
  import * as DOM from "../dom/DOM"
7
7
 
8
+ function isInHorizontalViewport(element) {
9
+ const rect = element.getBoundingClientRect();
10
+ return (
11
+ rect.left >= 0 &&
12
+ rect.right <= (window.innerWidth || document.documentElement.clientWidth)
13
+ );
14
+ }
15
+
8
16
  export class TimeAxis {
9
17
  constructor(elem, options, language) {
10
18
 
@@ -216,10 +224,21 @@ export class TimeAxis {
216
224
  tick.tick.className = "tl-timeaxis-tick";
217
225
  }
218
226
 
219
- };
227
+ }
228
+ }
229
+ }
230
+
231
+ getVisibleTicks() {
232
+ return {
233
+ major: this._getVisibleTickArray(this.major_ticks),
234
+ minor: this._getVisibleTickArray(this.minor_ticks)
220
235
  }
221
236
  }
222
237
 
238
+ _getVisibleTickArray(tick_array) {
239
+ return tick_array.filter(({ tick }) => isInHorizontalViewport(tick))
240
+ }
241
+
223
242
  /* Events
224
243
  ================================================== */
225
244