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.

266 lines
6.2 KiB

  1. const async = require('async')
  2. var wikidata = require('./wikidata')
  3. var wikipedia = require('./wikipedia')
  4. var cache = {}
  5. function ImageLoader (data) {
  6. this.sources = []
  7. this.found = []
  8. this.data = {}
  9. this.defaultCounter = {}
  10. this.parseObject(data)
  11. }
  12. ImageLoader.prototype.parseObject = function (data) {
  13. var img
  14. var id
  15. if (data.object.tags.image) {
  16. img = data.object.tags.image
  17. if (img.indexOf('File:') === 0) {
  18. id = img.substr(5)
  19. this.found.push(id)
  20. this.data[id] = {
  21. id: id,
  22. type: 'wikimedia'
  23. }
  24. } else if (img.indexOf('http://commons.wikimedia.org/wiki/File:') === 0) {
  25. id = decodeURIComponent(img.substr(39)).replace(/_/g, ' ')
  26. this.found.push(id)
  27. this.data[id] = {
  28. id: id,
  29. type: 'wikimedia'
  30. }
  31. } else if (img.indexOf('https://commons.wikimedia.org/wiki/File:') === 0) {
  32. id = decodeURIComponent(img.substr(40)).replace(/_/g, ' ')
  33. this.found.push(id)
  34. this.data[id] = {
  35. id: id,
  36. type: 'wikimedia'
  37. }
  38. } else {
  39. this.found.push(img)
  40. this.data[img] = {
  41. id: img,
  42. type: 'url'
  43. }
  44. }
  45. }
  46. if (data.object.tags.wikidata) {
  47. this.sources.push({
  48. type: 'wikidata',
  49. value: data.object.tags.wikidata
  50. })
  51. } else if (data.object.tags.wikipedia) {
  52. // only include wikipedia if no wikidata entry exists
  53. this.sources.push({
  54. type: 'wikipedia',
  55. value: data.object.tags.wikipedia
  56. })
  57. }
  58. if (data.object.tags.wikimedia_commons) {
  59. this.sources.push({
  60. type: 'wikimedia_commons',
  61. value: data.object.tags.wikimedia_commons
  62. })
  63. }
  64. cache[data.id] = this
  65. }
  66. ImageLoader.prototype.loadWikidata = function (src, callback) {
  67. var value = src.value
  68. wikidata.load(value, (err, result) => {
  69. async.series([
  70. (done) => {
  71. if (result && result.claims && result.claims.P18) {
  72. result.claims.P18.forEach((d) => {
  73. let id = d.mainsnak.datavalue.value
  74. if (this.found.indexOf(id) === -1) {
  75. this.found.push(id)
  76. this.data[id] = {
  77. id: id,
  78. type: 'wikimedia'
  79. }
  80. }
  81. })
  82. }
  83. done(null)
  84. },
  85. (done) => {
  86. // wikimedia commons
  87. if (result && result.claims && result.claims.P373) {
  88. result.claims.P373.forEach((d) => {
  89. let value = 'Category:' + d.mainsnak.datavalue.value
  90. this.sources.push({
  91. type: 'wikimedia_commons',
  92. value
  93. })
  94. })
  95. }
  96. done(null)
  97. }
  98. ], (err) => {
  99. callback(err)
  100. })
  101. })
  102. }
  103. ImageLoader.prototype.loadWikimediaCommons = function (src, callback) {
  104. var value = src.value
  105. let m = value.match(/^https?:\/\/commons\.wikimedia\.org\/wiki\/(.*)$/)
  106. if (m) {
  107. value = m[1]
  108. }
  109. if (value.substr(0, 9) === 'Category:') {
  110. var param = { page: value }
  111. if (src.continue) {
  112. param.continue = src.continue
  113. }
  114. ajax('ImageLoaderWikimediaCategoryList', param, function (result) {
  115. if (result.imageData) {
  116. result.imageData.forEach(function (d) {
  117. if (this.found.indexOf(d.id) === -1) {
  118. this.found.push(d.id)
  119. d.type = 'wikimedia'
  120. this.data[d.id] = d
  121. }
  122. }.bind(this))
  123. }
  124. if (result.continue) {
  125. this.sources.push({
  126. type: 'wikimedia_commons',
  127. value: value,
  128. continue: result.continue
  129. })
  130. }
  131. callback(null)
  132. }.bind(this))
  133. } else if (value.substr(0, 5) === 'File:') {
  134. var id = value.substr(5)
  135. if (this.found.indexOf(id) === -1) {
  136. this.found.push(id)
  137. this.data[id] = {
  138. id: id,
  139. type: 'wikimedia'
  140. }
  141. }
  142. callback(null)
  143. } else {
  144. callback(new Error('Can\'t parse value'))
  145. }
  146. }
  147. ImageLoader.prototype.loadWikipedia = function (src, callback) {
  148. var value = src.value
  149. wikipedia.getImages(value, function (err, result) {
  150. if (err) {
  151. return callback(err, null)
  152. }
  153. result.forEach(function (d) {
  154. if (this.found.indexOf(d) === -1) {
  155. this.found.push(d.id)
  156. d.type = 'wikimedia'
  157. this.data[d.id] = d
  158. }
  159. }.bind(this))
  160. callback(null)
  161. }.bind(this))
  162. }
  163. ImageLoader.prototype.handlePending = function () {
  164. var pending = this.pendingCallbacks
  165. delete this.pendingCallbacks
  166. pending.forEach(function (c) {
  167. this.callbackCurrent.apply(this, c)
  168. }.bind(this))
  169. }
  170. ImageLoader.prototype.callbackCurrent = function (index, options, callback) {
  171. if (index < this.found.length) {
  172. return callback(null, this.data[this.found[index]])
  173. }
  174. if (this.pendingCallbacks) {
  175. this.pendingCallbacks.push([ index, options, callback ])
  176. return
  177. }
  178. if (this.sources.length) {
  179. var src = this.sources.shift()
  180. this.pendingCallbacks = [ [ index, options, callback ] ]
  181. if (src.type === 'wikimedia_commons') {
  182. this.loadWikimediaCommons(src, this.handlePending.bind(this))
  183. } else if (src.type === 'wikidata') {
  184. this.loadWikidata(src, this.handlePending.bind(this))
  185. } else if (src.type === 'wikipedia') {
  186. this.loadWikipedia(src, this.handlePending.bind(this))
  187. }
  188. return
  189. }
  190. if (options.wrap && this.found.length) {
  191. var counter = this.defaultCounter
  192. if ('counter' in options) {
  193. counter = options.counter
  194. }
  195. counter.index = index - this.found.length
  196. return this.callbackCurrent(counter.index, options, callback)
  197. }
  198. callback(null, null)
  199. }
  200. /* options:
  201. * - wrap: whether to wrap to the first image after last (true/false)
  202. * - counter: use a different counter object (pass an empty object)
  203. */
  204. ImageLoader.prototype.first = function (options, callback) {
  205. var counter = this.defaultCounter
  206. if ('counter' in options) {
  207. counter = options.counter
  208. }
  209. counter.index = 0
  210. this.callbackCurrent(counter.index, options, callback)
  211. }
  212. ImageLoader.prototype.next = function (options, callback) {
  213. var counter = this.defaultCounter
  214. if ('counter' in options) {
  215. counter = options.counter
  216. }
  217. if (!('index' in counter) || counter.index === null) {
  218. counter.index = 0
  219. } else {
  220. counter.index ++
  221. }
  222. this.callbackCurrent(counter.index, options, callback)
  223. }
  224. module.exports = ImageLoader