|
| 1 | + |
| 2 | +/** |
| 3 | + Renders a thumbnail alongside a matched cell when the reconcilition service |
| 4 | + is associated with the MediaInfo entities of a Wikibase instance. |
| 5 | + */ |
| 6 | +class ThumbnailReconRenderer extends ReconCellRenderer { |
| 7 | + |
| 8 | + constructor() { |
| 9 | + super(); |
| 10 | + this.supportedExtensions = [ |
| 11 | + 'jpg', 'jpeg', 'png', 'gif', 'svg', 'tiff', 'ogv', 'pdf', 'djvu', 'webm' |
| 12 | + ]; |
| 13 | + this.siteIriToMediaWikiRootUrl = new Map(); |
| 14 | + var self = this; |
| 15 | + $.ajax({ |
| 16 | + url: 'command/core/get-preference?' + $.param({ name: 'wikibase.manifests' }), |
| 17 | + success: function (data) { |
| 18 | + let wikibases = JSON.parse(data.value || '[]'); |
| 19 | + for (let manifest of wikibases) { |
| 20 | + try { |
| 21 | + let api = manifest.mediawiki.api; |
| 22 | + let siteIri = null; |
| 23 | + if (manifest.entity_types && manifest.entity_types.mediainfo) { |
| 24 | + if (manifest.entity_types.mediainfo.site_iri) { |
| 25 | + siteIri = manifest.entity_types.mediainfo.site_iri; |
| 26 | + } else { |
| 27 | + siteIri = manifest.wikibase.site_iri; |
| 28 | + } |
| 29 | + } |
| 30 | + if (siteIri) { |
| 31 | + self.siteIriToMediaWikiRootUrl.set(siteIri, api.substr(0, api.length - 'w/api.php'.length)); |
| 32 | + } |
| 33 | + } catch(error) { |
| 34 | + console.warn('Unsupported manifest format for Wikibase instance ' + manifest.mediawiki.name); |
| 35 | + } |
| 36 | + } |
| 37 | + }}); |
| 38 | + } |
| 39 | + |
| 40 | + render(rowIndex, cellIndex, cell, cellUI) { |
| 41 | + var self = this; |
| 42 | + var divContent = document.createElement('div'); |
| 43 | + var divContentRecon = $(divContent); |
| 44 | + var r = cell.r; |
| 45 | + if ( !r.service ) { |
| 46 | + return undefined; |
| 47 | + } |
| 48 | + |
| 49 | + var service = ReconciliationManager.getServiceFromUrl(r.service); |
| 50 | + var mediaWikiRootUrl = self.siteIriToMediaWikiRootUrl.get(service.identifierSpace); |
| 51 | + // if the reconciliation service is not associated with a Wikibase, defer to recon renderer |
| 52 | + if (!mediaWikiRootUrl) { |
| 53 | + return undefined; |
| 54 | + } |
| 55 | + |
| 56 | + // only display thumbnails for matched cells |
| 57 | + if (cell && 'r' in cell && cell.r.j === 'matched') { |
| 58 | + var match = cell.r.m; |
| 59 | + var a = $('<a></a>') |
| 60 | + .text(match.name) |
| 61 | + .attr("target", "_blank") |
| 62 | + .appendTo(divContentRecon); |
| 63 | + |
| 64 | + var bareFileName = match.name.substr('File:'.length).replaceAll(' ', '_'); |
| 65 | + var fileNameParts = bareFileName.split('.'); |
| 66 | + var extension = fileNameParts[fileNameParts.length - 1]; |
| 67 | + if (!self.supportedExtensions.includes(extension)) { |
| 68 | + // defer to the standard recon renderer |
| 69 | + return undefined; |
| 70 | + } |
| 71 | + |
| 72 | + var imageUrl = self.getThumbnailUrl(mediaWikiRootUrl, bareFileName, 320); |
| 73 | + |
| 74 | + if (service && (service.view) && (service.view.url)) { |
| 75 | + a.attr("href", encodeURI(service.view.url.replace("{{id}}", match.id))); |
| 76 | + } |
| 77 | + |
| 78 | + $('<span></span>').appendTo(divContentRecon); |
| 79 | + var thumbnailDiv = $('<div></div>') |
| 80 | + .addClass('media-file-thumbnail-in-cell') |
| 81 | + .appendTo(divContentRecon); |
| 82 | + var image = $('<img />') |
| 83 | + .attr('src', imageUrl) |
| 84 | + .appendTo(thumbnailDiv); |
| 85 | + image.on('click', function(evt) { |
| 86 | + self.showFullScreenPreview(mediaWikiRootUrl, bareFileName); |
| 87 | + }); |
| 88 | + $('<a></a>') |
| 89 | + .text($.i18n('core-views/choose-match')) |
| 90 | + .addClass('data-table-recon-action') |
| 91 | + .appendTo(divContentRecon) |
| 92 | + .on('click', function(evt) { |
| 93 | + self.doRematch(rowIndex, cellIndex, cell, cellUI); |
| 94 | + }); |
| 95 | + |
| 96 | + return divContent; |
| 97 | + } |
| 98 | + |
| 99 | + } |
| 100 | + |
| 101 | + showFullScreenPreview(mediaWikiRootUrl, bareFileName) { |
| 102 | + var self = this; |
| 103 | + var imageUrl = self.getThumbnailUrl(mediaWikiRootUrl, bareFileName); |
| 104 | + let div = $('<div></div>') |
| 105 | + .addClass('media-file-full-screen-preview') |
| 106 | + .appendTo($('body')); |
| 107 | + let img = $('<img />') |
| 108 | + .attr('src', self.getThumbnailUrl(mediaWikiRootUrl, bareFileName, 1920)) |
| 109 | + .appendTo(div); |
| 110 | + div.on('click', function(evt) { |
| 111 | + div.remove(); |
| 112 | + }); |
| 113 | + } |
| 114 | + |
| 115 | + /* |
| 116 | + Important: thumbnails take resources to generate, so it is worth sticking to the thumbnail sizes |
| 117 | + which are auto-generated at upload time: |
| 118 | + 320, 640, 800, 1024, 1280, 1920, 2560, 2880 |
| 119 | + |
| 120 | + See https://www.mediawiki.org/wiki/Requests_for_comment/Standardized_thumbnails_sizes |
| 121 | + */ |
| 122 | + getThumbnailUrl(mediaWikiRootUrl, bareFileName, width) { |
| 123 | + return `${mediaWikiRootUrl}w/thumb.php?f=${encodeURIComponent(bareFileName)}&w=${width}&h=${width}`; |
| 124 | + } |
| 125 | +} |
| 126 | + |
| 127 | +CellRendererRegistry.addRenderer('thumbnail', new ThumbnailReconRenderer(), 'recon'); |
0 commit comments