MediaWiki:Gadget-Stockphoto.js

/* * StockPhoto - shows download and attribution buttons * * Revision: 2020-10-16 * Dependencies: mediawiki.util, mediawiki.user * Source: https://commons.wikimedia.org/wiki/MediaWiki:Gadget-Stockphoto.js * * Copyright 2010-09/10 by Magnus Manske * Copyright 2011-2020 Timo Tijhof * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* eslint-env browser */ /* global $, mw */ (function { 'use strict';

var stockPhoto, modalLoad;

if ( // Already loaded  window.stockPhoto  // Unsupported skin  || !/^(?:vector|monobook|timeless)$/.test(mw.config.get('skin'))  // Not a file page  || mw.config.get('wgCanonicalNamespace') !== 'File'  // Not regular view  || mw.config.get('wgAction') !== 'view' ) { // Do not load return; }

/** * @param {string} opt.pageName * @param {string} opt.originalUrl * @param {jQuery} opt.dom Keys $aut, $src, $attr, $credit, $licenseAut, $creator * @param {jQuery} opt.$licenses */ function File(opt) { var re, file = this;

// Strip namespace prefix and file suffix. // Use spaces for underscore. file.title = opt.pageName.replace(/^[^:]+:|\.[^.]+$/g, '').replace(/_+/g, ' ');

file.url = opt.originalUrl;

file.backlink = 'https://gratispaideia.miraheze.org/wiki/' + mw.util.wikiUrlencode(opt.pageName);

re = new RegExp('\\.(?:' + stockPhoto.audio_only.map(mw.RegExp.escape).join('|') + ')$', 'i'); file.audio = re.test(opt.pageName);

this.gfdlNote = false; this.attrRequired = true; this.computeMetadata(opt.dom, opt.$licenses); }

File.prototype.getLicenseLabel = function (short) { if (/^cc[- ]/i.test(short)) { // CC-BY-SA-3.0 -> CC BY-SA 3.0 // CC BY-SA 3.0 -> CC BY-SA 3.0 return short.toUpperCase .replace(/^(CC)-/, '$1 ') .replace(/[- ]([\d.]+)$/, ' $1'); } return short; };

File.prototype.getLicensePrio = function (short) { // 1. Public domain or CC 0 return /^(public domain|cc0)/i.test(short) ? 1 :   // 2. Creative Commons, e.g. "CC-BY-SA-#" (most templates), // or "CC BY-SA #" (some templates) /^cc[- ]by(-sa)?[- ]\d/i.test(short) ? 2 :   // 3. Eveything else (e.g. GFDL) 3; };

// Set #author, #attribution, and #licenses File.prototype.computeMetadata = function (dom, $licenses) { var credit, creditHtml, attribution, licenses, via = stockPhoto.i18n.via_gratispaideia, file = this, author = dom.$aut.text.trim, source = dom.$src.text.trim;

// Clean up 'author' if (dom.$aut.find('.boilerplate').length) { author = ''; } if (author.match(/^[Uu]nknown$/)) { author = ''; } author = author.replace(/\s*\(talk\)$/i, ''); if (author.indexOf('Original uploader was') !== -1) { author = author.replace(/\s*Original uploader was\s*/g, ''); } if (author.slice(0, 3) === '[&#9660;]') { author = author.slice(3); // ▼ (Black Down-Pointing Triangle) author = author.split('Description')[0].trim; }

// Clean up 'source' if (dom.$src.find('.boilerplate').length) { source = ''; } if (dom.$src.find('.int-own-work').length) { source = ''; }

// Fallback if (!author && source.length < 50) { author = source; }

// Parse licenses licenses = []; $licenses.each(function {    var $tpl = $(this),      short = $tpl.find('.licensetpl_short').text.trim;    if (!short) { return; }    licenses.push({ link: $tpl.find('.licensetpl_link').text.trim, short: short, label: file.getLicenseLabel(short), prio: file.getLicensePrio(short), link_req: $tpl.find('.licensetpl_link_req').text.trim, attr_req: $tpl.find('.licensetpl_attr_req').text.trim }); });

// Use prefered if possible (like CommonsMetadata API) licenses.sort(function (a, b) { return a.prio - b.prio; }); if (licenses[0] && licenses[0].prio < 3) { licenses.splice(1); }

file.licenses = licenses.map(function (v) {   if (v.attr_req === 'false') {      file.attrRequired = false;    }    if (v.short.indexOf('GFDL') !== -1) {      file.gfdlNote = true;    }    if (v.link_req === 'true' && v.link) {      return {        html: '' + mw.html.escape(v.label) + '',        txt: v.label + ' <' + v.link + '>'      };    } else {      return { html: mw.html.escape(v.label), txt: v.label };    }  });

// Determine the credit line if (dom.$credit.length) { // Custom credit line credit = dom.$credit.text; creditHtml = dom.$credit.html; } if (!credit) { // No custom credit line // Default credit line: attribution + (if required) license + via promo if (dom.$attr.length) { attribution = dom.$attr.text; } else if (dom.$licenseAut.length) { attribution = dom.$licenseAut.text; } else if (dom.$creator.length) { attribution = dom.$creator.text; }   if (!attribution) { // No custom attribution // Default attribution: author or (short) source. // If no author/source, point to the file description page instead. attribution = (author || stockPhoto.i18n.see_page_for_author); }   credit = attribution + file.getLicense + ', ' + via; creditHtml = mw.html.element('a', {     href: file.backlink    }, attribution) + file.getLicense(true) + ', ' + mw.html.escape(via); }

file.credit = credit; file.creditHtml = creditHtml; };

File.prototype.getLicense = function (useHtml) { var l1, l2, licenses = this.licenses.map(function (l) {   return useHtml ? l.html : l.txt;  }); if (!licenses.length) { return ', ' + stockPhoto.i18n.see_page_for_license; } if (licenses.length === 1) { return ', ' + licenses[0]; } if (licenses.length >= 2) { l2 = licenses.pop; l1 = licenses.pop; licenses.push(l1 + ' ' + stockPhoto.i18n.or + ' ' + l2); } return ' (' + licenses.join(', ') + ')'; };

stockPhoto = { ui_icon_download: 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/92/Gnome-document-save.svg/50px-Gnome-document-save.svg.png', ui_icon_web: 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Gnome-emblem-web.svg/50px-Gnome-emblem-web.svg.png', ui_icon_wiki: 'https://static.miraheze.org/gratispaideiawiki/e/e6/Gratispaideia-favicon.png', ui_icon_email: 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/ee/Gnome-mail-send.svg/50px-Gnome-mail-send.svg.png', ui_icon_help: 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Dialog-information_on.svg/50px-Dialog-information_on.svg.png', ui_icon_remove: 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Emblem-unreadable.svg/20px-Emblem-unreadable.svg.png', information_template_hints: ['fileinfotpl_desc', 'fileinfotpl_src'], audio_only: ['midi', 'ogg', 'flac'], horizontal_ui: ['midi', 'ogg', 'flac', 'pdf', 'djvu'], ogg_icon: '/w/resources/assets/file-type-icons/fileicon-ogg.png',

init: function { var $enable, $orgItems, has_information, $imgBox, xoff, yoff, horizontal, html, $base, re; // Original filetoc items $orgItems = $('#filetoc').find('a[href="#file"], a[href="#filehistory"], a[href="#filelinks"], a[href="#metadata"], a[href="#globalusage"]').parent; if ($.cookie('StockPhotoDisabled')) { $enable = $('') .append($('').text(stockPhoto.i18n.reuse)) .click(function (e){       e.preventDefault;        $(this).remove;        $.cookie('StockPhotoDisabled', null, { path: '/' });       stockPhoto.init;      }); $('#filetoc').append($enable); $orgItems = $orgItems.add($enable); return; }   if (!$('#file').length) { return; }

has_information = stockPhoto.information_template_hints.some(function (v) {     return document.querySelector('#' + v);    }); // No    if (!has_information) { return; }

// Has one or more problemtags // Changed to also include renames and normal deletes if (document.querySelector('.nuke')) { return; }

// * ".multipageimage": For paged tiff files. // * "#file img": For all other images (e.g. JPEG, PNG, SVG, etc.). // * "#file .mediaContainer": For TMH-media (video files, audio files). We cannot use 'mwEmbedPlayer' or 'mwPlayerContainer' as those might not exist yet. $imgBox = $('.multipageimage, #file img,#file .mediaContainer'); xoff = $imgBox.width + 40; yoff = $('#file').position.top + 5;

stockPhoto.small_horizontal_layout = (     // Small for logged-in      !mw.user.isAnon      // Small for media with short height      || ($imgBox.height < 300)    );

re = new RegExp('\\.(?:' + stockPhoto.horizontal_ui.map(mw.RegExp.escape).join('|') + ')$', 'i');

horizontal = (     // Anything small      stockPhoto.small_horizontal_layout      || re.test(mw.config.get('wgTitle'))      // Window width      || (document.documentElement.clientWidth < 1030)    );

// Initialize values stockPhoto.share_this(-1); html = ' ';

$base = $(html).append(     stockPhoto.add_button_row(stockPhoto.ui_icon_download, stockPhoto.call_download, stockPhoto.i18n.download, stockPhoto.i18n.all_sizes),      stockPhoto.add_button_row(stockPhoto.ui_icon_web, stockPhoto.call_web, stockPhoto.i18n.use_this_file_web_short, stockPhoto.i18n.on_a_website),      stockPhoto.add_button_row(stockPhoto.ui_icon_wiki, stockPhoto.call_wiki, stockPhoto.i18n.use_this_file_wiki_short, stockPhoto.i18n.on_a_wiki),      stockPhoto.add_button_row(stockPhoto.ui_icon_email, 'mailto:?subject=' + encodeURIComponent(stockPhoto.file.title) + '&body=' + encodeURIComponent(stockPhoto.file.backlink + '\n\n' + stockPhoto.file.credit), stockPhoto.i18n.email_link_short, stockPhoto.i18n.to_this_file),     stockPhoto.add_button_row(stockPhoto.ui_icon_help, mw.util.getUrl('Project:Reusing_content_outside_Gratispaideia'), stockPhoto.i18n.information, stockPhoto.i18n.about_reusing),     $(' ')        .click(function  { $.cookie('StockPhotoDisabled', true, {           expires: 60, // days            path: '/'          }); $base.remove; $orgItems.show; })   );    if (stockPhoto.small_horizontal_layout) { $orgItems.hide; $('#filetoc').append($base); } else { $('#filetoc').after($base); } },

add_button_row: function (icon_url, fn, txt, html) { var ret, size = 50; if (stockPhoto.small_horizontal_layout) { size = 20; }   // HiDPI "Retina" icon var realSize = size; if (window.devicePixelRatio > 1.0) { realSize *= 2; }   icon_url = icon_url.replace('/50px-', '/' + realSize + 'px-');

ret = document.createElement(typeof fn === 'string' ? 'a' : 'span'); ret.className = 'stockphoto_buttonrow'; ret.title = txt + ' ' + html; if (typeof fn === 'string') { ret.href = fn; } else { ret.role = 'button'; ret.tabIndex = 0; ret.onclick = fn; }   ret.innerHTML = ' ' + '' + txt + ' ' + html + ' '; return ret; },

stockphoto_get_thumbnail_url: function (width) { var thumb_url, alt_title, last; if (stockPhoto.file.audio) { return stockPhoto.ogg_icon; }   alt_title = mw.config.get('wgCanonicalNamespace') + ':' + mw.config.get('wgTitle'); $('#file img').each(function {      var i = this.alt;      if (i && i !== alt_title) {        return;      }      thumb_url = this.src.split('/');    }); // Special case of mwEmbed rewrite if (!thumb_url && $('.mwEmbedPlayer img').length) { return $('.mwEmbedPlayer img').attr('src'); }   if (!thumb_url || !thumb_url.length) return;

last = thumb_url.pop.replace(/^\d+px-/, width + 'px-'); thumb_url.push(last); thumb_url = thumb_url.join('/'); return thumb_url; },

is_audio_video_asset: function (url) { return /(ogv|ogg|oga|ebm|wav)$/.test(url.slice(-3)); },

make_html_textarea: function { var width, type, height, thumb_url, t;

width = $('#stockphoto_html_select').val; type = $('input[name="stockphoto_code_type"]:checked').val;

// Iframe share for mwEmbed player if (stockPhoto.is_audio_video_asset(stockPhoto.file.backlink) && type === 'html') { // Get the ratio (from html or from mwEmbed player) height = $('.mwEmbedPlayer').length ? width * $('.mwEmbedPlayer').height / $('.mwEmbedPlayer').width : width * $('#file img,#file video').height / $('#file img,#file video').width;

if (height === 0) { // For audio that has zero height height = 20; }     $('#stockphoto_html').text(' '); return; }

thumb_url = stockPhoto.stockphoto_get_thumbnail_url(width);

if (type === 'html') { t = '<img width="' + width + '" alt="' +        mw.html.escape(stockPhoto.file.title) + '" src="' + thumb_url + '"></a>'; } else if (type === 'bbcode') { t = '[url=' + stockPhoto.file.backlink + '][img]' + thumb_url + '[/img][/url]\n[url=' + stockPhoto.file.backlink + ']' + stockPhoto.file.title + '[/url]\n' + stockPhoto.file.credit; }   $('#stockphoto_html').text(t); },

// Event 'change' on input#stockphoto_attribution_html refresh_attribution: function { $('#stockphoto_attribution').val(stockPhoto.file[this.checked ? 'creditHtml' : 'credit']); },

createDialogRow: function (label, prefill, id, prepend, append) { var idtext = id ? ('id="' + id + '"') : ''; return '<div class="stockphoto_dialog_row">' + label + ': ' + (prepend || ) + '<input type="text" readonly ' + idtext + ' onclick="select" value="' + mw.html.escape(prefill) + '">' + (append || ) + ' '; },

share_this: function (ui_mode) { var widths, html, dtitle, dl_links, best_fit, pixelStr, widthSearchMatch, imageWidth, power, i;

stockPhoto.file = new File({     pageName: mw.config.get('wgPageName'),      originalUrl: $('div.fullMedia a').prop('href') || '',      dom: {        $src: $('#fileinfotpl_src + td'),        $aut: $('#fileinfotpl_aut + td'),        $attr: $('.licensetpl_attr').eq(0),        $credit: $('#fileinfotpl_credit + td'),        $creator: $('#creator'),        $licenseAut: $('.licensetpl_aut').eq(0)      },      $licenses: $('.licensetpl')    });

// Grab width in pixel from DOM, and trim it down // This does not yet work for SVGs or videos widths = []; try { pixelStr = $('.fileInfo').contents.get(0).data; widthSearchMatch = /([0-9 ,.\u00a0]+)\s*×/.exec(pixelStr); imageWidth = parseInt(widthSearchMatch[1].replace(/[ ,.\u00a0]/g, ''), 10); if (isNaN(imageWidth)) { throw new Error('Cannot parse'); }

// Calculate to which power of two we should go     power = Math.floor(Math.log(imageWidth) / Math.log(2));

// Push 6 width to array for (i = 0; i < 5; i++) { widths.push(Math.pow(2, power-i)); }     widths = widths.reverse;

} catch (e) { widths = [640, 800, 1024]; }   if (ui_mode === -1) { return; }

modalLoad = modalLoad || mw.loader.using('jquery.ui'); html = ''; html += stockPhoto.createDialogRow(stockPhoto.i18n.page_url, stockPhoto.file.backlink); html += stockPhoto.createDialogRow(stockPhoto.i18n.file_url, stockPhoto.file.url); html += stockPhoto.createDialogRow(stockPhoto.i18n.attribution, stockPhoto.file.credit,     'stockphoto_attribution',      '<blockquote class="stockphoto_attribution_preview" onclick="nextSibling.select;return false;">' + stockPhoto.file.creditHtml + ' ',      ' <input id="stockphoto_attribution_html" type="checkbox"> <label for="stockphoto_attribution_html">' + stockPhoto.i18n.html + ' '    ); if (stockPhoto.file.gfdlNote) { html += '<span class="stockphoto_note">' + stockPhoto.i18n.gfdl_warning + ' '; }   if (!stockPhoto.file.attrRequired) { html += ' <span class="stockphoto_note">' + stockPhoto.i18n.no_attr + ' '; }

switch (ui_mode) { case 1:

dtitle = stockPhoto.i18n.download_this_file; if (stockPhoto.file.url) { html += ' ' + stockPhoto.i18n.download_image_file + ': '; dl_links = []; widths.forEach(function (v) {         if (stockPhoto.file.audio) {            return;          }          dl_links.push('' + v + 'px</a>');        }); dl_links.push('' + stockPhoto.i18n.full_resolution + '</a>'); if (dl_links.length) { html += dl_links.join(' | '); } else { html += ' + stockPhoto.i18n.not_available + '; }       html += ' '; }

break;

case 2: dtitle = stockPhoto.i18n.use_this_file_web; html += '<div class="stockphoto_dialog_row"> '; html += '<input type="radio" name="stockphoto_code_type" value="html" id="stockphoto_code_type_html" onchange="stockPhoto.make_html_textarea;" checked><label for="stockphoto_code_type_html">' + stockPhoto.i18n.html + ' '; html += '<input type="radio" name="stockphoto_code_type" value="bbcode" id="stockphoto_code_type_bbcode" onchange="stockPhoto.make_html_textarea;"><label for="stockphoto_code_type_bbcode">' + stockPhoto.i18n.bbcode + ' ';

html += '<select id="stockphoto_html_select" onchange="stockPhoto.make_html_textarea;">'; best_fit = 75; if (stockPhoto.file.audio) { best_fit = 120; html += ' 120' + stockPhoto.i18n.px_wide_icon + ' '; } else { widths.forEach(function (v) {         if (v <= $('#file img').width) {            best_fit = v;          }          html += '<option value="' + v + '">' + v + stockPhoto.i18n.px_wide + ' ';        }); }     html += ' '; html += '' + stockPhoto.i18n.embed_this_file + '<textarea onclick="select" id="stockphoto_html" readonly>'; html += ' ';

break;

case 3: dtitle = stockPhoto.i18n.use_this_file_wiki;

html = stockPhoto.createDialogRow(stockPhoto.i18n.thumbnail, ''); html += stockPhoto.createDialogRow(stockPhoto.i18n.image, '');

break; }

modalLoad.done(function {      $(' ').html(html).dialog({ modal: true, width: 610, height: 'auto', title: dtitle, close: function { $(this).remove; }     });      $('#stockphoto_html_select').val(best_fit);

stockPhoto.make_html_textarea; $('#stockphoto_attribution_html').on('change', stockPhoto.refresh_attribution); }); },

call_download: function { stockPhoto.share_this(1); },

call_web: function { stockPhoto.share_this(2); },

call_wiki: function { stockPhoto.share_this(3); },

i18n: { // Download: // - Button label download: 'Download', // - Button caption all_sizes: 'all sizes', // - Dialog title download_this_file: 'Download this file',

// Use web: // - Button label use_this_file_web_short: 'Use this file', // - Button caption on_a_website: 'on the web', // - Dialog title use_this_file_web: 'Use this file on the web',

// Use wiki: // - Button label use_this_file_wiki_short: 'Use this file', // - Button caption on_a_wiki: 'on a wiki', // - Dialog title use_this_file_wiki: 'Use this file on a wiki', thumbnail: 'Thumbnail', image: 'Image',

// Email: // - Button label email_link_short: 'Email a link', // - Button caption to_this_file: 'to this file',

// Reuse: // - Button label information: 'Information', // - Button caption about_reusing: 'about reusing',

// Disable (button caption) remove_icons: 'Remove these icons', // Enable (button label) reuse: 'Reuse this file',

via_gratispaideia: 'via Gratispaideia', see_page_for_author: 'See page for author', see_page_for_license: 'see page for license', page_url: 'Page URL', file_url: 'File URL', attribution: 'Attribution', no_attr: 'Attribution not legally required', or: 'or', gfdl_warning: 'Using this file might require attaching a full copy of the GFDL</a>', download_image_file: 'Download image file', full_resolution: 'Full resolution', not_available: 'not available', share_this_file: 'Share this file', embed_this_file: 'Embed this file', html: 'HTML', bbcode: 'BBCode', px_wide_icon: 'px wide (icon)', px_wide: 'px wide' } };

// Export window.stockPhoto = stockPhoto;

if (mw.config.get('wgUserLanguage') === 'en') { $(stockPhoto.init); } else { $.ajax({   url: mw.config.get('wgScript') + '?title=' + mw.util.wikiUrlencode('MediaWiki:Gadget-Stockphoto.js/' + mw.config.get('wgUserLanguage')) + '&action=raw&ctype=text/javascript',    dataType: 'script',    // For performance, use 'crossDomain' to trigger instead of XHR.    // Browsers do cache scripts, but not XHR.    crossDomain: true,    cache: true  }).then(stockPhoto.init); }

// on subpages MediaWiki:stockPhoto.js/langcode: // stockPhoto.i18n = { ... } });