User:Inductiveload/index preview.js
Jump to navigation
Jump to search
Note: After saving, changes may not occur immediately. Click here to learn how to bypass your browser's cache.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Cmd-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (Cmd-Shift-R on a Mac)
- Internet Explorer: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Clear the cache in Tools → Preferences
For details and instructions about other browsers, see Wikipedia:Bypass your cache.
Code that you insert on this page could contain malicious content capable of compromising your account. If you are unsure whether code you are adding to this page is safe, you can ask at the central discussion page, Scriptorium. The code will be executed when previewing this page under some skins, including Monobook. You can in the interim if you wish to refresh the content sooner under another skin. |
This script seems to have a documentation page at User:Inductiveload/index preview. |
//
// Tool to helpfully preview page in an index (good for checking page numbers)
//
(function($, mw) {
"use strict";
function install_css(css) {
$('head').append(`<style type="text/css">${css}</style>`);
}
var QUALITY = {
WITHOUT_TEXT: 0,
NOT_PROOFREAD: 1,
PROBLEMATIC: 2,
PROOFREAD: 3,
VALIDATED: 4,
};
var Preview = {
strings: {
show_grid: 'Show page grid',
show_grid_tooltip: 'Show a grid of all page images',
hide_grid: 'Hide page grid',
hide_grid_tooltip: 'Hide the grid of all page images',
mark_without_text: 'Without text',
without_text_summary: '/* Without text */',
problematic_summary: "/* Problematic */",
created_ok_msg: 'Page created successfully',
created_fail_msg: 'Page could not be created',
mark_raw_image: 'Raw img',
mark_raw_image_tooltip: 'Mark this page problematic, with the {{raw image}} template (for full-page images).',
raw_image_summary: "[[Template:raw image]]",
mark_without_text_tooltip: 'Mark this page as "without text" (for blank pages).',
mark_table: 'Table',
mark_table_tooltip: 'Mark this page problematic, with the {{missing table}} template (for full-page tables).',
table_summary: '[[Template:Missing table]]',
tool_name: "Index Preview",
summary_note: "using [[$1|$2]]",
},
tool_link: "User:Inductiveload/index preview",
tag_name: "index preview tool",
toast_timeout: 4000,
min_retry_timeout: 200,
max_retry_timeout: 2000,
popup_padding: 5,
popup_img_size: 350,
grid_img_size: 100,
grid_gap: 6,
thumb_rate_limit: 70 / 30,
thumb_rate_margin: 1.5,
access_key: 'G',
};
var window_manager;
/**
* Get the imageinfo for a given page and callback with it
* @param {[type]} img the file name (without NS)
* @param {[type]} page the page number
* @param {[type]} size the requested size
* @param {[type]} iiprop the requested properties, |-separated
* @param {[type]} ii_cb the function to call with the imageinfo
*/
function get_imageinfo_for_page(img, page, size, iiprop, ii_cb) {
var api = new mw.Api();
api.get({
'action': 'query',
'prop': 'imageinfo',
'titles': "File:" + img,
'formatversion': 2,
'format': 'json',
'iiprop': iiprop,
'iiurlparam': "page" + page + "-" + size + "px"
})
.done(function(data) {
ii_cb(data.query.pages[0].imageinfo[0]);
});
}
/*
* Do an action if a specific page exists, or not
*/
function if_page_exists(page, is_exists, pg_cb) {
var api = new mw.Api();
api.get({
'action': 'query',
'prop': 'pageprops',
'titles': page,
'formatversion': 2,
'format': 'json',
})
.done(function(data) {
var page = data.query.pages[0];
if ((is_exists && page.missing === undefined)
|| (!is_exists && page.missing === true)) {
pg_cb(page);
}
});
}
function update_labels(add) {
var text, tt;
if (add) {
text = Preview.strings.show_grid;
tt = Preview.strings.show_grid_tooltip;
} else {
text = Preview.strings.hide_grid;
tt = Preview.strings.hide_grid_tooltip;
}
$(".userjs-prp-index-grid-trigger a")
.text(text)
.attr("title", tt);
}
function activate_grid() {
if ($(".userjs-prp-index-grid").length > 0) {
$(".userjs-prp-index-grid").remove();
update_labels(true);
} else {
var filename = mw.config.get("wgTitle");
// The reason we do this, rather than scraping the pagelist is that
// the page list isn't guaranteed to have every single link in it
// TODO: fall back to pagelist for non DJVU/PDF files
get_imageinfo_for_page(filename, 1, 100, "dimensions|url", function(imageinfo) {
build_grid(filename, imageinfo.thumburl, imageinfo.pagecount);
update_labels(false);
});
}
}
function strip_ns(title) {
return title.replace(/^[A-Za-z]+:/, "");
}
/*
* Get the file and page number from a link
*/
function get_file_from_pagelink($link) {
const url = new URL($link.attr('href'), "https://" + mw.config.get("wgServer"));
var splt = url.pathname.split("/");
var parts = null;
if (splt[1] === "wiki") {
parts = [ strip_ns(splt[2]), splt[3] ];
} else if (splt[1] === "w") {
// redlink?
var title = url.searchParams.get("title").split("/");
parts = [ strip_ns(title[0]), title[1]];
} else {
console.error("Unknown URL structure: ", url);
}
return parts;
}
function get_random_integer(min, max) {
return Math.floor(Math.random() * (max - min + 1) ) + min;
}
/*
* Handler for clicks on page thumbnails
* Alt: show a popup and queue an zoomed image load into it
*
* $attachment_pt: where the popup attaches
* url_getter: function that find the url calls the given callback with that url
*/
function page_thumb_click(event, page_title, $attachment_pt, url_getter) {
if (!event.altKey) {
return;
}
var progbar = new OO.ui.ProgressBarWidget({
progress: false,
});
var popup = new OO.ui.PopupWidget({
$floatableContainer: $attachment_pt,
autoClose: true,
align: 'center',
classes: ["userjs-prp-page-preview-popup"],
hideWhenOutOfView: false,
data: {
page_title: page_title,
progbar: progbar,
}
});
popup.setAnchorEdge("bottom");
$('<div>')
.addClass('userjs-prp-page-popup-controls')
.appendTo(popup.$body);
$('<div>')
.addClass('userjs-prp-page-popup-image')
.append(progbar.$element)
.appendTo(popup.$body);
// Append and display
$(document.body).append(popup.$element);
popup.toggle(true);
url_getter(function(img_url) {
load_img_into_popup(img_url, popup);
});
if_page_exists(page_title, false, function() {
load_quick_tools_into_popup(popup);
});
}
function load_img_into_popup(img_url, popup) {
var img = $("<img>")
.attr("src", img_url)
.on('error', handle_load_error)
.on("load", function() {
popup.getData().progbar.toggle(false);
popup.$body
.find(".userjs-prp-page-popup-image")
.append(img);
var marg = popup.$body.outerWidth(true) - popup.$body.innerWidth() + Preview.popup_padding * 2;
popup.setSize(img.width() + marg, null, true);
}
);
}
function page_tool_button(text, title, onclick) {
var button = new OO.ui.ButtonWidget( {
label: text,
title: title,
flags: 'progressive'
});
button.on('click', onclick);
return button;
}
function load_quick_tools_into_popup(popup) {
var wot_button = page_tool_button(
Preview.strings.mark_without_text,
Preview.strings.mark_without_text_tooltip,
function() {
mark_without_text(popup.getData().page_title);
}
);
var raw_button = page_tool_button(
Preview.strings.mark_raw_image,
Preview.strings.mark_raw_image_tooltip,
function() {
mark_raw_image(popup.getData().page_title);
}
);
var table_button = page_tool_button(
Preview.strings.mark_table,
Preview.strings.mark_table_tooltip,
function() {
mark_table(popup.getData().page_title);
}
);
var btn_grp = new OO.ui.ButtonGroupWidget({
items: [wot_button, raw_button, table_button]
});
popup.$body.find(".userjs-prp-page-popup-controls")
.append(btn_grp.$element);
}
function construct_page_content(header, body, footer, quality) {
return '<noinclude><pagequality level="' + quality + '" ' +
'user="' + mw.config.get("wgUserName") + '" />' + header + '</noinclude>' +
body + '<noinclude>' + footer + '</noinclude>';
}
function set_page_quality(pg_title, quality) {
pg_title = pg_title.replace(/_/g, ' ');
$('a[title="' + pg_title + '"], a[title^="' + pg_title + ' "], '+
'.userjs-prp-index-grid-page[data-pg_title="' + pg_title + '"] .userjs-prp-index-grid-pageindex')
.addClass("quality" + quality)
.removeClass("new")
}
function create_page(pg_title, summary, header, body, footer, quality) {
var content = construct_page_content(header, body, footer, quality);
summary += "; " + Preview.strings.summary_note
.replace("$1", Preview.tool_link)
.replace("$2", Preview.strings.tool_name);
var api = new mw.Api();
api
.create(pg_title,
{
summary: summary,
tags: Preview.tag_name
},
content
)
.done(function() {
show_toast('success', Preview.strings.created_ok_msg, pg_title);
set_page_quality(pg_title, quality);
})
.fail(function(error, info) {
show_message(Preview.strings.created_fail_msg,
info.error.info + "\n" + pg_title);
});
}
function show_message(title, msg, button_text) {
var message_dialog = new OO.ui.MessageDialog();
window_manager.addWindows([message_dialog]);
window_manager.openWindow(message_dialog, {
title: title,
message: msg,
actions: [
{
action: 'accept',
label: button_text || "OK",
flags: 'primary'
}
]
});
}
function show_toast(type, title, message) {
var bubble_type = type || "info";
mw.notify(message, {
title: title,
type: bubble_type,
autoHideSeconds: Preview.toast_timeout
});
}
/*
* Mark a given page as a "raw image" page
*/
function mark_raw_image(pg_title) {
var summary = Preview.strings.problematic_summary +
" " + Preview.strings.raw_image_summary;
create_page(pg_title, summary,
'', '{{raw image|' + strip_ns(pg_title) + '}}', '', QUALITY.PROBLEMATIC);
}
/*
* Mark a given page as a "raw image" page
*/
function mark_table(pg_title) {
var summary = Preview.strings.problematic_summary +
" " + Preview.strings.table_summary;
create_page(pg_title, summary,
'', '{{missing table}}', '', QUALITY.PROBLEMATIC);
}
/*
* Mark a given page as "without text"
*/
function mark_without_text(pg_title) {
create_page(pg_title, Preview.strings.without_text_summary,
'', '', '', QUALITY.WITHOUT_TEXT);
}
var reload_queue = [];
var reload_timer = null;
function check_reload_queue() {
// slightly under the max rate limit once we hit the limiter
var timer_interval = Preview.thumb_rate_margin * (1000 / Preview.thumb_rate_limit);
if (reload_timer === null && reload_queue.length > 0) {
reload_timer = setInterval(function() {
var to_reload = reload_queue[0];
reload_queue.shift();
if (reload_queue.length === 0) {
window,clearInterval(reload_timer);
reload_timer = null;
}
to_reload.attr("src", to_reload.attr("src"));
}, timer_interval);
}
}
/*
* If an image fails to load (probably because of a rate limit), ask again
* in a short while.
*/
function handle_load_error(event) {
var $img = $(event.target);
// console.log("Load error", event);
reload_queue.push($img);
// we really want the first images to load first
reload_queue.sort(function(a, b) {
return a.data('pg_num') - b.data('pg_num');
});
check_reload_queue();
}
/*
* Copies prooreading status pagenum and href from an index-pagelist link
*/
function assign_page_status_from_link($pg_div, $link) {
var classes = ["quality0", "quality1", "quality2", "quality3", "quality4", "new"];
var $targets = $pg_div.find(".userjs-prp-index-grid-pageindex");
for (var i = 0; i < classes.length; ++i) {
if ($link.hasClass(classes[i])) {
$targets.addClass(classes[i]);
}
}
// chop off the hidden 00's
var children = $link[0].childNodes;
var text = children[children.length - 1].nodeValue;
$targets
.append(" — " + text)
.attr("href", $link.attr("href"));
// and assign to the image link
$pg_div.find(".userjs-prp-index-grid-page a")
.attr("href", $link.attr("href"));
}
function assign_page_status($pg_div) {
var pg_num = $pg_div.data("pg_num");
// first, see if we can sneak it out of the pagelist
// look for existing pages with quality classes
var selector = ".index-pagelist a[href$='/" + pg_num + "']";
$(selector).each(function(idx, elem) {
assign_page_status_from_link($pg_div, $(elem));
});
// and now for red links
selector = ".index-pagelist a[href*='/" + pg_num + "&action']";
$(selector).each(function(idx, elem) {
assign_page_status_from_link($pg_div, $(elem));
});
}
function build_grid(filename, url, num_pages) {
$(".userjs-prp-index-grid").parents("tr").first().remove();
var $tr = $("<tr>");
var $grid = $("<div>")
.addClass("userjs-prp-index-grid")
.appendTo($tr);
var $tbody = $(".index-pagelist").parents("tr").first().parent();
$tbody.append($tr);
var last_slash = url.lastIndexOf("/");
var page_prefix = mw.config.get("wgServer")
+ mw.config.get("wgArticlePath").replace("$1", "Page:" + filename);
for (var page = 1; page <= num_pages; ++page) {
var page_url = url.replace("/page1-", "/page" + page + "-");
var $page_div = $("<div>")
.addClass("userjs-prp-index-grid-page")
.attr( { 'data-pg_num': page } )
.attr( { 'data-pg_title': "Page:" + filename + "/" + page } )
.data('pg_num', page);
var $num = $("<div>")
.addClass("userjs-prp-index-grid-info")
.append($("<a>")
.addClass("userjs-prp-index-grid-pageindex")
.append("#" + page))
.appendTo($page_div);
var $a = $("<a>")
.addClass("popups_nopopup") // disable popups here
.attr("href", page_prefix + "/" + page)
.appendTo($page_div);
var $img = $("<img>")
.attr("src", page_url)
.on('error', handle_load_error)
.data('pg_num', page)
.click(function(event) {
var src = $(this).attr("src");
var url_getter = function(cb) {
var img_url = src.replace(/\d+px/, Preview.popup_img_size + "px");
cb(img_url);
};
// no ES-6 let, so cheat and use a data attribute
var page_title = "Page:" + filename + "/" + $(this).data('pg_num');
page_thumb_click(event,
page_title,
$(event.target).parents(".userjs-prp-index-grid-page").first(),
url_getter);
})
.appendTo($a);
assign_page_status($page_div);
$grid.append($page_div);
}
}
/*
* A pagelist item was clicked - look up the image URL and pop-up the image
*/
function on_pagelist_click(event) {
var $link_elem = $(event.target);
var img_page = get_file_from_pagelink($link_elem);
var size = Preview.popup_img_size;
var page_name = "Page:" + img_page[0] + "/" + img_page[1];
get_imageinfo_for_page(img_page[0], img_page[1], size, "url", function(imageinfo) {
var url_getter = function(url_cb) {
url_cb(imageinfo.thumburl);
};
page_thumb_click(event, page_name, $link_elem, url_getter);
});
}
function add_portlet(cb) {
var portlet = mw.util.addPortletLink(
'p-tb',
'#',
'',
't-show-grid',
'',
Preview.access_key || undefined,
);
$(portlet)
.addClass("userjs-prp-index-grid-trigger")
.click(function(e) {
e.preventDefault();
cb();
});
update_labels(true);
}
/*
* Add a button at the top right of the pagelist
* This is enWS specific and depends on the layout of
* MediaWiki:Proofreadpage index template
*/
function add_page_list_button($btn) {
$btn.css({float: "right"});
$(".index-pagelist").parents("td").first().find("> :first-child").prepend($btn);
}
function add_grid_button(cb) {
var $link = $("<a>")
.click(cb);
var $span = $("<span>")
.addClass("userjs-prp-index-grid-trigger")
.append("[", $link, "]");
add_page_list_button($span);
update_labels(true);
}
function init_index_grid_gadget() {
window_manager = new OO.ui.WindowManager();
$('body').append(window_manager.$element);
// console.log("Init Index Page Grid");
var max_w = Preview.grid_img_size * 10 + Preview.grid_gap * 10;
// Install CSS
install_css('\
.userjs-prp-index-grid {\
display: grid;\
grid-gap: ' + Preview.grid_gap + 'px;\
grid-template-columns: repeat(auto-fill, minmax(' + Preview.grid_img_size + 'px, 1fr));\
max-width: ' + max_w + 'px;\
}\
\
.userjs-prp-index-grid-page {\
text-align: center;\
position: relative;\
margin-bottom: 5px;\
}\
.userjs-prp-index-grid-info {\
width:100%;\
}\
.userjs-prp-page-popup-image img,\
.userjs-prp-index-grid-page img {\
max-width: 100%;\
height: auto;\
border: 1px solid lightgrey;\
}\
.userjs-prp-page-preview-popup .oo-ui-popupWidget-popup {\
padding: ' + Preview.popup_padding + 'px;\
}\
.userjs-prp-page-popup-image {\
margin-top: 5px;\
}\
.userjs-prp-page-popup-controls {\
text-align: center;\
font-size: 0.8rem;\
}\
');
// install the pagelist previewer always
$(".index-pagelist a")
.click(on_pagelist_click);
add_portlet(activate_grid);
add_grid_button(activate_grid);
}
mw.hook( 'ext.gadget.index-preview-grid.config' ).fire( Preview );
if (mw.config.get("wgCanonicalNamespace") === "Index") {
mw.loader.using(['mediawiki.util', 'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows'], function() {
init_index_grid_gadget();
});
}
}(jQuery, mediaWiki));