Benutzer:Schnark/js/imagepopups.js
< Benutzer:Schnark | js
Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
//Dokumentation unter [[Benutzer:Schnark/js/imagepopups]] <nowiki>
/*global mediaWiki*/
(function ($, mw) {
"use strict";
//jscs:disable maximumLineLength
var l10n = {
en: {
'size-bytes': '$1 B',
'size-kilobytes': '$1 KB',
'size-megabytes': '$1 MB',
'size-gigabytes': '$1 GB',
'schnark-imagepopups-close': '×', //icon for close button
'schnark-imagepopups-close-title': 'close', //tooltip for close button
'schnark-imagepopups-local-desc': 'local description page', //link text for local description page
'schnark-imagepopups-shared-desc': 'description page (Commons)', //link text for description page on Commons
'schnark-imagepopups-missing': '$1 (missing)', //appended to link to local description page if missing
'schnark-imagepopups-full-resolution': 'full resolution', //link text for full resolution image
'schnark-imagepopups-info': '$1 ($2)', //$1: link to full res., $2: info
'schnark-imagepopups-info-default': '$1×$2, $3, $4', //default info, $1: width, $2: height, $3: mime, $4: size
'schnark-imagepopups-info-paged': '$5 pages, $3, $4', //info for paged files like PDF, $1: width, $2: height, $3: mime, $4: size, $5: pages
'schnark-imagepopups-info-video': '$5, $3, $4', //info for videos, $1: width, $2: height, $3: mime, $4: size, $5: time
'schnark-imagepopups-sep': ' / ', //separator between links
'schnark-imagepopups-play': 'Play video', //tooltip for play button
'schnark-imagepopups-time-seperator': ':',
'schnark-imagepopups-unknown-length': 'unknown length', //shown for videos with unknown length
'schnark-imagepopups-prev-page': '<', //button for previous page
'schnark-imagepopups-prev-page-tooltip': 'Show previous page', //tooltip for this button
'schnark-imagepopups-next-page': '>', //button for next page
'schnark-imagepopups-next-page-tooltip': 'Show next page', //tooltip for this button
'schnark-imagepopups-current-page': ' · <b>page $1</b> · ', //text to indicate current page, $1 is replaced with a <span> containing the current page number
'schnark-imagepopups-3d-viewer': 'Open in 3D viewer',
'schnark-imagepopups-mediaviewer': 'You have enabled both this script and the MediaViewer. You should decide for one script and disable the other.'
},
de: {
'size-bytes': '$1 Bytes',
'schnark-imagepopups-close-title': 'Schließen',
'schnark-imagepopups-local-desc': 'lokale Bildbeschreibungsseite',
'schnark-imagepopups-shared-desc': 'Bildbeschreibungsseite (Commons)',
'schnark-imagepopups-missing': '$1 (fehlt)',
'schnark-imagepopups-full-resolution': 'Vollbild',
'schnark-imagepopups-info-paged': '$5 Seiten, $3, $4',
'schnark-imagepopups-play': 'Vido abspielen',
'schnark-imagepopups-unknown-length': 'unbekannte Dauer',
'schnark-imagepopups-prev-page-tooltip': 'Vorherige Seite zeigen',
'schnark-imagepopups-next-page-tooltip': 'Nächste Seite zeigen',
'schnark-imagepopups-current-page': ' · <b>Seite $1</b> · ',
'schnark-imagepopups-3d-viewer': 'Im 3-D-Betrachter öffnen',
'schnark-imagepopups-mediaviewer': 'Du hast sowohl dieses Skript als auch den Medienbetrachter aktiviert. Du solltest dich für ein Skript entscheiden und das andere deaktivieren.'
},
'de-ch': {
'schnark-imagepopups-close-title': 'Schliessen'
},
'de-formal': {
'schnark-imagepopups-mediaviewer': 'Sie haben sowohl dieses Skript als auch den Medienbetrachter aktiviert. Sie sollten sich für ein Skript entscheiden und das andere deaktivieren.'
}
//jscs:enable maximumLineLength
}, config = {
replaceTMHPopup: true,
useMwEmbed: true,
warnAboutMV: false,
css:
'.imagePopup {' +
'position: fixed;' +
'border: solid 3px #000;' +
'background-color: #fff;' +
'min-width: 10em;' +
'font-size: medium;' +
'}' +
'.imagePopupTitleBar {' +
'position: relative;' + //a absolute
'padding-right: 1em;' +
'overflow: hidden;' +
'text-overflow: ellipsis;' +
'white-space: nowrap;' +
'background-color: #000;' +
'color: #fff;' +
'font-weight: bold;' +
'text-align: center;' +
'cursor: move;' +
'}' +
'.imagePopupTitleBar a {' +
'display: block;' +
'position: absolute;' +
'top: -3px; right: -3px;' + //Rahmenbreite
'padding: 3px;' + //Rahmenbreite
'padding-bottom: 0px;' +
'background-color: #000;' +
'text-decoration: none;' +
'outline: none !important;' +
'color: #fff !important;' +
'transition: color 0.25s;' +
'cursor: pointer;' +
'}' +
'.imagePopupTitleBar a:hover {' +
'text-decoration: none;' +
'color: #d33 !important;' +
'}' +
'.imagePopup img:not(.playerPoster), .imagePopup .mediaContainer {' +
'padding: 5px;' +
'margin-left: auto;' +
'margin-right: auto;' +
'}' +
'.imagePopupDesc {' +
'max-height: 8em;' +
'overflow: auto;' +
'background-color: #fef6e7;' +
'padding: 5px;' +
'font-size: small;' +
'}',
zIndex: 120,
/*
evt. relevante Werte:
100 für obere Leiste in Vector
101 in OOUI (in Vector) für Dialoge etc.
110+ in EPOP (reagiert aber ohnehin nur auf Links im Haupttext)
200 in popuprefs.js
*/
sizes: [[320, 240], [640, 480], [800, 600], [1024, 768], [1280, 1024]], //from DefaultSettings.php
extensions: {
'png': 'image',
'apng': 'image',
'jpg': 'image',
'jpeg': 'image',
'jpe': 'image',
'gif': 'image',
'bmp': 'image',
'svg': 'image',
'xcf': 'image',
'webp': 'image',
'pdf': 'paged',
'tif': 'paged',
'tiff': 'paged',
'djvu': 'paged',
'djv': 'paged',
'ogg': 'video',
'ogx': 'video',
'ogm': 'video',
'ogv': 'video',
'oga': 'video',
'spx': 'video',
'opus': 'video',
'mp4': 'video',
'm4a': 'video',
'm4p': 'video',
'm4b': 'video',
'm4r': 'video',
'm4v': 'video',
'webm': 'video',
'stl': 'threed'
}
},
parseHTML = {
image: function ($file) {
var data = {};
data.url = getUrl($file);
data.sharedUrl = getSharedUrl(data.url);
data.localUrl = data.url.replace(/^(?:https?:)?\/\/[^\/]+\//, mw.config.get('wgServer') + '/');
data.title = getTitle(data.url);
data.$desc = getDesc($file, data.title);
data.lang = mw.util.getParamValue('lang', data.url) || '';
return data;
},
paged: function ($file) {
var data = parseHTML.image($file);
data.page = mw.util.getParamValue('page', data.url) || 1;
return data;
},
video: function ($file) {
var $container = $file.parent('.PopUpMediaTransform'), data = parseHTML.image($container);
data.$video = $($container.attr('videopayload')).find('video');
return data.$video.length === 1 ? data : false;
},
threed: function ($file) {
var data = parseHTML.image($file);
data.threedHash = '/media/File:' + data.title.replace(/ /g, '_');
return data;
}
},
getParam = {
image: function (data, w, h) {
var param = {
action: 'query',
prop: 'imageinfo',
iiprop: 'url|size|mime',
iiurlwidth: w,
iiurlheight: h,
titles: 'File:' + data.title,
format: 'json',
formatversion: 2
};
if (data.lang) {
param.iiurlparam = 'lang' + data.lang + '-' + w + 'px';
}
return param;
},
paged: function (data, w, h) {
var param = getParam.image(data, w, h);
param.iiurlparam = 'page' + data.page + '-' + w + 'px';
return param;
},
video: function (data, w, h) {
var param = getParam.image(data, w, h);
//TODO param.iiurlparam = 'seek:'
return param;
},
threed: function (data, w, h) {
return getParam.image(data, w, h);
}
},
extractData = {
image: function (json) {
var ii = json.imageinfo[0], data = {};
if (json.missing) {
data.localMissing = true;
}
if (json.imagerepository === 'shared') {
data.shared = true;
}
data.urlFull = ii.url;
data.info = mw.msg('schnark-imagepopups-info-default',
ii.width, ii.height, ii.mime, formatSize(ii.size));
data.urlThumb = ii.thumburl;
data.thumbwidth = ii.thumbwidth;
data.thumbheight = ii.thumbheight;
return data;
},
paged: function (json, htmlData) {
var ii = json.imageinfo[0], data = extractData.image(json);
if (ii.pagecount > 1) {
data.page = htmlData.page;
data.pages = ii.pagecount;
data.info = mw.msg('schnark-imagepopups-info-paged',
ii.width, ii.height, ii.mime, formatSize(ii.size), ii.pagecount);
} else {
htmlData.type = 'image';
}
return data;
},
video: function (json, htmlData) {
var ii = json.imageinfo[0], data = extractData.image(json);
data.info = mw.msg('schnark-imagepopups-info-video',
ii.width, ii.height, ii.mime, formatSize(ii.size), formatTime(ii.duration));
data.attr = getDataAttr(htmlData.$video);
//TODO über API auslesen
data.sources = [];
htmlData.$video.find('source').each(function () {
var $source = $(this);
data.sources.push($.extend(getDataAttr($source), {
src: $source.attr('src'),
type: $source.attr('type'),
'data-width': data.thumbwidth,
'data-height': data.thumbheight
}));
});
if (data.sources.length === 0) {
data.sources.push({
src: data.urlFull,
type: ii.mime,
'data-width': data.thumbwidth,
'data-height': data.thumbheight
});
}
data.tracks = [];
htmlData.$video.find('track').each(function () {
var $track = $(this);
data.tracks.push($.extend(getDataAttr($track), {
kind: $track.attr('kind'),
type: $track.attr('type'),
src: $track.attr('src'),
srclang: $track.attr('srclang'),
label: $track.attr('label')
}));
});
return data;
},
threed: function (json, htmlData) {
var data = extractData.image(json);
data.threedHash = htmlData.threedHash;
return data;
}
},
innerHTML = {
image: function (data) {
return mw.html.element('img', {width: data.width, height: data.height, src: data.url});
},
paged: function (data) {
return innerHTML.image(data) + mw.html.element('div', {style: 'text-align: center;'}, new mw.html.Raw(
mw.html.element('button', {'class': 'prev',
title: mw.msg('schnark-imagepopups-prev-page-tooltip')},
mw.msg('schnark-imagepopups-prev-page')) +
mw.html.element('span', {}, new mw.html.Raw(mw.msg('schnark-imagepopups-current-page',
mw.html.element('span', {'class': 'currentPage'}, data.data.page)))) +
mw.html.element('button', {'class': 'next',
title: mw.msg('schnark-imagepopups-next-page-tooltip')},
mw.msg('schnark-imagepopups-next-page'))
));
},
video: function (data) {
var containerAttr = {
'class': 'mediaContainer',
style: 'width:' + data.width + 'px;'
}, videoAttr = $.extend(data.attr, {
style: 'width:' + data.width + 'px; height:' + data.height + 'px;',
'class': 'kskin',
width: data.width,
height: data.height,
poster: data.url,
controls: true,
autoplay: true
}), video = mw.html.element('video', videoAttr, new mw.html.Raw(
data.data.sources.map(function (attr) {
return mw.html.element('source', attr);
}) +
data.data.tracks.map(function (attr) {
mw.html.element('track', attr);
})
));
return mw.html.element('div', {'class': 'containerParent'}, new mw.html.Raw(
mw.html.element('div', containerAttr, new mw.html.Raw(video))
));
},
threed: function (data) {
return innerHTML.image(data) + mw.html.element('div', {style: 'text-align: center;'}, new mw.html.Raw(
mw.html.element('button', {'class': 'threed-viewer'}, mw.msg('schnark-imagepopups-3d-viewer'))
));
}
},
setEvents = {
image: function () {},
paged: function ($popup, data) {
function changePage (diff) {
var page = Number($popup.find('.currentPage').text()) + diff, $img, src;
if (page < 1 || page > data.data.pages) {
return;
}
$popup.find('button.prev').prop('disabled', page === 1);
$popup.find('button.next').prop('disabled', page === data.data.pages);
$popup.find('.currentPage').text(page);
$img = $popup.find('img');
src = $img.attr('src').replace(/page\d+/, 'page' + page);
$img.attr('src', '');
$img.attr('src', src);
}
$popup.find('button.prev').on('click', function () {
changePage(-1);
}).prop('disabled', data.data.page === 1);
$popup.find('button.next').on('click', function () {
changePage(/*+*/1);
}).prop('disabled', data.data.page === data.data.pages);
},
video: function ($popup) {
if (config.useMwEmbed) {
mw.hook('wikipage.content').fire($popup.find('.containerParent'));
}
},
threed: function ($popup, data) {
$popup.find('button.threed-viewer').on('click', function () {
location.hash = data.data.threedHash;
});
}
};
function initL10N (l10n, keep) {
var i, chain = mw.language.getFallbackLanguageChain();
keep = $.grep(mw.messages.get(keep), function (val) {
return val !== null;
});
for (i = chain.length - 1; i >= 0; i--) {
if (chain[i] in l10n) {
mw.messages.set(l10n[chain[i]]);
}
}
mw.messages.set(keep);
}
function random (x) {
var s = 0, i, n = 6;
for (i = 0; i < n; i++) {
s += Math.random();
}
return Math.round((s / n) * x);
}
function formatSize (s) {
var i = 0, sizes = ['size-bytes', 'size-kilobytes', 'size-megabytes', 'size-gigabytes'];
while (s > 1024 && i < sizes.length - 1) {
s /= 1024;
i++;
}
return mw.msg(sizes[i], Math.round(s));
}
function formatTime (s) {
var hh, mm, ss, colon = mw.msg('schnark-imagepopups-time-seperator');
if (s === undefined) {
return mw.msg('schnark-imagepopups-unknown-length');
}
hh = Math.floor(s / 3600);
s -= hh * 3600;
mm = Math.floor(s / 60);
s -= mm * 60;
ss = Math.round(s);
if (ss === 60) {
ss = 0;
mm++;
}
if (mm === 60) {
mm = 0;
hh++;
}
if (hh > 0 && mm < 10) {
mm = '0' + String(mm);
}
if (ss < 10) {
ss = '0' + String(ss);
}
return (hh > 0 ? hh + colon : '') + mm + colon + ss;
}
function getTopLeft (w, h) {
w = $(window).width() - w - 20;
h = $(window).height() - h - 120;
if (w < 0) {
w = 0;
}
if (h < 0) {
h = 0;
}
return [random(h), random(w)];
}
function getStyle (data) {
var topLeft = getTopLeft(data.width, data.height);
return 'z-Index:' + config.zIndex++ + '; width:' + (data.width + 10) + 'px;' +
'top:' + topLeft[0] + 'px; left:' + topLeft[1] + 'px;';
}
function makeDraggable ($element, $handle) {
var diff;
function dragHandler (e) {
e.preventDefault();
e.stopPropagation();
$element.css({
top: e.clientY + diff.y,
left: e.clientX + diff.x
});
}
$handle.on('mousedown', function (e) {
var pos, $doc;
if ((e.which && e.which !== 1) || e.shiftKey || e.altKey || e.ctrlKey || e.metaKey) {
return;
}
if ($(e.target).is('a')) {
return;
}
e.preventDefault();
pos = $element.position();
diff = {x: pos.left - e.clientX, y: pos.top - e.clientY};
$doc = $(document);
$doc.on('mousemove', dragHandler);
$doc.on('mouseup', function () {
$doc.off('mousemove', dragHandler);
});
});
}
function showPopup (data, type) {
var $popup = $(
mw.html.element('div', {'class': 'imagePopup', style: getStyle(data)}, new mw.html.Raw(
mw.html.element('div', {'class': 'imagePopupTitleBar'}, new mw.html.Raw(
mw.html.element('span', {title: data.title, 'class': 'imagePopupTitle'}, data.title) +
mw.html.element('a', {'class': 'close-link',
title: mw.msg('schnark-imagepopups-close-title'), href: '#'},
mw.msg('schnark-imagepopups-close'))
)) +
innerHTML[type](data) +
mw.html.element('div', {'class': 'imagePopupDesc mw-body-content mw-parser-output'}, new mw.html.Raw(data.info))
)));
if (data.$desc) {
$popup.find('.imagePopupDesc').prepend(data.$desc);
}
mw.hook('wikipage.content').fire($popup.find('.imagePopupDesc'));
$popup.appendTo($('body'))
.on('mousedown', function () {
$popup.css({zIndex: config.zIndex++});
})
.find('.close-link').on('click', function () {
var video = $popup.find('video')[0];
if (video && video.pause) { //Firefox plays even removed videos
video.pause();
}
$popup.remove();
return false;
});
makeDraggable($popup, $popup.find('.imagePopupTitleBar'));
setEvents[type]($popup, data);
}
function parseHTMLX ($file) {
var href = $file.attr('href'),
ext = /\.(\w+)(?:&|$)/.exec(href || ''),
type, data;
ext = ((ext && ext[1]) || '').toLowerCase();
if (!ext || !config.extensions[ext]) {
return false;
}
type = config.extensions[ext];
data = parseHTML[type]($file);
data.type = type;
return data;
}
function getUrl ($file) {
var url = $file.attr('href');
if (url) {
return url;
}
url = $file.next('.thumbcaption').find('.magnify a').attr('href');
if (url) {
return url;
}
url = $file.find('a').attr('href');
return mw.util.getUrl(mw.config.get('wgFormattedNamespaces')[6] + ':' +
decodeURIComponent(url.replace(/^.*\//, '')));
}
function getTitle (url) {
var title = mw.util.getParamValue('title', url),
re = new RegExp('^.*?(?:File|' + mw.util.escapeRegExp(mw.config.get('wgFormattedNamespaces')[6]) + '):');
if (title) {
return title.replace(re, '').replace(/_/g, ' ');
}
return decodeURIComponent(url).replace(re, '').replace(/_/g, ' ');
}
function getSharedUrl (url) {
var re = new RegExp(
'(?:File|' + mw.util.escapeRegExp(encodeURIComponent(mw.config.get('wgFormattedNamespaces')[6])) + '):'
);
return url.replace(re, 'File:').replace(/^(?:(?:https?:)?\/\/[^\/]+)?\//, 'https://commons.wikimedia.org/');
}
function getDataAttr ($el) {
if ($el.length === 0) {
return {};
}
var raw = $el[0].attributes,
attrs = {},
i;
for (i = 0; i < raw.length; i++) {
if (raw[i].nodeName.indexOf('data-') === 0) {
attrs[raw[i].nodeName] = raw[i].nodeValue;
}
}
return attrs;
}
function getDesc ($file, title) {
var alt,
$desc = false,
//try to find caption
$caption = $file.next('.thumbcaption');
if ($caption.length === 0) {
//next try: perhaps a gallery
$caption = $file.parents('.thumb').next('.gallerytext').find('p');
}
if ($caption.length === 0) {
//next try: new gallery style
$caption = $file.parents('.thumb').next('.gallerytextwrapper').find('.gallerytext p');
}
if ($caption.length === 1) { //found something?
$desc = $caption.clone();
$desc.find('div.magnify').remove();
}
if (!$desc) { //last try
alt = $file.find('img').attr('alt') || '';
if (alt !== title && alt !== '') {
$desc = $('<p>').text(alt);
}
}
return $desc;
}
function makeFileInfo (htmlData, data) {
var info = [], localDesc;
localDesc = mw.html.element('a', {href: htmlData.localUrl}, mw.msg('schnark-imagepopups-local-desc'));
if (data.localMissing) {
localDesc = mw.msg('schnark-imagepopups-missing', localDesc);
}
info.push(localDesc);
if (data.shared) {
info.push(mw.html.element('a', {href: htmlData.sharedUrl}, mw.msg('schnark-imagepopups-shared-desc')));
}
info.push(mw.msg('schnark-imagepopups-info',
mw.html.element('a', {href: data.urlFull}, mw.msg('schnark-imagepopups-full-resolution')), data.info));
info = info.join(mw.msg('schnark-imagepopups-sep'));
if (config.warnAboutMV) {
info += '<br>' + mw.msg('schnark-imagepopups-mediaviewer');
}
return info;
}
function processFile ($file) {
var htmlData = parseHTMLX($file), is;
if (!htmlData) {
return false;
}
is = config.sizes[mw.user.options.get('imagesize') || 2] || [800, 600];
$.getJSON(mw.util.wikiScript('api'), getParam[htmlData.type](htmlData, is[0], is[1])).then(function (json) {
var p, data;
if (!json || !json.query || !json.query.pages) {
return;
}
p = json.query.pages[0];
if (!p) {
return;
}
data = extractData[htmlData.type](p, htmlData);
showPopup({
title: htmlData.title,
url: data.urlThumb,
$desc: htmlData.$desc,
info: makeFileInfo(htmlData, data),
width: data.thumbwidth,
height: data.thumbheight,
data: data
}, htmlData.type);
});
return true;
}
function onImageClick (e) {
/*jshint validthis: true*///Event-Handler
if ((e.which && e.which !== 1) || e.shiftKey || e.altKey || e.ctrlKey || e.metaKey) {
return;
}
if (processFile($(this))) {
e.preventDefault();
}
}
function run ($content) {
if (config.replaceTMHPopup && mw.loader.getState('mw.PopUpMediaTransform')) {
mw.loader.using('mw.PopUpMediaTransform').then(function () {
$content.find('.PopUpMediaTransform a').off('click').on('click', onImageClick);
});
}
$content.find('a.image').filter(':has(img)').on('click', onImageClick);
}
function init () {
if (mw.config.get('wgMediaViewerOnClick')) {
config.warnAboutMV = true;
mw.config.set('wgMediaViewerOnClick', false);
}
initL10N(l10n, ['size-bytes', 'size-kilobytes', 'size-megabytes', 'size-gigabytes']);
mw.util.addCSS(config.css);
mw.hook('wikipage.content').add(run);
}
mw.hook('userjs.load-script.imagepopups').fire(config);
mw.loader.using(['mediawiki.language', 'mediawiki.util', 'user.options']).then(init);
})(jQuery, mediaWiki);
//</nowiki>