MediaWiki:Gadget-QuoteSourceRedirects.js: Difference between revisions
From Bodhicitta
MediaWiki:Gadget-QuoteSourceRedirects.js
(VSCode edit) |
(Four actions: rewrite, redirect, create, delete) |
||
| Line 2: | Line 2: | ||
* QuoteSourceRedirects | * QuoteSourceRedirects | ||
* | * | ||
* | * Tools for [[QuoteSource Unresolved Values]]. | ||
* | * | ||
* | * QuoteSource is a page property naming the work a quote segment cites. Where a | ||
* value does not resolve to a text page, one of four things fixes it: | |||
* | |||
* | * | ||
* | * rewrite change the value on every segment that uses it | ||
* | * redirect point the value's page name at a text page | ||
* create create the text page here, for works DRL already holds | |||
* delete remove the value from every segment that uses it | |||
* | |||
* Controls are built here rather than written into the page: raw <button> and | |||
* <input> do not survive the wikitext sanitizer. | |||
*/ | */ | ||
( function () { | ( function () { | ||
| Line 18: | Line 20: | ||
var api; | var api; | ||
/* Mirrors attr() | function getApi() { | ||
* | if ( !api ) { api = new mw.Api(); } | ||
return api; | |||
} | |||
/* Mirrors the page generator's attr(): the wikitext stores each value | |||
* HTML-escaped in data-value, so a lookup has to escape it the same way. */ | |||
function attrEscape( s ) { | function attrEscape( s ) { | ||
return String( s ) | return String( s ) | ||
| Line 28: | Line 35: | ||
.replace( /\r\n?|\n/g, ' ' ) | .replace( /\r\n?|\n/g, ' ' ) | ||
.replace( /\|/g, '|' ); | .replace( /\|/g, '|' ); | ||
} | } | ||
$( function () { | $( function () { | ||
var $tables = $( '.qs-table' ); | var $tables = $( '.qs-table' ); | ||
if ( !$tables.length ) { | if ( !$tables.length ) { return; } | ||
function | /* 27 values contain a newline, which would end the table cell. They are | ||
$ | * rendered with a pilcrow and carry the real value in data-value, so | ||
* every lookup reads the attribute rather than the cell text. */ | |||
function valueOf( $tr ) { | |||
return $tr.find( '.qs-actions' ).attr( 'data-value' ); | |||
} | } | ||
function setStatus( $tr, cls, text ) { | |||
$tr.find( '.qs-status' ).removeClass( 'qs-ok qs-err qs-busy' ) | |||
.addClass( cls ).text( text ); | |||
function | |||
} | } | ||
function isTreated( $tr ) { | |||
return $tr.hasClass( 'qs-treated' ); | |||
function isTreated( $ | |||
return $ | |||
} | } | ||
function setTreated( $ | function setTreated( $tr ) { | ||
$ | $tr.addClass( 'qs-treated' ); | ||
$ | $tr.find( 'button' ).prop( 'disabled', true ); | ||
$ | $tr.find( '.qs-dismiss' ).text( 'undo' ).prop( 'disabled', false ); | ||
} | } | ||
function clearTreated( $ | function clearTreated( $tr ) { | ||
$ | $tr.removeClass( 'qs-treated' ); | ||
$ | $tr.find( 'button' ).prop( 'disabled', false ); | ||
$ | $tr.find( '.qs-dismiss' ).text( 'done' ); | ||
} | } | ||
function countTreated() { | function countTreated() { | ||
var n = $( 'tr.qs-treated' ).length; | var n = $( 'tr.qs-treated' ).length; | ||
var total = $( '.qs-table tr' ).has( 'td' ).length; | |||
$( '.qs-treated-count' ).text( n ? n + ' of ' + total + ' handled' : '' ); | |||
} | } | ||
/* | /* --------------------------------------------------------------- | ||
* | * Marking a row handled, persisted into this page's wikitext. | ||
* The row's `|-` becomes `|- class="qs-treated"`, found by the | |||
* The row | * data-value attribute, which is unique per row. | ||
* --------------------------------------------------------------- */ | |||
* | |||
* | |||
function markInWikitext( value, add ) { | function markInWikitext( value, add ) { | ||
var page = mw.config.get( 'wgPageName' ); | var page = mw.config.get( 'wgPageName' ); | ||
| Line 126: | Line 86: | ||
rvslots: 'main', titles: page, formatversion: 2 | rvslots: 'main', titles: page, formatversion: 2 | ||
} ).then( function ( res ) { | } ).then( function ( res ) { | ||
var | var text = res.query.pages[ 0 ].revisions[ 0 ].slots.main.content; | ||
var at = text.indexOf( 'data-value="' + attrEscape( value ) + '"' ); | var at = text.indexOf( 'data-value="' + attrEscape( value ) + '"' ); | ||
if ( at === -1 ) { | if ( at === -1 ) { | ||
| Line 140: | Line 97: | ||
? ( line.indexOf( 'qs-treated' ) === -1 ? '|- class="qs-treated"' : line ) | ? ( line.indexOf( 'qs-treated' ) === -1 ? '|- class="qs-treated"' : line ) | ||
: '|-'; | : '|-'; | ||
return getApi().postWithToken( 'csrf', { | return getApi().postWithToken( 'csrf', { | ||
action: 'edit', title: page, text: | action: 'edit', title: page, | ||
summary: ( add ? 'mark' : 'unmark' ) + ' QuoteSource row | text: text.slice( 0, rowStart ) + updated + text.slice( lineEnd ), | ||
summary: ( add ? 'mark' : 'unmark' ) + ' QuoteSource row handled: ' + value | |||
} ); | } ); | ||
} ); | } ); | ||
} | } | ||
/* --------------------------------------------------------------- | |||
* Finding the segments that use a value | |||
* --------------------------------------------------------------- */ | |||
function segmentsUsing( value ) { | |||
return getApi().get( { | |||
action: 'ask', | |||
query: '[[QuoteSource::' + value + ']]|limit=500', | |||
formatversion: 2 | |||
} ).then( function ( res ) { | |||
return Object.keys( ( res.query && res.query.results ) || {} ); | |||
} ); | } ); | ||
} | } | ||
/ | /* QuoteSource can hold several sources separated by ';', so a value is | ||
* changed by exact match on one part, never by replacing the field. */ | |||
function editField( text, from, to ) { | |||
return text.replace( /(\|\s*QuoteSource\s*=)([^\n]*)/g, function ( all, head, val ) { | |||
* | var parts = val.split( ';' ); | ||
var kept = []; | |||
for ( var i = 0; i < parts.length; i++ ) { | |||
function | if ( $.trim( parts[ i ] ) === $.trim( from ) ) { | ||
var parts = | if ( to !== null ) { kept.push( to ); } | ||
} else { | |||
kept.push( parts[ i ] ); | |||
} | |||
kept.push( | |||
} | } | ||
return head + kept.join( ';' ); | |||
} ); | |||
} | } | ||
function | function editSegments( pages, i, from, to, $tr, done ) { | ||
if ( i >= pages.length ) { | if ( i >= pages.length ) { | ||
$ | setStatus( $tr, 'qs-ok', ( to === null ? 'removed from ' : 'rewrote ' ) + | ||
pages.length + ' segments' ); | |||
done(); | done(); | ||
return; | return; | ||
} | } | ||
$ | setStatus( $tr, 'qs-busy', 'editing ' + ( i + 1 ) + ' of ' + pages.length + '…' ); | ||
getApi().get( { | getApi().get( { | ||
action: 'query', prop: 'revisions', rvprop: 'content', | action: 'query', prop: 'revisions', rvprop: 'content', | ||
| Line 199: | Line 149: | ||
var pg = res.query.pages[ 0 ]; | var pg = res.query.pages[ 0 ]; | ||
if ( !pg || !pg.revisions ) { | if ( !pg || !pg.revisions ) { | ||
editSegments( pages, i + 1, from, to, $tr, done ); return; | |||
} | } | ||
var text = pg.revisions[ 0 ].slots.main.content; | var text = pg.revisions[ 0 ].slots.main.content; | ||
var updated = text | var updated = editField( text, from, to ); | ||
if ( updated === text ) { | if ( updated === text ) { | ||
editSegments( pages, i + 1, from, to, $tr, done ); return; | |||
} | } | ||
getApi().postWithToken( 'csrf', { | getApi().postWithToken( 'csrf', { | ||
action: 'edit', title: pages[ i ], text: updated, | action: 'edit', title: pages[ i ], text: updated, | ||
summary: 'QuoteSource: remove ' + | summary: to === null | ||
? ( 'QuoteSource: remove ' + from ) | |||
: ( 'QuoteSource: ' + from + ' → ' + to ) | |||
} ).done( function () { | } ).done( function () { | ||
editSegments( pages, i + 1, from, to, $tr, done ); | |||
} ).fail( function ( code ) { | } ).fail( function ( code ) { | ||
$ | setStatus( $tr, 'qs-err', code ); | ||
} ); | } ); | ||
} ).fail( function ( code ) { | } ).fail( function ( code ) { | ||
$ | setStatus( $tr, 'qs-err', code ); | ||
} ); | } ); | ||
} | } | ||
$ | function runOnSegments( $tr, from, to ) { | ||
setStatus( $tr, 'qs-busy', 'finding segments…' ); | |||
segmentsUsing( from ).done( function ( pages ) { | |||
if ( !pages.length ) { | if ( !pages.length ) { | ||
$ | setStatus( $tr, 'qs-err', 'no segments found' ); return; | ||
} | } | ||
editSegments( pages, 0, from, to, $tr, function () { | |||
setTreated( $tr ); | setTreated( $tr ); | ||
countTreated(); | countTreated(); | ||
markInWikitext( | markInWikitext( from, true ); | ||
} ); | } ); | ||
} ).fail( function ( code ) { | } ).fail( function ( code ) { | ||
$ | setStatus( $tr, 'qs-err', code ); | ||
} ); | } ); | ||
} ); | } | ||
/* --------------------------------------------------------------- | |||
* The four actions | |||
* --------------------------------------------------------------- */ | |||
function doRewrite( $tr ) { | |||
var from = valueOf( $tr ); | |||
var suggest = $tr.find( '.qs-actions' ).attr( 'data-suggest' ) || from; | |||
var to = window.prompt( | |||
'Rewrite this QuoteSource value on every segment that uses it:', suggest ); | |||
if ( to === null || !$.trim( to ) || $.trim( to ) === from ) { return; } | |||
runOnSegments( $tr, from, $.trim( to ) ); | |||
} | |||
function doDelete( $tr ) { | |||
var value = valueOf( $tr ); | |||
if ( !window.confirm( | |||
'Remove "' + value + '" from every segment that uses it?\n\n' + | |||
var | 'Other sources on the same segment are kept.' ) ) { return; } | ||
if ( | runOnSegments( $tr, value, null ); | ||
} | |||
} | |||
function doRedirect( $tr ) { | |||
function | |||
var from = valueOf( $tr ); | var from = valueOf( $tr ); | ||
var | var suggest = $tr.find( '.qs-actions' ).attr( 'data-suggest' ) || ''; | ||
var to = window.prompt( | var to = window.prompt( 'Redirect "' + from + '" to which page?', suggest ); | ||
if ( to === null || !$.trim( to ) ) { return; } | |||
if ( to === null | |||
to = $.trim( to ); | to = $.trim( to ); | ||
setStatus( $tr, 'qs-busy', 'creating redirect…' ); | |||
$ | getApi().postWithToken( 'csrf', { | ||
getApi(). | action: 'edit', title: from, | ||
action: ' | text: '#REDIRECT [[' + to + ']]', | ||
summary: 'QuoteSource redirect to [[' + to + ']]', | |||
createonly: 1 | |||
} ).done( function ( | } ).done( function () { | ||
setStatus( $tr, 'qs-ok', 'redirect created' ); | |||
setTreated( $tr ); countTreated(); markInWikitext( from, true ); | |||
if ( | } ).fail( function ( code, res ) { | ||
$ | if ( code === 'articleexists' ) { | ||
setStatus( $tr, 'qs-ok', 'already exists' ); | |||
} else { | |||
setStatus( $tr, 'qs-err', | |||
( res && res.error && res.error.info ) || code ); | |||
} | } | ||
} ); | } ); | ||
} | } | ||
function | /* The page is created with {{LibraryItem}} and the DRL page name, which | ||
* is what pulls the work's data across. Nothing else is guessed here. */ | |||
function doCreate( $tr ) { | |||
var value = valueOf( $tr ); | |||
var drl = $tr.find( '.qs-actions' ).attr( 'data-drl' ); | |||
if ( !drl ) { setStatus( $tr, 'qs-err', 'no DRL page recorded' ); return; } | |||
$ | var title = window.prompt( | ||
getApi(). | 'Create the text page here, with {{LibraryItem}}.\n\n' + | ||
action: ' | 'DRL holds this work as: ' + drl + '\n\nPage name:', | ||
'Texts/' + drl ); | |||
} ).done( function ( | if ( title === null || !$.trim( title ) ) { return; } | ||
title = $.trim( title ); | |||
if ( | setStatus( $tr, 'qs-busy', 'creating page…' ); | ||
getApi().postWithToken( 'csrf', { | |||
action: 'edit', title: title, | |||
text: '{{LibraryItem}}', | |||
summary: 'Create text page for QuoteSource value "' + value + '"', | |||
createonly: 1 | |||
} ).done( function () { | |||
// The value still has to reach the new page: if the value is not | |||
// the page name itself, it needs a redirect as well. | |||
if ( value === title ) { | |||
setStatus( $tr, 'qs-ok', 'page created' ); | |||
setTreated( $tr ); countTreated(); markInWikitext( value, true ); | |||
return; | return; | ||
} | } | ||
getApi().postWithToken( 'csrf', { | getApi().postWithToken( 'csrf', { | ||
action: 'edit', title: | action: 'edit', title: value, | ||
summary: 'QuoteSource | text: '#REDIRECT [[' + title + ']]', | ||
} ). | summary: 'QuoteSource redirect to [[' + title + ']]', | ||
createonly: 1 | |||
} ).always( function () { | |||
setStatus( $tr, 'qs-ok', 'page created, value redirected' ); | |||
setTreated( $tr ); countTreated(); markInWikitext( value, true ); | |||
} ); | } ); | ||
} ).fail( function ( code ) { | } ).fail( function ( code, res ) { | ||
$ | if ( code === 'articleexists' ) { | ||
setStatus( $tr, 'qs-err', 'that page already exists' ); | |||
} else { | |||
setStatus( $tr, 'qs-err', | |||
( res && res.error && res.error.info ) || code ); | |||
} | |||
} ); | } ); | ||
} | } | ||
var ACTIONS = [ | |||
{ key: 'rewrite', label: 'rewrite', run: doRewrite }, | |||
{ key: 'redirect', label: 'redirect', run: doRedirect }, | |||
{ key: 'create', label: 'create', run: doCreate }, | |||
{ key: 'delete', label: 'delete', run: doDelete } | |||
]; | |||
/* | /* --------------------------------------------------------------- | ||
* Build the controls | * Build the controls | ||
* | * --------------------------------------------------------------- */ | ||
$( '.qs- | $( '.qs-actions' ).each( function () { | ||
var $cell = $( this ); | var $cell = $( this ); | ||
var $tr = $cell.closest( 'tr' ); | |||
var recommended = $cell.attr( 'data-recommended' ); | |||
var hasDrl = !!$cell.attr( 'data-drl' ); | |||
ACTIONS.forEach( function ( a ) { | |||
// "create" needs a confirmed DRL page; without one there is | |||
// nothing to base the new page on. | |||
if ( a.key === 'create' && !hasDrl ) { return; } | |||
var $b = $( '<button>' ) | |||
.addClass( 'qs-btn qs-btn--' + a.key ) | |||
.text( a.label ) | |||
.on( 'click', function () { | |||
if ( isTreated( $tr ) ) { return; } | |||
a.run( $tr ); | |||
} ); | |||
if ( a.key === recommended ) { $b.addClass( 'qs-btn--suggested' ); } | |||
$cell.append( $b ); | |||
} ); | |||
$cell.append( | $cell.append( | ||
$( '<button>' ).addClass( 'qs- | $( '<button>' ).addClass( 'qs-dismiss' ).text( 'done' ) | ||
.attr( 'title', 'Mark this row handled' ) | |||
.on( 'click', function () { | .on( 'click', function () { | ||
var value = valueOf( $tr ); | |||
var add = !isTreated( $tr ); | |||
} ) | if ( add ) { setTreated( $tr ); } else { clearTreated( $tr ); } | ||
countTreated(); | |||
markInWikitext( value, add ).fail( function ( why ) { | |||
if ( add ) { clearTreated( $tr ); } else { setTreated( $tr ); } | |||
countTreated(); | |||
setStatus( $tr, 'qs-err', 'could not save: ' + why ); | |||
} ); | |||
} ), | |||
$( '<span>' ).addClass( 'qs-status' ) | |||
); | ); | ||
} ); | } ); | ||
| Line 362: | Line 328: | ||
$( '.qs-controls' ).each( function () { | $( '.qs-controls' ).each( function () { | ||
var $box = $( this ); | var $box = $( this ); | ||
var $table = $box.nextAll( '.qs-table' ).first(); | var $table = $box.nextAll( '.qs-table' ).first(); | ||
$cb.on( 'change', function () { | if ( !$table.length ) { $table = $( '.qs-table' ).first(); } | ||
var $cb = $( '<input>' ).attr( 'type', 'checkbox' ) | |||
.on( 'change', function () { | |||
$table.toggleClass( 'qs-hiding', this.checked ); | |||
} ); | |||
$box.append( | $box.append( | ||
$( '<label>' ).addClass( 'qs-hide-label' ).append( $cb, ' Hide | $( '<label>' ).addClass( 'qs-hide-label' ).append( $cb, ' Hide handled rows' ), | ||
$( '<span>' ).addClass( 'qs-treated-count' ) | $( '<span>' ).addClass( 'qs-treated-count' ) | ||
); | ); | ||
| Line 375: | Line 342: | ||
$( 'tr.qs-treated' ).each( function () { setTreated( $( this ) ); } ); | $( 'tr.qs-treated' ).each( function () { setTreated( $( this ) ); } ); | ||
countTreated(); | countTreated(); | ||
} ); | } ); | ||
}() ); | }() ); | ||
Latest revision as of 16:47, 17 September 2026
/**
* QuoteSourceRedirects
*
* Tools for [[QuoteSource Unresolved Values]].
*
* QuoteSource is a page property naming the work a quote segment cites. Where a
* value does not resolve to a text page, one of four things fixes it:
*
* rewrite change the value on every segment that uses it
* redirect point the value's page name at a text page
* create create the text page here, for works DRL already holds
* delete remove the value from every segment that uses it
*
* Controls are built here rather than written into the page: raw <button> and
* <input> do not survive the wikitext sanitizer.
*/
( function () {
'use strict';
var api;
function getApi() {
if ( !api ) { api = new mw.Api(); }
return api;
}
/* Mirrors the page generator's attr(): the wikitext stores each value
* HTML-escaped in data-value, so a lookup has to escape it the same way. */
function attrEscape( s ) {
return String( s )
.replace( /&/g, '&' )
.replace( /"/g, '"' )
.replace( /</g, '<' )
.replace( />/g, '>' )
.replace( /\r\n?|\n/g, ' ' )
.replace( /\|/g, '|' );
}
$( function () {
var $tables = $( '.qs-table' );
if ( !$tables.length ) { return; }
/* 27 values contain a newline, which would end the table cell. They are
* rendered with a pilcrow and carry the real value in data-value, so
* every lookup reads the attribute rather than the cell text. */
function valueOf( $tr ) {
return $tr.find( '.qs-actions' ).attr( 'data-value' );
}
function setStatus( $tr, cls, text ) {
$tr.find( '.qs-status' ).removeClass( 'qs-ok qs-err qs-busy' )
.addClass( cls ).text( text );
}
function isTreated( $tr ) {
return $tr.hasClass( 'qs-treated' );
}
function setTreated( $tr ) {
$tr.addClass( 'qs-treated' );
$tr.find( 'button' ).prop( 'disabled', true );
$tr.find( '.qs-dismiss' ).text( 'undo' ).prop( 'disabled', false );
}
function clearTreated( $tr ) {
$tr.removeClass( 'qs-treated' );
$tr.find( 'button' ).prop( 'disabled', false );
$tr.find( '.qs-dismiss' ).text( 'done' );
}
function countTreated() {
var n = $( 'tr.qs-treated' ).length;
var total = $( '.qs-table tr' ).has( 'td' ).length;
$( '.qs-treated-count' ).text( n ? n + ' of ' + total + ' handled' : '' );
}
/* ---------------------------------------------------------------
* Marking a row handled, persisted into this page's wikitext.
* The row's `|-` becomes `|- class="qs-treated"`, found by the
* data-value attribute, which is unique per row.
* --------------------------------------------------------------- */
function markInWikitext( value, add ) {
var page = mw.config.get( 'wgPageName' );
return getApi().get( {
action: 'query', prop: 'revisions', rvprop: 'content',
rvslots: 'main', titles: page, formatversion: 2
} ).then( function ( res ) {
var text = res.query.pages[ 0 ].revisions[ 0 ].slots.main.content;
var at = text.indexOf( 'data-value="' + attrEscape( value ) + '"' );
if ( at === -1 ) {
return $.Deferred().reject( 'row not found in wikitext' );
}
var rowStart = text.lastIndexOf( '|-', at );
var lineEnd = text.indexOf( '\n', rowStart );
var line = text.slice( rowStart, lineEnd );
var updated = add
? ( line.indexOf( 'qs-treated' ) === -1 ? '|- class="qs-treated"' : line )
: '|-';
return getApi().postWithToken( 'csrf', {
action: 'edit', title: page,
text: text.slice( 0, rowStart ) + updated + text.slice( lineEnd ),
summary: ( add ? 'mark' : 'unmark' ) + ' QuoteSource row handled: ' + value
} );
} );
}
/* ---------------------------------------------------------------
* Finding the segments that use a value
* --------------------------------------------------------------- */
function segmentsUsing( value ) {
return getApi().get( {
action: 'ask',
query: '[[QuoteSource::' + value + ']]|limit=500',
formatversion: 2
} ).then( function ( res ) {
return Object.keys( ( res.query && res.query.results ) || {} );
} );
}
/* QuoteSource can hold several sources separated by ';', so a value is
* changed by exact match on one part, never by replacing the field. */
function editField( text, from, to ) {
return text.replace( /(\|\s*QuoteSource\s*=)([^\n]*)/g, function ( all, head, val ) {
var parts = val.split( ';' );
var kept = [];
for ( var i = 0; i < parts.length; i++ ) {
if ( $.trim( parts[ i ] ) === $.trim( from ) ) {
if ( to !== null ) { kept.push( to ); }
} else {
kept.push( parts[ i ] );
}
}
return head + kept.join( ';' );
} );
}
function editSegments( pages, i, from, to, $tr, done ) {
if ( i >= pages.length ) {
setStatus( $tr, 'qs-ok', ( to === null ? 'removed from ' : 'rewrote ' ) +
pages.length + ' segments' );
done();
return;
}
setStatus( $tr, 'qs-busy', 'editing ' + ( i + 1 ) + ' of ' + pages.length + '…' );
getApi().get( {
action: 'query', prop: 'revisions', rvprop: 'content',
rvslots: 'main', titles: pages[ i ], formatversion: 2
} ).done( function ( res ) {
var pg = res.query.pages[ 0 ];
if ( !pg || !pg.revisions ) {
editSegments( pages, i + 1, from, to, $tr, done ); return;
}
var text = pg.revisions[ 0 ].slots.main.content;
var updated = editField( text, from, to );
if ( updated === text ) {
editSegments( pages, i + 1, from, to, $tr, done ); return;
}
getApi().postWithToken( 'csrf', {
action: 'edit', title: pages[ i ], text: updated,
summary: to === null
? ( 'QuoteSource: remove ' + from )
: ( 'QuoteSource: ' + from + ' → ' + to )
} ).done( function () {
editSegments( pages, i + 1, from, to, $tr, done );
} ).fail( function ( code ) {
setStatus( $tr, 'qs-err', code );
} );
} ).fail( function ( code ) {
setStatus( $tr, 'qs-err', code );
} );
}
function runOnSegments( $tr, from, to ) {
setStatus( $tr, 'qs-busy', 'finding segments…' );
segmentsUsing( from ).done( function ( pages ) {
if ( !pages.length ) {
setStatus( $tr, 'qs-err', 'no segments found' ); return;
}
editSegments( pages, 0, from, to, $tr, function () {
setTreated( $tr );
countTreated();
markInWikitext( from, true );
} );
} ).fail( function ( code ) {
setStatus( $tr, 'qs-err', code );
} );
}
/* ---------------------------------------------------------------
* The four actions
* --------------------------------------------------------------- */
function doRewrite( $tr ) {
var from = valueOf( $tr );
var suggest = $tr.find( '.qs-actions' ).attr( 'data-suggest' ) || from;
var to = window.prompt(
'Rewrite this QuoteSource value on every segment that uses it:', suggest );
if ( to === null || !$.trim( to ) || $.trim( to ) === from ) { return; }
runOnSegments( $tr, from, $.trim( to ) );
}
function doDelete( $tr ) {
var value = valueOf( $tr );
if ( !window.confirm(
'Remove "' + value + '" from every segment that uses it?\n\n' +
'Other sources on the same segment are kept.' ) ) { return; }
runOnSegments( $tr, value, null );
}
function doRedirect( $tr ) {
var from = valueOf( $tr );
var suggest = $tr.find( '.qs-actions' ).attr( 'data-suggest' ) || '';
var to = window.prompt( 'Redirect "' + from + '" to which page?', suggest );
if ( to === null || !$.trim( to ) ) { return; }
to = $.trim( to );
setStatus( $tr, 'qs-busy', 'creating redirect…' );
getApi().postWithToken( 'csrf', {
action: 'edit', title: from,
text: '#REDIRECT [[' + to + ']]',
summary: 'QuoteSource redirect to [[' + to + ']]',
createonly: 1
} ).done( function () {
setStatus( $tr, 'qs-ok', 'redirect created' );
setTreated( $tr ); countTreated(); markInWikitext( from, true );
} ).fail( function ( code, res ) {
if ( code === 'articleexists' ) {
setStatus( $tr, 'qs-ok', 'already exists' );
} else {
setStatus( $tr, 'qs-err',
( res && res.error && res.error.info ) || code );
}
} );
}
/* The page is created with {{LibraryItem}} and the DRL page name, which
* is what pulls the work's data across. Nothing else is guessed here. */
function doCreate( $tr ) {
var value = valueOf( $tr );
var drl = $tr.find( '.qs-actions' ).attr( 'data-drl' );
if ( !drl ) { setStatus( $tr, 'qs-err', 'no DRL page recorded' ); return; }
var title = window.prompt(
'Create the text page here, with {{LibraryItem}}.\n\n' +
'DRL holds this work as: ' + drl + '\n\nPage name:',
'Texts/' + drl );
if ( title === null || !$.trim( title ) ) { return; }
title = $.trim( title );
setStatus( $tr, 'qs-busy', 'creating page…' );
getApi().postWithToken( 'csrf', {
action: 'edit', title: title,
text: '{{LibraryItem}}',
summary: 'Create text page for QuoteSource value "' + value + '"',
createonly: 1
} ).done( function () {
// The value still has to reach the new page: if the value is not
// the page name itself, it needs a redirect as well.
if ( value === title ) {
setStatus( $tr, 'qs-ok', 'page created' );
setTreated( $tr ); countTreated(); markInWikitext( value, true );
return;
}
getApi().postWithToken( 'csrf', {
action: 'edit', title: value,
text: '#REDIRECT [[' + title + ']]',
summary: 'QuoteSource redirect to [[' + title + ']]',
createonly: 1
} ).always( function () {
setStatus( $tr, 'qs-ok', 'page created, value redirected' );
setTreated( $tr ); countTreated(); markInWikitext( value, true );
} );
} ).fail( function ( code, res ) {
if ( code === 'articleexists' ) {
setStatus( $tr, 'qs-err', 'that page already exists' );
} else {
setStatus( $tr, 'qs-err',
( res && res.error && res.error.info ) || code );
}
} );
}
var ACTIONS = [
{ key: 'rewrite', label: 'rewrite', run: doRewrite },
{ key: 'redirect', label: 'redirect', run: doRedirect },
{ key: 'create', label: 'create', run: doCreate },
{ key: 'delete', label: 'delete', run: doDelete }
];
/* ---------------------------------------------------------------
* Build the controls
* --------------------------------------------------------------- */
$( '.qs-actions' ).each( function () {
var $cell = $( this );
var $tr = $cell.closest( 'tr' );
var recommended = $cell.attr( 'data-recommended' );
var hasDrl = !!$cell.attr( 'data-drl' );
ACTIONS.forEach( function ( a ) {
// "create" needs a confirmed DRL page; without one there is
// nothing to base the new page on.
if ( a.key === 'create' && !hasDrl ) { return; }
var $b = $( '<button>' )
.addClass( 'qs-btn qs-btn--' + a.key )
.text( a.label )
.on( 'click', function () {
if ( isTreated( $tr ) ) { return; }
a.run( $tr );
} );
if ( a.key === recommended ) { $b.addClass( 'qs-btn--suggested' ); }
$cell.append( $b );
} );
$cell.append(
$( '<button>' ).addClass( 'qs-dismiss' ).text( 'done' )
.attr( 'title', 'Mark this row handled' )
.on( 'click', function () {
var value = valueOf( $tr );
var add = !isTreated( $tr );
if ( add ) { setTreated( $tr ); } else { clearTreated( $tr ); }
countTreated();
markInWikitext( value, add ).fail( function ( why ) {
if ( add ) { clearTreated( $tr ); } else { setTreated( $tr ); }
countTreated();
setStatus( $tr, 'qs-err', 'could not save: ' + why );
} );
} ),
$( '<span>' ).addClass( 'qs-status' )
);
} );
$( '.qs-controls' ).each( function () {
var $box = $( this );
var $table = $box.nextAll( '.qs-table' ).first();
if ( !$table.length ) { $table = $( '.qs-table' ).first(); }
var $cb = $( '<input>' ).attr( 'type', 'checkbox' )
.on( 'change', function () {
$table.toggleClass( 'qs-hiding', this.checked );
} );
$box.append(
$( '<label>' ).addClass( 'qs-hide-label' ).append( $cb, ' Hide handled rows' ),
$( '<span>' ).addClass( 'qs-treated-count' )
);
} );
$( 'tr.qs-treated' ).each( function () { setTreated( $( this ) ); } );
countTreated();
} );
}() );