You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

432 lines
9.6 KiB

7 years ago
7 years ago
7 years ago
7 years ago
6 years ago
6 years ago
6 years ago
6 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
3 years ago
6 years ago
6 years ago
  1. const async = require('async')
  2. const OverpassLayer = require('overpass-layer')
  3. var wikidata = require('./wikidata')
  4. const displayBlock = require('./displayBlock')
  5. var cache = {}
  6. var getAbstractCache = {}
  7. var loadClash = {}
  8. function stripLinks (dom) {
  9. var as = dom.getElementsByTagName('a')
  10. as = Array.prototype.slice.call(as)
  11. as.forEach(function (current) {
  12. while (current.firstChild) {
  13. current.parentNode.insertBefore(current.firstChild, current)
  14. }
  15. current.parentNode.removeChild(current)
  16. })
  17. }
  18. function prepare (div) {
  19. var i
  20. var contents = div.getElementsByTagName('div')
  21. for (i = 0; i < contents.length; i++) {
  22. if (contents[i].id === 'mw-content-text') {
  23. var content = contents[i]
  24. break
  25. }
  26. }
  27. if (!content) {
  28. return null
  29. }
  30. var p = content.firstChild.firstChild
  31. while (p && (p.tagName !== 'P' || p.className !== '' || p.textContent.match(/^\s*$/) || p.querySelector('span#coordinates'))) {
  32. p = p.nextSibling
  33. }
  34. if (!p) {
  35. return null
  36. }
  37. stripLinks(p)
  38. // first image
  39. var imgs = content.getElementsByTagName('img')
  40. for (i = 0; i < imgs.length; i++) {
  41. var img = imgs[i]
  42. // ignore icons
  43. if (img.width <= 64 && img.height <= 64) {
  44. continue
  45. }
  46. img.removeAttribute('width')
  47. img.removeAttribute('height')
  48. p.insertBefore(img, p.firstChild)
  49. break
  50. }
  51. return p.innerHTML
  52. }
  53. function get (value, callback) {
  54. var cacheId = options.data_lang + ':' + value
  55. if (cacheId in cache) {
  56. return callback(null, cache[cacheId])
  57. }
  58. if (cacheId in loadClash) {
  59. loadClash[cacheId].push(callback)
  60. return
  61. }
  62. loadClash[cacheId] = []
  63. ajax('wikipedia',
  64. {
  65. page: value,
  66. lang: options.data_lang
  67. },
  68. function (result) {
  69. if (!result.content) {
  70. return callback(new Error('error'), null)
  71. }
  72. cache[cacheId] = result
  73. callback(null, result)
  74. loadClash[cacheId].forEach(function (d) {
  75. d(null, result)
  76. })
  77. delete loadClash[cacheId]
  78. }
  79. )
  80. }
  81. function getAbstract (value, callback) {
  82. const cacheId = options.data_lang + ':' + value
  83. if (cacheId in getAbstractCache) {
  84. callback(null, getAbstractCache[cacheId])
  85. return getAbstractCache[cacheId]
  86. }
  87. get(value,
  88. function (err, result) {
  89. var text = null
  90. if (result) {
  91. var div = document.createElement('div')
  92. div.innerHTML = result.content
  93. text = prepare(div)
  94. text += ' <a target="_blank" href="' + result.languages[result.language] + '">' + lang('more') + '</a>'
  95. }
  96. getAbstractCache[cacheId] = text
  97. callback(err, text)
  98. }
  99. )
  100. }
  101. function updateDomWikipedia (dom, callback) {
  102. const wikipediaQueries = dom.querySelectorAll('.wikipedia')
  103. async.each(
  104. wikipediaQueries,
  105. (div, done) => {
  106. if (div.hasAttribute('data-done')) {
  107. return done()
  108. }
  109. getAbstract(div.getAttribute('data-id'),
  110. (err, result) => {
  111. if (result) {
  112. div.innerHTML = result
  113. div.setAttribute('data-done', 'true')
  114. }
  115. done()
  116. }
  117. )
  118. },
  119. () => {
  120. callback()
  121. }
  122. )
  123. }
  124. register_hook('show-popup', function (data, category, dom, callback) {
  125. updateDomWikipedia(dom, () => updateDomWikipedia(dom, () => {}))
  126. callback()
  127. })
  128. register_hook('show-details', function (data, category, dom, callback) {
  129. var ob = data.object
  130. var found = 0
  131. var foundPrefixes = []
  132. var finished = 0
  133. var errs = []
  134. var h, k, m
  135. var div = document.createElement('div')
  136. div.className = 'wikipedia'
  137. const mainBlock = document.createElement('div')
  138. div.appendChild(mainBlock)
  139. if ('wikipedia' in ob.tags) {
  140. foundPrefixes.push('')
  141. ob.tags.wikipedia.split(/;/g).forEach(value => {
  142. value = value.trim()
  143. found++
  144. showWikipedia(value, mainBlock, done)
  145. })
  146. }
  147. for (k in ob.tags) {
  148. m = k.match(/^(.*):wikipedia$/)
  149. if (m) {
  150. let prefix = m[1]
  151. const block = document.createElement('div')
  152. div.appendChild(block)
  153. h = document.createElement('h4')
  154. h.appendChild(document.createTextNode(lang('tag:' + prefix)))
  155. block.appendChild(h)
  156. foundPrefixes.push(prefix)
  157. ob.tags[k].split(/;/g).forEach(value => {
  158. value = value.trim()
  159. found++
  160. showWikipedia(value, block, done)
  161. })
  162. }
  163. m = k.match(/^((.*):)?wikipedia:(.*)$/)
  164. if (m) {
  165. let prefix = m[1]
  166. if (typeof prefix === 'undefined' && foundPrefixes.indexOf('') !== -1) {
  167. continue
  168. }
  169. if (foundPrefixes.indexOf(prefix) !== -1) {
  170. continue
  171. }
  172. let block = mainBlock
  173. if (prefix) {
  174. block = document.createElement('div')
  175. div.appendChild(block)
  176. h = document.createElement('h4')
  177. h.appendChild(document.createTextNode(lang('tag:' + prefix)))
  178. block.appendChild(h)
  179. }
  180. foundPrefixes.push(prefix)
  181. ;(m[3] + ':' + ob.tags[k]).split(/;/g).forEach(value => {
  182. found++
  183. showWikipedia(value, block, done)
  184. })
  185. }
  186. }
  187. if (ob.tags.wikidata && foundPrefixes.indexOf('') === -1) {
  188. foundPrefixes.push('')
  189. ob.tags.wikidata.split(/;/g).forEach(value => {
  190. value = value.trim()
  191. found++
  192. wikidata.load(value, function (err, result) {
  193. var x
  194. if (err) {
  195. return done(err)
  196. }
  197. if (!result.sitelinks) {
  198. return done(new Error('No Wikipedia links defined for Wikidata'))
  199. }
  200. if (options.data_lang + 'wiki' in result.sitelinks) {
  201. x = result.sitelinks[options.data_lang + 'wiki']
  202. return showWikipedia(options.data_lang + ':' + x.title, mainBlock, done)
  203. }
  204. for (k in result.sitelinks) {
  205. if (k === 'commonswiki') {
  206. continue
  207. }
  208. x = result.sitelinks[k]
  209. m = k.match(/^(.*)wiki$/)
  210. return showWikipedia(m[1] + ':' + x.title, mainBlock, done)
  211. }
  212. done()
  213. })
  214. })
  215. }
  216. for (k in ob.tags) {
  217. m = k.match(/^(.*):wikidata$/)
  218. if (m) {
  219. let prefix = m[1]
  220. found++
  221. if (foundPrefixes.indexOf(prefix) !== -1) {
  222. continue
  223. }
  224. foundPrefixes.push(prefix)
  225. const block = document.createElement('div')
  226. div.appendChild(block)
  227. h = document.createElement('h4')
  228. h.appendChild(document.createTextNode(lang('tag:' + prefix)))
  229. block.appendChild(h)
  230. ob.tags[k].split(/;/g).forEach(value => {
  231. value = value.trim()
  232. wikidata.load(value, (err, result) => {
  233. var x
  234. if (err) {
  235. return done(err)
  236. }
  237. if (!result.sitelinks) {
  238. return done()
  239. }
  240. if (options.data_lang + 'wiki' in result.sitelinks) {
  241. x = result.sitelinks[options.data_lang + 'wiki']
  242. return showWikipedia(options.data_lang + ':' + x.title, block, done)
  243. }
  244. for (k in result.sitelinks) {
  245. if (k === 'commonswiki') {
  246. continue
  247. }
  248. x = result.sitelinks[k]
  249. m = k.match(/^(.*)wiki$/)
  250. return showWikipedia(m[1] + ':' + x.title, block, done)
  251. }
  252. done()
  253. })
  254. })
  255. }
  256. }
  257. if (found) {
  258. displayBlock({
  259. dom,
  260. title: lang('tag:wikipedia'),
  261. content: div,
  262. order: 1
  263. })
  264. } else {
  265. callback()
  266. }
  267. function done (err) {
  268. finished++
  269. if (err) {
  270. errs.push(err)
  271. }
  272. if (found === finished) {
  273. callback(errs.length ? errs : null)
  274. }
  275. }
  276. })
  277. function showWikipedia (tagValue, dom, callback) {
  278. var block = document.createElement('div')
  279. block.className = 'loading'
  280. dom.appendChild(block)
  281. var l = document.createElement('div')
  282. l.innerHTML = '<i class="fa fa-spinner fa-pulse fa-fw"></i><span class="sr-only">Loading...</span>'
  283. l.className = 'loadingIndicator'
  284. block.appendChild(l)
  285. getAbstract(tagValue, function (err, text) {
  286. if (!text) {
  287. block.appendChild(document.createTextNode(lang('wikipedia:no-url-parse')))
  288. }
  289. var div = document.createElement('div')
  290. div.innerHTML = text
  291. block.appendChild(div)
  292. block.removeChild(l)
  293. block.className = 'clearfix'
  294. callback(err)
  295. })
  296. }
  297. function getImages (tagValue, callback) {
  298. var i
  299. get(tagValue, function (err, result) {
  300. if (err) {
  301. return callback(err, null)
  302. }
  303. var div = document.createElement('div')
  304. div.innerHTML = result.content
  305. var imgs = div.getElementsByTagName('img')
  306. var ret = []
  307. for (i = 0; i < imgs.length; i++) {
  308. var img = imgs[i]
  309. // ignore icons
  310. if (img.width <= 64 && img.height <= 64) {
  311. continue
  312. }
  313. img.removeAttribute('width')
  314. img.removeAttribute('height')
  315. var m = img.src.match(/^https?:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/\w+\/\w+\/([^/]+)/)
  316. if (m) {
  317. var file = decodeURIComponent(m[1]).replace(/_/g, ' ')
  318. ret.push({
  319. id: file,
  320. width: img.getAttribute('data-file-width'),
  321. height: img.getAttribute('data-file-height')
  322. })
  323. }
  324. }
  325. callback(null, ret)
  326. })
  327. }
  328. module.exports = {
  329. getImages: getImages
  330. }
  331. OverpassLayer.twig.extendFilter('wikipediaAbstract', function (value, param) {
  332. let result
  333. const cacheId = options.data_lang + ':' + value
  334. if (cacheId in getAbstractCache) {
  335. const text = getAbstractCache[cacheId]
  336. result = '<div class="wikipedia" data-id="' + value + '" data-done="true">' + text + '</div>'
  337. } else {
  338. result = '<div class="wikipedia" data-id="' + value + '"><a href="https://wikidata.org/wiki/' + value + '">' + value + '</a></div>'
  339. }
  340. return OverpassLayer.twig.filters.raw(result)
  341. })