@knight-lab/timelinejs 3.9.0 → 3.9.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knight-lab/timelinejs",
3
- "version": "3.9.0",
3
+ "version": "3.9.2",
4
4
  "license": "MPL-2.0",
5
5
  "description": "TimelineJS v3: A Storytelling Timeline built in JavaScript, made by Northwestern University Knight Lab.",
6
6
  "dependencies": {
@@ -59,7 +59,8 @@
59
59
  "stage_dev": "npm run dist && node tasks/stage.js dev",
60
60
  "prepublishOnly": "npm run dist"
61
61
  },
62
- "contributors": [{
62
+ "contributors": [
63
+ {
63
64
  "name": "Zach Wise",
64
65
  "email": "wise@northwestern.edu",
65
66
  "url": "https://github.com/zachwise"
@@ -70,4 +71,4 @@
70
71
  "url": "https://github.com/JoeGermuska"
71
72
  }
72
73
  ]
73
- }
74
+ }
@@ -178,13 +178,24 @@ export const TLDate = TLClass.extend({
178
178
  this._createDateObj();
179
179
  }
180
180
 
181
+ if (data.format && !format) {
182
+ format = data.format
183
+ }
181
184
  this._setFormat(format, format_short);
182
185
  },
183
186
 
184
187
  setDateFormat: function(format) {
185
188
  this.data.format = format;
186
189
  },
187
-
190
+ /**
191
+ * Return a string representation of this date. If this date has been created with a `display_date` property,
192
+ * that value is always returned, regardless of arguments to the method invocation. Otherwise,
193
+ * the given `Language` is asked to create a string representation based on this Date's data, passing through the provided
194
+ * `format` String, or, if that is undefined, this date's default format string.
195
+ * @param {Language} language
196
+ * @param {String} format
197
+ * @returns {String} formattedDate
198
+ */
188
199
  getDisplayDate: function(language, format) {
189
200
  if (this.data.display_date) {
190
201
  return this.data.display_date;
@@ -345,6 +356,14 @@ export const TLDate = TLClass.extend({
345
356
  } else if (!this.data.format_short) {
346
357
  this.data.format_short = this.findBestFormat(true);
347
358
  }
359
+ },
360
+ /**
361
+ * Get the year-only representation of this date. Ticks need this to layout
362
+ * the time axis, and this needs to work isomorphically for TLDate and BigDate
363
+ * @returns {Number}
364
+ */
365
+ getFullYear: function() {
366
+ return this.data.date_obj.getFullYear()
348
367
  }
349
368
  });
350
369
 
@@ -477,6 +496,10 @@ export const BigDate = TLDate.extend({
477
496
  this._createDateObj();
478
497
  }
479
498
 
499
+ if (data.format && !format) {
500
+ format = data.format
501
+ }
502
+
480
503
  this._setFormat(format, format_short);
481
504
  },
482
505
 
@@ -497,5 +520,13 @@ export const BigDate = TLDate.extend({
497
520
  }
498
521
 
499
522
  throw new TLError("invalid_scale_err", scale);
523
+ },
524
+ /**
525
+ * Get the year-only representation of this date. Ticks need this to layout
526
+ * the time axis, and this needs to work isomorphically for TLDate and BigDate
527
+ * @returns {Number}
528
+ */
529
+ getFullYear: function() {
530
+ return this.data.date_obj.getTime()
500
531
  }
501
532
  });
@@ -55,6 +55,16 @@ test("display_text overrides other formatting", () => {
55
55
 
56
56
  })
57
57
 
58
+ test("date constructor format overrides other formatting", () => {
59
+ var cdate = new TLDate({ year: 2014, month: 12 }, 'yyyy')
60
+ expect(cdate.getDisplayDate()).toBe('2014')
61
+ })
62
+
63
+ test("format in constructor data param overrides other formatting", () => {
64
+ var cdate = new TLDate({ year: 2014, month: 12, format: 'yyyy' })
65
+ expect(cdate.getDisplayDate()).toBe('2014')
66
+ })
67
+
58
68
  test("handle years in the first century CE correctly", () => {
59
69
  var date = makeDate({ year: 75 });
60
70
  expect(date.getDisplayDate()).toBe("75")
package/src/js/dom/DOM.js CHANGED
@@ -19,6 +19,12 @@ function create(tagName, className, container) {
19
19
  return el;
20
20
  }
21
21
 
22
+ function createButton(className, container) {
23
+ var el = create('button', className, container);
24
+ el.type = 'button';
25
+ return el;
26
+ }
27
+
22
28
  function createText(content, container) {
23
29
  var el = document.createTextNode(content);
24
30
  if (container) {
@@ -78,4 +84,4 @@ let TRANSFORM = testProp(['transformProperty', 'WebkitTransform', 'OTransform',
78
84
  let TRANSLATE_OPEN = 'translate' + (Browser.webkit3d ? '3d(' : '(')
79
85
  let TRANSLATE_CLOSE = Browser.webkit3d ? ',0)' : ')'
80
86
 
81
- export { get, create, getPosition }
87
+ export { get, create, createButton, getPosition }
@@ -318,6 +318,7 @@ var LANGUAGES = {
318
318
  twitter_load_err: "Unable to load Tweet",
319
319
  twitterembed_invalidurl_err: "Invalid Twitter Embed url",
320
320
  wikipedia_load_err: "Unable to load Wikipedia entry",
321
+ wikipedia_image_load_err: "Unable to load Wikipedia image data",
321
322
  spotify_invalid_url: "Invalid Spotify URL",
322
323
  invalid_rgb_err: "Invalid RGB argument",
323
324
  time_scale_scale_err: "Don't know how to get date from time for scale",
@@ -0,0 +1,69 @@
1
+ {
2
+ "lang": "en-gb",
3
+ "date": {
4
+ "month_abbr": [
5
+ "Jan",
6
+ "Feb",
7
+ "Mar",
8
+ "Apr",
9
+ "May",
10
+ "Jun",
11
+ "Jul",
12
+ "Aug",
13
+ "Sep",
14
+ "Oct",
15
+ "Nov",
16
+ "Dec"
17
+ ],
18
+ "day_abbr": [
19
+ "Sun",
20
+ "Mon",
21
+ "Tue",
22
+ "Wed",
23
+ "Thu",
24
+ "Fri",
25
+ "Sat"
26
+ ],
27
+ "day": [
28
+ "Sunday",
29
+ "Monday",
30
+ "Tuesday",
31
+ "Wednesday",
32
+ "Thursday",
33
+ "Friday",
34
+ "Saturday"
35
+ ],
36
+ "month": [
37
+ "January",
38
+ "February",
39
+ "March",
40
+ "April",
41
+ "May",
42
+ "June",
43
+ "July",
44
+ "August",
45
+ "September",
46
+ "October",
47
+ "November",
48
+ "December"
49
+ ]
50
+ },
51
+ "api": {
52
+ "wikipedia": "en"
53
+ },
54
+ "messages": {
55
+ "comment": "There is no need to repeat message keys because the fallback is English and this file only exists to control date formatting."
56
+ },
57
+ "dateformats": {
58
+ "full_long": "d mmm yyyy 'at' HH:MM TT",
59
+ "full_short": "d mmm",
60
+ "full": "d mmmm yyyy",
61
+ "month_short": "mmm",
62
+ "time_no_seconds_small_date": "HH:MM TT'<br/><small>'d mmmm yyyy'</small>'",
63
+ "month": "mmmm yyyy",
64
+ "time_no_seconds_short": "HH:MM TT",
65
+ "time_short": "HH:MM:ss",
66
+ "year": "yyyy",
67
+ "full_long_small_date": "HH:MM TT'<br/><small>d mmm yyyy'</small>'"
68
+ }
69
+ }
@@ -54,7 +54,7 @@
54
54
  "messages": {
55
55
  "loading": "cargando",
56
56
  "return_to_title": "Volver al título",
57
- "swipe_nav": "Desliza para ver",
57
+ "swipe_to_navigate": "Desliza para ver<br><span class='tl-button'>OK</span>",
58
58
  "wikipedia": "Desde Wikipedia, la enciclopedia libre",
59
59
  "loading_content": "cargando",
60
60
  "loading_timeline": "La cronología esta cargando"
@@ -54,7 +54,6 @@
54
54
  "messages": {
55
55
  "loading": "लोड हो रहा है",
56
56
  "return_to_title": "शीर्षक पर लौटें",
57
- "swipe_nav": "Swipe to Navigate",
58
57
  "read_more": "और पढ़ें",
59
58
  "wikipedia": "विकिपीडिया, मुक्त विश्वकोश से",
60
59
  "loading_timeline": "टाइमलाइन लोड हो रहा है",
@@ -15,6 +15,7 @@ import YouTube from "./types/YouTube"
15
15
  import GoogleMap from "./types/GoogleMap"
16
16
  import Blockquote from "./types/Blockquote"
17
17
  import Wikipedia from "./types/Wikipedia"
18
+ import WikipediaImage from "./types/WikipediaImage"
18
19
  import SoundCloud from "./types/SoundCloud"
19
20
  import Vimeo from "./types/Vimeo"
20
21
  import DailyMotion from "./types/DailyMotion"
@@ -58,7 +59,7 @@ export function lookupMediaType(m, image_only) {
58
59
  media_types = [{
59
60
  type: "youtube",
60
61
  name: "YouTube",
61
- match_str: "^(https?:)?\/*(www.)?youtube|youtu\.be",
62
+ match_str: "^(https?:)?\/*(www.|m.)?youtube|youtu\.be",
62
63
  cls: YouTube
63
64
  },
64
65
  {
@@ -133,6 +134,24 @@ export function lookupMediaType(m, image_only) {
133
134
  match_str: /documentcloud.org\//,
134
135
  cls: DocumentCloud
135
136
  },
137
+ {
138
+ type: "wikipedia-image",
139
+ name: "WikipediaImage",
140
+ match_str: "^https:\/\/.+\.wiki[mp]edia\.org.+#/media/.+\.(jpg|jpeg|png|gif|svg|webp)",
141
+ cls: WikipediaImage
142
+ },
143
+ {
144
+ type: "wikipedia-image",
145
+ name: "WikipediaImage",
146
+ match_str: "^https:\/\/commons.wikimedia.org/wiki/File:.+\.(jpg|jpeg|png|gif|svg|webp)",
147
+ cls: WikipediaImage
148
+ },
149
+ {
150
+ type: "wikipedia",
151
+ name: "Wikipedia",
152
+ match_str: "^(https?:)?\/.+.wikipedia\.org",
153
+ cls: Wikipedia
154
+ },
136
155
  {
137
156
  type: "image",
138
157
  name: "Image",
@@ -148,7 +167,7 @@ export function lookupMediaType(m, image_only) {
148
167
  {
149
168
  type: "googledocs",
150
169
  name: "Google Doc",
151
- match_str: "^(https?:)?\/*[^.]*.google.com\/[^\/]*\/d\/[^\/]*\/[^\/]*\?usp=sharing|^(https?:)?\/*drive.google.com\/open\?id=[^\&]*\&authuser=0|^(https?:)?\/\/*drive.google.com\/open\\?id=[^\&]*|^(https?:)?\/*[^.]*.googledrive.com\/host\/[^\/]*\/",
170
+ match_str: "^(https?:)?\/*[^.]*.google.com\/[^\/]*\/d\/[^\/]*\/[^\/]*\?usp=shar.*|^(https?:)?\/*drive.google.com\/open\?id=[^\&]*\&authuser=0|^(https?:)?\/\/*drive.google.com\/open\\?id=[^\&]*|^(https?:)?\/*[^.]*.googledrive.com\/host\/[^\/]*\/",
152
171
  cls: GoogleDoc
153
172
  },
154
173
  {
@@ -157,12 +176,6 @@ export function lookupMediaType(m, image_only) {
157
176
  match_str: /^.*\.pdf(\?.*)?(\#.*)?/,
158
177
  cls: PDF
159
178
  },
160
- {
161
- type: "wikipedia",
162
- name: "Wikipedia",
163
- match_str: "^(https?:)?\/*(www.)?wikipedia\.org|^(https?:)?\/*([a-z][a-z].)?wikipedia\.org",
164
- cls: Wikipedia
165
- },
166
179
  {
167
180
  type: "spotify",
168
181
  name: "spotify",
@@ -0,0 +1,47 @@
1
+ import { lookupMediaType } from "../MediaType";
2
+ import { test, expect } from "@jest/globals"
3
+
4
+ const TEST_CASES = [
5
+ // tuples: [URL, expected type, optional note which isn't used.]
6
+ ['https://www.youtube.com/watch?v=pi2v1m6gmD8&t=5m21s', 'youtube', 'YouTube with time stamp'],
7
+ ['//www.youtube.com/watch?v=pi2v1m6gmD8', 'youtube', 'YouTube with no protocol'],
8
+ ['youtu.be/pi2v1m6gmD8', 'youtube', 'YouTu.be short url'],
9
+ ['http://vimeo.com/20839673', 'vimeo'],
10
+ ['http://www.dailymotion.com/video/x2fo0e8_the-history-of-advertising-in-60-seconds_lifestyle', 'dailymotion'],
11
+ ['https://vine.co/v/Og5Ai71WHdD', 'vine'],
12
+ ['https://soundcloud.com/usher-raymond-music/usher-i-dont-mind-feat-juicy-j', 'soundcloud'],
13
+ ['https://twitter.com/NASASpaceflight/status/562327074384654336', 'twitter'],
14
+ ['<blockquote class="twitter-tweet" lang="en"><p>Rough vs. smooth: the Anuket and Anubis regions on <a href="https://twitter.com/hashtag/67P?src=hash">#67P</a> <a href="http://t.co/kOyAiOKlma">http://t.co/kOyAiOKlma</a> <a href="https://twitter.com/hashtag/CometWatch?src=hash">#CometWatch</a> <a href="http://t.co/YmQ8bP5WbS">pic.twitter.com/YmQ8bP5WbS</a></p>&mdash; ESA Rosetta Mission (@ESA_Rosetta) <a href="https://twitter.com/ESA_Rosetta/status/563722810397560832">February 6, 2015</a></blockquote>', 'twitterembed'],
15
+ ['https://www.google.com/maps/@42.032147,-87.6689625,15z', 'googlemaps', 'Google Maps with lat/long'],
16
+ ['https://www.google.com/maps/search/target/@41.8747339,-87.6481257,13z?hl=en-US', 'googlemaps', 'Google Maps with search'],
17
+ ['https://www.google.com/maps/place/Northwestern+University/@42.056459,-87.675267,17z/data=!3m1!4b1!4m2!3m1!1s0x880fd00b703e4c39:0x2c37b567fad56106', 'googlemaps', 'Google Maps with place'],
18
+ ['https://www.google.com/maps/dir/W+Adams+St+%26+S+Clark+St,+Chicago,+IL/Northwestern+University,+633+Clark+Street,+Evanston,+IL+60208/@41.9672743,-87.7225481,12z/data=!3m1!4b1!4m13!4m12!1m5!1m1!1s0x880e2cbc8bcec53b:0x72d2c7372d97283d!2m2!1d-87.6308023!2d41.8794067!1m5!1m1!1s0x880fd00b703e4c39:0x2c37b567fad56106!2m2!1d-87.675267!2d42.056459', 'googlemaps', 'Google Maps with directions'],
19
+ ['http://instagram.com/p/ymwL5JAsw5/', 'instagram'],
20
+ ['http://instagram.com/lukerague/', 'profile'],
21
+ ['https://www.flickr.com/photos/critterseeker/16420145375', 'flickr'],
22
+ ['https://flic.kr/p/u7SSxw', 'flickr'],
23
+ ['https://www.documentcloud.org/documents/1377371-folketinget.html', 'documentcloud'],
24
+ ['http://www.kidzone.ws/images-changed/sharks/head.jpg', 'image', 'JPG'],
25
+ ['http://usatlife.files.wordpress.com/2014/06/groundhog-day-bill-murray-winter-never-going-to-end.gif', 'image', 'GIF'],
26
+ ['http://pngimg.com/upload/banana_PNG842.png', 'image', 'PNG'],
27
+ ['http://upload.wikimedia.org/wikipedia/commons/c/c2/Rocky_Mountains.jpeg', 'image', 'JPEG'],
28
+ ['https://docs.google.com/document/d/1RvKYxHuwweIP8zRrnjad-0exVoZOUsSVgDYPp0J1mzY/edit?usp=sharing', 'googledocs'],
29
+ ['http://stlab.adobe.com/wiki/images/d/d3/Test.pdf', 'pdf'],
30
+ ['https://en.wikipedia.org/wiki/1997_International_Tennis_Championships_%E2%80%93_Doubles', 'wikipedia'],
31
+ ['<iframe src="https://embed.spotify.com/?uri=https://play.spotify.com/artist/2iE18Oxc8YSumAU232n4rW" width="300" height="380" frameborder="0" allowtransparency="true"></iframe>', 'spotify', 'artist'],
32
+ ['https://play.spotify.com/user/edvard_m/playlist/4xFSdiuP4gpR4wq2OghlOs', 'spotify', 'playlist'],
33
+ ['https://play.spotify.com/track/5SdB3onMcO9ZBoKrdvCqhR', 'spotify', 'track'],
34
+ ["<iframe src='https://cdn.knightlab.com/libs/timeline/latest/embed/index.html?source=10fFZXg4kioMz8uTVDZfawiJkgrWZxfJuziK1i1AaCrs&font=Bevan-PotanoSans&maptype=toner&lang=en&height=650' width='100%' height='650' frameborder='0'></iframe>", 'iframe'],
35
+ ['<blockquote>This is a block quote.</blockquote>', 'blockquote'],
36
+ ['http://2.bp.blogspot.com/-dxJbW0CG8Zs/TmkoMA5-cPI/AAAAAAAAAqw/fQpsz9GpFdo/s1600/voyage-dans-la-lune-1902-02-g.jpg', 'image'],
37
+ ['https://de.wikipedia.org/wiki/Beryllium#/media/Datei:Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg', 'wikipedia-image', 'regular wikipedia link'],
38
+ ['https://commons.wikimedia.org/wiki/File:Hyperspace_Mountain_(27766070223).jpg', 'wikipedia-image', 'Wikimedia Commons link'],
39
+ ['https://commons.wikimedia.org/wiki/Category:Airdancers#/media/File:Sky-dancer-japan.jpg', 'wikipedia-image', 'Wikimedia Commons link'],
40
+
41
+ ]
42
+
43
+ test.each(TEST_CASES)("Ensure test URLs yield expected type", (url, expected_type) => {
44
+ const result = lookupMediaType({ url: url })
45
+ expect(result.type).toBe(expected_type)
46
+
47
+ })
@@ -0,0 +1,159 @@
1
+ import { Media } from "../Media";
2
+ import { getJSON, fetchJSON } from "../../net/Net";
3
+ import TLError from "../../core/TLError";
4
+ import { unhtmlify } from "../../core/Util";
5
+
6
+ export function computeMediaId(url) {
7
+
8
+ if (url.match(/^.+#\/media\/.+/)) {
9
+ let parts = url.split('#')
10
+ let file_parts = parts[1].split(':') // the prefix is File in English but different on other language WPs
11
+ return `File:${file_parts[1]}`
12
+ }
13
+
14
+ if (url.match(/^.*commons.wikimedia.org\/wiki\/File:.+/)) {
15
+ let parts = url.split('/')
16
+ return parts[parts.length - 1]
17
+ }
18
+
19
+ return null
20
+ }
21
+
22
+ /**
23
+ * Given a JSON response from a Wikimedia Commons API call, extract the base thumbnail URL
24
+ * and the page ID, which can be used to get alt text later.
25
+ * We assume that there is only one meaningful result in the object.
26
+ *
27
+ * @param {Object} j
28
+ */
29
+ export function processImageInfoAPIJSON(j) {
30
+ let response = {}
31
+ if (j.query && j.query.pages) {
32
+ let page_ids = Object.keys(j.query.pages)
33
+ response['page_id'] = page_ids[0]
34
+ let data = j.query.pages[response['page_id']]
35
+ response['url'] = data.imageinfo[0].thumburl
36
+ if (data.entityterms && data.entityterms.label) {
37
+ response['label'] = data.entityterms.label[0]
38
+ }
39
+ }
40
+ return response
41
+ }
42
+
43
+ export default class WikipediaImage extends Media {
44
+
45
+ _loadMedia() {
46
+ var api_url,
47
+ image_width = this.options.width || 1000,
48
+ language_code = this.getLanguage().lang.toLowerCase(),
49
+ self = this;
50
+
51
+ try {
52
+ // Get Media ID
53
+ this.establishMediaID();
54
+
55
+ // API URL
56
+ api_url = `https://commons.wikimedia.org/w/api.php?action=query&titles=${this.media_id}&prop=imageinfo|entityterms&iiprop=url&&iiurlwidth=${image_width}&format=json&origin=*&wbetlanguage=${language_code}`
57
+ // API Call
58
+ getJSON(api_url, function(d) {
59
+ let response = processImageInfoAPIJSON(d)
60
+ if (response.url) {
61
+ self.base_image_url = response.url
62
+ self.page_id = response.page_id
63
+ if (!(self.data.alt) && response.label) {
64
+ self.data.alt = response.label
65
+ }
66
+
67
+ if (!self.options.background) {
68
+ self.createMedia();
69
+ }
70
+
71
+ self.onLoaded();
72
+ } else {
73
+ self.loadErrorDisplay(self._("wikipedia_image_load_err"));
74
+ }
75
+ });
76
+ } catch (e) {
77
+ self.loadErrorDisplay(self._(e.message_key));
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Analyze the URL provided in the data object passed to the constructor to extract
83
+ * the Wikimedia commons "page" name
84
+ */
85
+ establishMediaID() {
86
+ let media_id = computeMediaId(this.data.url)
87
+ if (media_id) {
88
+ this.media_id = media_id
89
+ } else {
90
+ throw new TLError(`Invalid Wikipedia Image URL`)
91
+ }
92
+ }
93
+
94
+ createMedia() {
95
+ var self = this;
96
+
97
+ // Photo
98
+ this._el.content_item = this.domCreate("img", "tl-media-item tl-media-image tl-media-wikipedia-image tl-media-shadow", this._el.content);
99
+
100
+ if (this.data.alt) {
101
+ this._el.content_item.alt = this.data.alt;
102
+ } else if (this.page_id) {
103
+ let wikibase_id = `M${this.page_id}`
104
+ let wikibase_url = `https://commons.wikimedia.org/w/api.php?action=wbgetentities&format=json&ids=${wikibase_id}&format=json&origin=*`
105
+ fetchJSON(wikibase_url).then(j => {
106
+ if (j.entities && j.entities[wikibase_id]) {
107
+ let labels = j.entities[wikibase_id].labels
108
+ let language_code = self.getLanguage().lang.toLowerCase()
109
+ let label = null
110
+ if (labels[language_code]) {
111
+ label = labels[language_code]
112
+ } else if (language_code.length > 2 && labels[language_code.substr(0, 2)]) {
113
+ label = labels[language_code.substr(0, 2)]
114
+ } else if (labels['en']) {
115
+ label = labels['en']
116
+ }
117
+ if (label) {
118
+ console.log(`wikibase_id: ${self.media_id} alt ${label.value}`)
119
+ self.data.alt = label.value
120
+ self._el.content_item.alt = self.data.alt
121
+ } else {
122
+ console.log(`wikibase_id: ${self.media_id} ain't got no alt`)
123
+ }
124
+ }
125
+ })
126
+ }
127
+
128
+ if (this.data.title) {
129
+ this._el.content_item.title = this.data.title;
130
+ }
131
+
132
+ // Media Loaded Event
133
+ this._el.content_item.addEventListener('load', function(e) {
134
+ self.onMediaLoaded();
135
+ });
136
+
137
+ // Set Image Source
138
+ this._el.content_item.src = this.getImageURL(this.options.width, this.options.height);
139
+ }
140
+
141
+ _getImageURL(w, h) {
142
+ if (w && this.base_image_url) {
143
+ let match = this.base_image_url.match(/(\/\d+px-)/)
144
+ if (match) {
145
+ return this.base_image_url.replace(match[1], `/${w}px-`) // Wikipedia will autoscale the image for us
146
+ }
147
+ }
148
+ // they don't always have that pattern!
149
+ return this.base_image_url
150
+ }
151
+
152
+ }
153
+
154
+ /**
155
+ *
156
+ *
157
+ * https://commons.wikimedia.org/w/api.php?action=query&titles=File:Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg&prop=imageinfo&iiprop=url&&iiurlwidth=1000
158
+ * https://commons.wikimedia.org/wiki/File:Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg
159
+ */
@@ -0,0 +1,48 @@
1
+ import { computeMediaId, processImageInfoAPIJSON } from "../WikipediaImage"
2
+ import { test, expect } from "@jest/globals"
3
+
4
+ const TEST_CASES = [
5
+ ['https://commons.wikimedia.org/wiki/https://commons.wikimedia.org/wiki/File:Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg', 'File:Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg'],
6
+ ['https://en.wikipedia.org/wiki/Main_Page#/media/File:Symphyotrichum_kentuckiense_233619783_(inflor).jpg', 'File:Symphyotrichum_kentuckiense_233619783_(inflor).jpg'],
7
+ ['https://es.wikipedia.org/wiki/Portal:M%C3%BAsica#/media/Archivo:David-Bowie_Chicago_2002-08-08_photoby_Adam-Bielawski-cropped.jpg', 'File:David-Bowie_Chicago_2002-08-08_photoby_Adam-Bielawski-cropped.jpg'],
8
+ ['https://commons.wikimedia.org/wiki/Category:Airdancers#/media/File:Sky-dancer-japan.jpg', 'File:Sky-dancer-japan.jpg']
9
+ ]
10
+
11
+ const IMAGE_INFO_JSON_RESPONSE = {
12
+ "batchcomplete": "",
13
+ "query": {
14
+ "normalized": [{
15
+ "from": "File:Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg",
16
+ "to": "File:Beryl-Quartz-Emerald-Zambia-33mm 0885.jpg"
17
+ }],
18
+ "pages": {
19
+ "10450749": {
20
+ "pageid": 10450749,
21
+ "ns": 6,
22
+ "title": "File:Beryl-Quartz-Emerald-Zambia-33mm 0885.jpg",
23
+ "imagerepository": "local",
24
+ "imageinfo": [{
25
+ "thumburl": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg/100px-Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg",
26
+ "thumbwidth": 100,
27
+ "thumbheight": 121,
28
+ "thumbmime": "image/jpeg",
29
+ "responsiveUrls": { "1.5": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg/150px-Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg", "2": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg/200px-Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg" },
30
+ "url": "https://upload.wikimedia.org/wikipedia/commons/d/df/Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg",
31
+ "descriptionurl": "https://commons.wikimedia.org/wiki/File:Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg",
32
+ "descriptionshorturl": "https://commons.wikimedia.org/w/index.php?curid=10450749",
33
+ "mime": "image/jpeg"
34
+ }]
35
+ }
36
+ }
37
+ }
38
+ }
39
+
40
+ test.each(TEST_CASES)("Ensure correct file identifier is extracted", (url, file_id) =>
41
+ expect(computeMediaId(url)).toBe(file_id)
42
+ )
43
+
44
+ test("get image info data", () => {
45
+ let response = processImageInfoAPIJSON(IMAGE_INFO_JSON_RESPONSE)
46
+ expect(response.url).toBe("https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg/100px-Beryl-Quartz-Emerald-Zambia-33mm_0885.jpg")
47
+ expect(response.page_id).toBe("10450749")
48
+ })
package/src/js/net/Net.js CHANGED
@@ -1477,7 +1477,7 @@ window.$ === undefined && (window.$ = Zepto)
1477
1477
 
1478
1478
  /**
1479
1479
  * Add a promisified option to better handle cases where we need the data
1480
- * synchronously.
1480
+ * asynchronously.
1481
1481
  * @param {String} url
1482
1482
  */
1483
1483
  $.fetchJSON = function(url) {
@@ -39,7 +39,7 @@ export class SlideNav {
39
39
  mergeData(this.data, data);
40
40
 
41
41
 
42
- this._el.container = DOM.create("button", "tl-slidenav-" + this.options.direction);
42
+ this._el.container = DOM.createButton("tl-slidenav-" + this.options.direction);
43
43
 
44
44
  if (Browser.mobile) {
45
45
  this._el.container.setAttribute("ontouchstart"," ");
@@ -80,7 +80,9 @@ export class TimeNav {
80
80
  this._eras = [];
81
81
  this.has_eras = false;
82
82
 
83
- // Groups Array
83
+ /**
84
+ * @type TimeGroup
85
+ */
84
86
  this._groups = [];
85
87
 
86
88
  // Row Height
@@ -97,10 +97,10 @@ export class MenuBar {
97
97
  }
98
98
 
99
99
  const firstTick = minor_ticks[0];
100
- const firstYear = this._getTickYear(firstTick);
100
+ const firstYear = firstTick.date.getFullYear();
101
101
 
102
102
  const lastTick = minor_ticks[minor_ticks.length - 1];
103
- const lastYear = this._getTickYear(lastTick);
103
+ const lastYear = lastTick.date.getFullYear();
104
104
 
105
105
  this.data.visible_ticks_dates = {
106
106
  start: firstYear,
@@ -110,10 +110,6 @@ export class MenuBar {
110
110
  this._updateZoomAriaLabels()
111
111
  }
112
112
 
113
- _getTickYear(tick) {
114
- return tick.date.data.date_obj.getFullYear();
115
- }
116
-
117
113
  setSticky(y) {
118
114
  this.options.menubar_default_y = y;
119
115
  }
@@ -158,10 +154,10 @@ export class MenuBar {
158
154
  _initLayout() {
159
155
 
160
156
  // Create Layout
161
- this._el.button_zoomin = DOM.create('button', 'tl-menubar-button', this._el.container);
162
- this._el.button_zoomout = DOM.create('button', 'tl-menubar-button', this._el.container);
163
- this._el.button_forwardtoend = DOM.create('button', 'tl-menubar-button', this._el.container);
164
- this._el.button_backtostart = DOM.create('button', 'tl-menubar-button', this._el.container);
157
+ this._el.button_zoomin = DOM.createButton('tl-menubar-button', this._el.container);
158
+ this._el.button_zoomout = DOM.createButton('tl-menubar-button', this._el.container);
159
+ this._el.button_forwardtoend = DOM.createButton('tl-menubar-button', this._el.container);
160
+ this._el.button_backtostart = DOM.createButton('tl-menubar-button', this._el.container);
165
161
 
166
162
  if (Browser.mobile) {
167
163
  this._el.container.setAttribute("ontouchstart", " ");