User:AntiCompositeNumber/Gadget-Stockphoto.js

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
/*
 * 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(?:-2022)?|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://commons.wikimedia.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_wikimedia_commons,
    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) === '[▼]') {
    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: '<a href="' + v.link + '">' + mw.html.escape(v.label) + '</a>',
        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://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Tango_style_Wikipedia_Icon.svg/50px-Tango_style_Wikipedia_Icon.svg.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 = $('<li>')
      .append($('<a href="#"></a>').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 {{Information}}
    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 = '<div class="';
    html += (horizontal ? 'stockphoto-layout-horizontal' : 'stockphoto-layout-vertical');
    html += (stockPhoto.small_horizontal_layout ? ' stockphoto-layout-horizontal-small' : '');
    html += '"';
    if (!horizontal) {
      if (document.querySelector('body.rtl')) {
        html += ' style="right: ' + xoff + 'px; top:' + yoff + 'px;"';
      } else {
        html += ' style="left: ' + xoff + 'px; top:' + yoff + 'px;"';
      }
    }
    html += '></div>';

    $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('Special:MyLanguage/Commons:Reusing_content_outside_Wikimedia'),
        stockPhoto.i18n.information, stockPhoto.i18n.about_reusing),
      $('<span class="stockphoto_buttonrow"><a title="' + stockPhoto.i18n.remove_icons + '" class="stockphoto_buttonrow_icon"><img src="' + stockPhoto.ui_icon_remove + '"></a></span>')
        .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 = '<span class="stockphoto_buttonrow_icon"><img width="' + size + '" height="' + size + '" src="' + icon_url + '"></span>' +
      '<span class="stockphoto_buttonrow_text"><a>' + txt + '</a><small>' + html + '</small></span>';
    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-').split('?')[0];
    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('<iframe src="' + stockPhoto.file.backlink +
        '?embedplayer=yes" width="' + width + '" height="'+ height + '" frameborder="0" ></iframe>');
      return;
    }

    thumb_url = stockPhoto.stockphoto_get_thumbnail_url(width);

    if (type === 'html') {
      t = '<a title="' + mw.html.escape(stockPhoto.file.credit) +
        '" href="' + stockPhoto.file.backlink + '"><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"><b>' + label + ':</b><br>' + (prepend || '') + '<input type="text" readonly ' + idtext + ' onclick="select()" value="' + mw.html.escape(prefill) + '">' + (append || '') + '</div>';
  },

  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 + '</blockquote>',
      ' <input id="stockphoto_attribution_html" type="checkbox">&nbsp;<label for="stockphoto_attribution_html">' + stockPhoto.i18n.html + '</label>'
    );
    if (stockPhoto.file.gfdlNote) {
      html += '<span class="stockphoto_note">' + stockPhoto.i18n.gfdl_warning + '</span>';
    }
    if (!stockPhoto.file.attrRequired) {
      html += '<br><span class="stockphoto_note">' + stockPhoto.i18n.no_attr + '</span>';
    }

    switch (ui_mode) {
    case 1:

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

      break;

    case 2:
      dtitle = stockPhoto.i18n.use_this_file_web;
      html += '<div class="stockphoto_dialog_row"><div style="float: right;">';
      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 + '</label> ';
      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 + '</label> ';

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

      break;

    case 3:
      dtitle = stockPhoto.i18n.use_this_file_wiki;

      html = stockPhoto.createDialogRow(stockPhoto.i18n.thumbnail, '[[File:' + mw.config.get('wgTitle') + '|thumb|' + stockPhoto.file.title + ']]');
      html += stockPhoto.createDialogRow(stockPhoto.i18n.image, '[[File:' + mw.config.get('wgTitle') + '|' + stockPhoto.file.title + ']]');

      break;
    }

    modalLoad.done(function () {
      $('<div style="display: none;"></div>').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_wikimedia_commons: 'via Wikimedia Commons',
    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 <a href="//en.wikipedia.org/wiki/GNU_Free_Documentation_License">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 <script> 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 = { ... }
}());