Difference between revisions of "MediaWiki:Gadget-Stockphoto.js"
(modifications for www.academiclecturetranscripts.org) |
(Removing 'fileinfotpl_desc' from information_template_hints as it was removed from Template:Information (https://www.academiclecturetranscripts.org/index.php?title=Template:Information&oldid=36)) |
||
Line 226: | Line 226: | ||
ui_icon_help: 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Dialog-information_on.svg/50px-Dialog-information_on.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', | ui_icon_remove: 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Emblem-unreadable.svg/20px-Emblem-unreadable.svg.png', | ||
− | information_template_hints: [ | + | information_template_hints: ['fileinfotpl_src'], |
audio_only: ['midi', 'ogg', 'flac'], | audio_only: ['midi', 'ogg', 'flac'], | ||
horizontal_ui: ['midi', 'ogg', 'flac', 'pdf', 'djvu'], | horizontal_ui: ['midi', 'ogg', 'flac', 'pdf', 'djvu'], |
Latest revision as of 11:05, 13 August 2018
/* * StockPhoto - shows download and attribution buttons * * Dependencies: mediawiki.util, mediawiki.user, mediawiki.RegExp, jquery.ui.dialog * * @rev 2018-04-16 * @source https://commons.wikimedia.org/wiki/MediaWiki:Gadget-Stockphoto.js * * Copyright 2010-09/10 by Magnus Manske * Copyright 2011-2018 Timo Tijhof * (modified 2018 by David Kit Friedman for www.academiclecturetranscripts.org) * * 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; if ( // Already loaded window.stockPhoto // Unsupported skin || !(mw.config.get('skin') === 'vector' || mw.config.get('skin') === 'monobook') // Not a file page || mw.config.get('wgCanonicalNamespace') !== 'File' // Not regular view || mw.config.get('wgAction') !== 'view' ) { // Do not load in these cases return; } /** * @param {string} opt.pageName * @param {string} opt.originalUrl * @param {jQuery} opt.info Keys '$src', '$aut' * @param {jQuery} opt.license For credit line, keys '$aut', '$attr', '$credit' * @param {jQuery} opt.$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://www.academiclecturetranscripts.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.fromCommons = false; this.gfdlNote = false; this.attrRequired = true; this.computeMetadata(opt.info, opt.license, opt.$creator, opt.$licenses); } // Set #author, #attribution, #attributionHtml and #licenses File.prototype.computeMetadata = function (info, license, $creator, $licenses) { var attribution, attributionHtml, licenses, attrIsCredit, from, file = this, author = info.$aut.text().trim(), source = info.$src.text().trim(); // Clean up 'author' if (info.$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, ''); this.fromCommons = true; } if (author.substr(0, 3) === '[▼]') { author = author.substr(3); // ▼ (Black Down-Pointing Triangle) author = author.split('Description')[0].trim(); } // Clean up 'source' if (info.$src.find('.boilerplate').length) { source = ''; } if (author && info.$src.find('.int-own-work').length) { // Remove "own work" notice source = ''; this.fromCommons = true; } if (author && source.length > 50) { // Only use source in attribution if source is short source = ''; } // Decide author if (author && source) { this.author = author + ' (' + source + ')'; } else if (author) { this.author = author; } else if (source) { this.author = source; } else { this.author = ''; } // Decide attribution if (license.$credit.length) { attribution = license.$credit.text(); attributionHtml = license.$credit.html(); attrIsCredit = true; } else if (license.$attr.length) { attribution = license.$attr.text(); attributionHtml = license.$attr.html(); } else if (license.$aut.length) { attribution = license.$aut.text(); attributionHtml = license.$aut.html(); } else if ($creator.length) { attribution = $creator.text(); } else if (author && this.author) { // Default attribution: prepend author/source with "By" attribution = stockPhoto.i18n.by_u + ' ' + this.author; } else { // If no author/source, or only source attribution = stockPhoto.i18n.see_page_for_author; } this.attribution = attribution; this.attributionHtml = attributionHtml || attribution; // Parse licenses licenses = []; $licenses.each(function () { var $tpl = $(this); var cL = { link: $tpl.find('.licensetpl_link').text(), short: $tpl.find('.licensetpl_short').text(), long: $tpl.find('.licensetpl_long').text(), attr: $tpl.find('.licensetpl_attr').text(), aut: $tpl.find('.licensetpl_aut').text(), link_req: $tpl.find('.licensetpl_link_req').text(), attr_req: $tpl.find('.licensetpl_attr_req').text() }; if (cL.short) { licenses.push(cL); } }); this.licenses = licenses.map(function (v) { var txt; if (v.attr_req === 'false') { file.attrRequired = false; } if (v.short.indexOf('GFDL') !== -1) { file.gfdlNote = true; } if (v.link_req === 'true' && v.link) { txt = v.short + ' (' + v.link + ')'; } else { txt = v.short; } if (v.link) { return { html: '<a href="' + v.link + '">' + v.short + '</a>', txt: txt }; } else { return { html: txt, txt: txt }; } }); // Decide credit if (attrIsCredit) { this.credit = this.attribution; this.creditHtml = this.attributionHtml; } else { from = this.fromCommons ? stockPhoto.i18n.from_wikimedia_commons : stockPhoto.i18n.via_wikimedia_commons; this.credit = this.attribution + this.getLicense() + ', ' + from; this.creditHtml = this.attributionHtml + this.getLicense(true) + ', <a href="' + mw.html.escape(this.backlink) + '">' + from + '</a>'; } }; 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 >= 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_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, img_width, img_height, 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 = false; stockPhoto.information_template_hints.forEach(function (v) { if ($('#' + v).length) { has_information = true; } }); // No {{Information}} if (!has_information) { return; } // Has one or more problemtags // Changed to also include renames and normal deletes if (document.querySelector('.nuke')) { return; } img_width = $('.multipageimage, #file img').eq(0).width(); img_height = $('#file img').height(); xoff = img_width + 40; yoff = $('#file').position().top + 5; stockPhoto.small_horizontal_layout = ( // Small for logged-in !mw.user.isAnon() // Small for narrow media || (img_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), $('<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 icon_url = icon_url.replace('/50px-', window.devicePixelRatio > 1.0 ? '/40px-' : '/20px-'); } 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 && $('#mwe_ogg_player_1').length) { return $('#mwe_ogg_player_1').find('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) { var ext = url.substr(-3); return (ext === 'ogv' || ext === 'ogg' || ext === 'oga' || ext === 'ebm'); }, /** * @param e {jQuery.Event} [optional] */ make_html_textarea: function (e) { var width, type, height, thumb_url, t; if(e) { e.preventDefault(); } 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 = $('#mwe_ogg_player_1').width() ? width * $('#mwe_ogg_player_1').height() / $('#mwe_ogg_player_1').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]' + stockPhoto.file.getLicense() + ', ' + stockPhoto.i18n.by + ' ' + stockPhoto.file.author + ', ' + stockPhoto.i18n.from_wikimedia_commons; } $('#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, append) { var idtext = id ? ('id="' + id + '"') : ''; return '<div class="stockphoto_dialog_row"><b>' + label + ':</b><br><input type="text" readonly ' + idtext + ' onClick="select()" value="' + 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') || '', info: { $src: $('#fileinfotpl_src + td'), $aut: $('#fileinfotpl_aut + td') }, $creator: $('#creator'), license: { $aut: $('.licensetpl_aut').eq(0), $attr: $('.licensetpl_attr').eq(0), $credit: $('#fileinfotpl_credit + td') }, $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 = [75, 100, 120, 240, 500, 640, 800, 1024]; } if (ui_mode === -1) { return; } html = ''; html += stockPhoto.createDialogRow(stockPhoto.i18n.page_url, mw.html.escape(stockPhoto.file.backlink)); html += stockPhoto.createDialogRow(stockPhoto.i18n.file_url, mw.html.escape(stockPhoto.file.url)); html += stockPhoto.createDialogRow(stockPhoto.i18n.attribution, mw.html.escape(stockPhoto.file.credit), 'stockphoto_attribution', '<input id="stockphoto_attribution_html" type="checkbox"><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.html + '/' + stockPhoto.i18n.bbcode + ':</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, mw.html.escape('[[File:' + mw.config.get('wgTitle') + '|thumb|' + stockPhoto.file.title + ']]')); html += stockPhoto.createDialogRow(stockPhoto.i18n.image, mw.html.escape('[[File:' + mw.config.get('wgTitle') + '|' + stockPhoto.file.title + ']]')); break; } $('<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', from_wikimedia_commons: '', via_wikimedia_commons: '', by: 'by', by_u: 'By', 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', 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', // @performance Browsers do HTTP 304 cache for script, but not XHR. // Use 'crossDomain' to trigger <script> instead of XHR. crossDomain: true, cache: true }).then(stockPhoto.init); } // i18n on subpages [[MediaWiki:stockPhoto.js/langcode]]: // stockPhoto.i18n = { ... } }());