User:Sohom Datta/jump to file.js
Appearance
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. |
![]() | Documentation for this user script can be added at User:Sohom Datta/jump to file. |
/* ==================================================================
* jump_to_file.js
*
* Adds a link from Page to the transcluding mainspace page
*
* Adds a link to the file at Commons from a Page: namespace page,
* an Index: page or a mainspace page with a "source" tab
*
* Adds a link to the work and hi-res page image at source (e.g. IA) if possible.
*
* Configuration is via "High-res options" in the sidebar.
* ================================================================== */
/* eslint-disable
camelcase, no-use-before-define, no-var
*/
( function () {
'strict';
// reference pre-loaded libraries from global scope
var getGlobalLibrary = function ( key ) {
var scriptName = window.ScriptDeps && window.ScriptDeps[ key ] || key;
return window[ scriptName ];
};
var debugSuffix = '';
/* debug-replace: debugSuffix = '.debug'; */
var ILUI = getGlobalLibrary( 'iltools.ui' + debugSuffix );
/* ======================================================================== */
// MW Options and LocalStorage JSON interfaces
var persistUserOption = function ( key, data ) {
new mw.Api().saveOption( 'userjs-' + key, JSON.stringify( data ) );
mw.user.options.set( key, data );
};
var getUserOption = function ( key ) {
var data = mw.user.options.get( 'userjs-' + key ) || '';
try {
data = JSON.parse( data );
} catch ( e ) {
data = {};
}
return data;
};
var persistLocalStorage = function ( key, data ) {
window.localStorage.setItem( 'userjs-' + key, JSON.stringify( data ) );
};
var getLocalStorage = function ( key ) {
var lsData = window.localStorage.getItem( 'userjs-' + key ) || '';
try {
lsData = JSON.parse( lsData );
} catch ( e ) {
lsData = {};
}
return lsData;
};
/* ======================================================================== */
var JumpToFile = {
signature: 'JumpToFile',
strings: {
link_title_pat: 'Page transcluded in $1',
go_to_file_commons: 'Go to file at Commons',
go_to_file_ns: 'Go to file in File namespace',
visit_pat: 'Visit document at $1',
portlet_title: 'Links',
portlet_id: 'p-jumptofile',
hiResOptions: 'High-res options',
hiResOptionsTip: 'Change settings for download of high-res options'
},
state: {
offset: 0,
loadHiRes: false
},
// for things that probably don't change often, allow to cache the results
// for a short while to speed up scrubbing though pages
maxage: 120,
pageIndex: getPageIndex(),
transcludeIcon: '//upload.wikimedia.org/wikipedia/commons/thumb/9/92/Open_book_nae_02.svg/25px-Open_book_nae_02.svg.png',
pageAnchorPrefix: 'pageindex_',
useHighresImg: true,
infoHostname: 'https://pagelister.toolforge.org',
fileIcon: '//upload.wikimedia.org/wikipedia/commons/5/56/Book_go.png',
showIiifLinks: false
};
function getPageIndex() {
if ( mw.config.get( 'wgCanonicalNamespace' ) === 'Page' ) {
var num = mw.config.get( 'wgPageName' ).substring( mw.config.get( 'wgPageName' ).lastIndexOf( '/' ) + 1 );
return parseInt( num );
}
return null;
}
function addLinks(links) {
for (let i = 0; i < links.length; ++i) {
let portletLink = mw.util.addPortletLink(
JumpToFile.strings.portlet_id,
links[i].href,
' ' + links[i].title,
links[i].id
);
if (links[i].icon) {
$(portletLink).find('a').prepend(
$('<img>')
.attr({
src: links[i].icon,
height: 16
})
);
}
if (links[i].class) {
// The following classes are used here:
// * srcimglink
// * transclusion-link
$(portletLink).find('a').addClass(links[i].class);
}
}
}
function api_get_tranclusions( namespaces, callback ) {
var ns_str = namespaces.join( '|' ),
api = new mw.Api();
api.get( {
action: 'query',
format: 'json',
list: 'embeddedin',
einamespace: ns_str,
eititle: mw.config.get( 'wgPageName' )
} ).done( function ( data ) {
callback( data.query.embeddedin );
} );
}
/*
* Calls the given callback with true if the image is shared
*/
function apiCheckRepository( filename, callback ) {
var api = new mw.Api();
api.get( {
action: 'query',
format: 'json',
formatversion: 2,
maxage: JumpToFile.maxage,
prop: 'imageinfo',
titles: 'File:' + filename,
iiprop: ''
} ).done( function ( data ) {
callback( data.query.pages[ 0 ].imagerepository );
} );
}
/**
* Reject bogus mainspace transclusions (e.g. the page is used in
* a progress bar)
*
* @param {string} title
* @return {boolean}
*/
function isValidTransclusion( title ) {
var base = title.split( '/' )[ 0 ];
return base !== 'Main Page';
}
/**
* Set up link to pages which transclude this one
*/
function setUpTransclusionLinks() {
if ( mw.config.get( 'wgCanonicalNamespace' ) !== 'Page' ) {
return;
}
api_get_tranclusions( [ 0, 114 ], function ( embeddedin ) {
var links = [];
for ( var i = 0; i < embeddedin.length; ++i ) {
if ( !isValidTransclusion( embeddedin[ i ].title ) ) {
continue;
}
var href = mw.util.getUrl( embeddedin[ i ].title ) +
'#' + JumpToFile.pageAnchorPrefix + getPageIndex(),
link = {
id: 'ca-ns0_' + i,
href: href,
icon: JumpToFile.transcludeIcon,
title: JumpToFile.strings.link_title_pat.replace( '$1', embeddedin[ i ].title ),
class: 'transclusion-link'
};
links.push( link );
}
addLinks( links );
} );
}
function setUpFileLinks() {
var filename,
ns = mw.config.get( 'wgCanonicalNamespace' );
if ( ns === 'Index' ) {
filename = mw.config.get( 'wgTitle' );
} else if ( ns === 'Page' ) {
filename = mw.config.get( 'wgTitle' ).replace( /\/\d+$/, '' );
}
if ( !filename ) {
// look in the "source" tab
// eslint-disable-next-line no-jquery/no-global-selector
var $link = $( '#ca-proofread-source a' );
if ( $link.length ) {
filename = $link.first().attr( 'href' );
filename = filename.substring( filename.indexOf( ':' ) + 1 );
}
}
set_up_link_from_file( filename );
}
function set_up_link_from_file( filename ) {
apiCheckRepository( filename, function ( repo ) {
var link_href = '';
var shared = [ 'shared', 'wikimediacommons' ].indexOf( repo ) !== -1;
if ( shared ) {
link_href = '//commons.wikimedia.org/wiki/File:' + filename;
} else {
link_href = mw.config.get( 'wgArticlePath' ).replace( '$1', 'File:' + filename );
}
addLinks( [ {
icon: JumpToFile.fileIcon,
href: link_href,
title: shared ?
JumpToFile.strings.go_to_file_commons : JumpToFile.strings.go_to_file_ns,
class: 'srcimglink'
} ] );
} );
var url = new URL( JumpToFile.infoHostname + '/img_links/v1/links' );
url.searchParams.append( 'file', filename );
url.searchParams.append( 'page', JumpToFile.pageIndex + JumpToFile.state.offset );
fetch( url )
.then( function ( data ) {
return data.json();
} )
.then( function ( jsonData ) {
handle_file_links( jsonData );
} );
}
function handle_file_links( linkData ) {
if ( !linkData.links ) {
return;
}
let highRes;
// Add links to tab
const displayLinks = linkData.links
.filter( ( link ) => {
// IIIF minfest not very useful to users in this menu
if ( [ 'iiif', 'iiif-manifest' ].indexOf( link.type ) !== -1 ) {
return JumpToFile.showIiifLinks;
}
// show everything else
return true;
} )
.map( ( link ) => {
return {
icon: link.icon,
href: link.url,
title: link.title,
class: 'srcimglink'
};
} );
addLinks( displayLinks );
for ( const link of linkData.links ) {
if ( link.highres === true && !highRes ) {
highRes = link;
} else if ( [ 'dzi', 'iiif' ].indexOf( link.type ) !== -1 ) {
highRes = link;
}
}
if ( highRes ) {
setHighresImg( highRes );
}
}
let originalItemCount;
function setHighresImg( highres ) {
// no viewer, nothing to do
if ( !mw.proofreadpage || !mw.proofreadpage.openseadragon || !mw.proofreadpage.openseadragon.viewer ) {
return;
}
if ( JumpToFile.state.loadHighres &&
highres && ( mw.config.get( 'wgCanonicalNamespace' ) === 'Page' ) ) {
if ( originalItemCount === undefined ) {
originalItemCount = mw.proofreadpage.openseadragon.viewer.world.getItemCount();
}
if ( highres.type === 'iiif' || highres.type === 'dzi' ) {
mw.proofreadpage.openseadragon.viewer
.addTiledImage( {
// load with the old image in the background to avoid a large flicker
preload: true,
tileSource: highres.data || highres.url
} );
} else if ( highres.type === 'image' ) {
mw.proofreadpage.openseadragon.viewer
.addSimpleImage( {
replace: false,
url: highres.url
} );
}
mw.hook( JumpToFile.signature + '.highres_set' ).fire( highres );
} else {
// remove any high res images
const currCount = mw.proofreadpage.openseadragon.viewer.world.getItemCount();
for ( let i = currCount - 1; i >= originalItemCount; --i ) {
const item = mw.proofreadpage.openseadragon.viewer.world.getItemAt( i );
mw.proofreadpage.openseadragon.viewer.world.removeItem( item );
}
}
}
var initWindowManager = function () {
if ( JumpToFile.windowManager ) {
return;
}
JumpToFile.windowManager = new OO.ui.WindowManager();
// Create and append a window manager, which will open and close the window.
$( document.body ).append( JumpToFile.windowManager.$element );
};
var getIndexKey = function () {
return mw.config.get( 'wgTitle' ).replace( /\/\d+$/, '' );
};
var initialiseLsData = function ( lsData ) {
if ( !lsData.offsets ) {
lsData.offsets = {};
}
};
var loadSettings = function () {
var opts = getUserOption( JumpToFile.signature );
var lsData = getLocalStorage( JumpToFile.signature );
initialiseLsData( lsData );
loadSettingsFromData( opts, lsData );
};
var loadSettingsFromData = function ( opts, lsData ) {
JumpToFile.state.loadHighres = opts.loadHighres || false;
JumpToFile.state.offset = lsData.offsets[ getIndexKey() ] || 0;
};
var storeOptions = function ( params ) {
// these options save persistently across all sessions
var opts = {
loadHighres: params[ 0 ]
};
persistUserOption( JumpToFile.signature, opts );
// the offset opts go into local storage because they're big and can go stale
// TODO persist somewhere on the index page for all users?
var filename = getIndexKey();
var lsData = getLocalStorage( JumpToFile.signature );
initialiseLsData( lsData );
lsData.offsets[ filename ] = params[ 1 ];
persistLocalStorage( JumpToFile.signature, lsData );
loadSettingsFromData( opts, lsData );
reloadFileLinks();
return true;
};
var reloadFileLinks = function () {
if ( JumpToFile.state.$linkUl ) {
JumpToFile.state.$linkUl.find( '.srcimglink' ).remove();
}
setUpFileLinks();
};
var showOptions = function () {
initWindowManager();
var needs = [
{
type: 'bool',
label: 'Load hi-res images',
help: 'Load high-resolution images from the upstream source if possible',
value: JumpToFile.state.loadHighres
},
{
type: 'int',
label: 'Source offset',
help: 'The offset between the page numbering in the WIkisource file and the file at the source. ' +
'Example: "0" if the files are identical, "1" if the source has a cover sheet and the Wikisource ' +
'file has had that page removed.',
value: JumpToFile.state.offset
}
];
// Make the window.
var dialog = new ILUI.GeneralParamsDialog( {
size: 'medium'
} );
JumpToFile.windowManager.addWindows( [ dialog ] );
JumpToFile.windowManager.openWindow( dialog, {
title: 'JumpToFile options',
needs: needs,
saveCallback: function ( params ) {
return $.Deferred().resolve( storeOptions( params ) );
}
} );
};
var addConfigMenu = function () {
var portlet = mw.util.addPortletLink( 'p-tb', '#',
JumpToFile.strings.hiResOptions, JumpToFile.signature + '-enable_hires_dl',
JumpToFile.strings.hiResOptionsTip );
$( portlet ).on( 'click', function ( e ) {
e.preventDefault();
showOptions();
} );
};
var addLinksMenu = function () {
const portlet = mw.util.addPortlet(
JumpToFile.strings.portlet_id,
JumpToFile.strings.portlet_title,
'#p-cactions'
);
const skin = mw.config.get("skin");
if (skin === 'vector') {
// We can use the returned node directly
$(portlet).appendTo("#left-navigation");
} else if (skin === 'vector-2022') {
// Need to grab the *-dropdown wrapper Vector 2022 creates
$('#' + JumpToFile.strings.portlet_id + "-dropdown")
.appendTo("#left-navigation");
} else {
// Do nothing since the skin doesn't support dropdowns anyway.
}
};
// The loader has handled "wait for DOM ready"
mw.hook( JumpToFile.signature + '.config' ).fire( JumpToFile );
loadSettings();
addConfigMenu();
addLinksMenu();
setUpTransclusionLinks();
reloadFileLinks();
}() );