User:Ricordisamoa/FlagSense.js
Jump to navigation
Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
This user script seems to have a documentation page at User:Ricordisamoa/FlagSense. |
- Report page listing warnings and errors.
/* <nowiki>
*
* FlagSense
* @author [[User:Ricordisamoa]]
*
* the current action to be made in the edit page is stored in the sessionStorage
*/
/* global mediaWiki, jQuery */
( function ( mw, $ ) {
'use strict';
var FlagSense = {
storage: sessionStorage,
messages: {
en: {
'add-template-VVA': 'Add {{Vector version available}} to the file description page',
'add-template-toSVG': 'Add {{Convert to SVG|flag}} to the file description page',
'add-template-toPNG': 'Add {{Convert to PNG}} to the file description page',
'add-template-ssPNG': 'Add {{SupersededPNG}} to the file description page',
'alreadySVG': 'This is already a vector image.\nIt doesn\'t need to be converted in SVG again.',
'vvaConfirmToSVG': 'This page already contains the template {{Vector version available}}\n' +
'To replace {{Vector version available}} with {{Convert to SVG}}, press OK;\n' +
'To skip editing this page, press UNDO',
'vvaConfirmToPNG': 'This page already contains the template {{Vector version available}}\n' +
'To replace {{Vector version available}} with {{Convert to PNG}}, press OK;\n' +
'To skip editing this page, press UNDO',
'vvaConfirmNew': 'This page already contains the template {{Vector version available}}\n' +
'To replace the existing istance with this new one, press OK;\n' +
'To skip editing this page, press UNDO',
'svgAlternative': 'The name of the SVG alternative (without File: prefix):',
'pngAlternative': 'The name of the PNG alternative (without File: prefix):',
'alreadySameCategory': 'FlagSense detected that, by a size of $1×$2, the proper category for this image was: «$3».\n' +
'However, the image is already in this category.',
'alreadyOtherCategory': 'FlagSense detected that, by a size of $1×$2, the proper category for this image would be: «$3».\n' +
'However, the image is already categorized by aspect ratio.\nDo you anyway want to replace the old category with this one?',
'noMatchFound': 'FlagSense tried to search the proper category for this image, but no matches were found.\n\n' +
'Details:\nimage size: $1×$2\naspect ratio: $3/$4'
},
it: {
'add-template-VVA': 'Aggiungi {{Vector version available}} alla pagina di descrizione del file',
'add-template-toSVG': 'Aggiungi {{Convert to SVG|flag}} alla pagina di descrizione del file',
'add-template-toPNG': 'Aggiungi {{Convert to PNG}} alla pagina di descrizione del file',
'add-template-ssPNG': 'Aggiungi {{SupersededPNG}} alla pagina di descrizione del file',
'alreadySVG': 'Questa è già una immagine vettoriale.\nNon necessita di essere convertita in SVG di nuovo.',
'vvaConfirmToSVG': 'Questa pagina contiene già il template {{Vector version available}}\n' +
'Per sostituire {{Vector version available}} con {{Convert to SVG}}, premi OK;\n' +
'Per evitare di modificare questa pagina, premi ANNULLA',
'vvaConfirmToPNG': 'Questa pagina contiene già il template {{Vector version available}}\n' +
'Per sostituire {{Vector version available}} con {{Convert to PNG}}, premi OK;\n' +
'Per evitare di modificare questa pagina, premi ANNULLA',
'vvaConfirmNew': 'Questa pagina contiene già il template {{Vector version available}}\n' +
'Per sostituire la vecchia istanza con quella nuova, premi OK;\n'+
'Per evitare di modificare questa pagina, premi ANNULLA',
'svgAlternative': 'Il nome dell\'alternativa SVG (senza prefisso File:)',
'pngAlternative': 'Il nome dell\'alternativa PNG (senza prefisso File:)',
'alreadySameCategory': 'FlagSense ha rilevato che, con una dimensione di $1×$2, la categoria appropriata per questa immagine sarebbe stata: «$3».\n' +
'Tuttavia, l\'immagine è già in questa categoria.',
'alreadyOtherCategory': 'FlagSense ha rilevato che, con una dimensione di $1×$2, la categoria appropriata per questa immagine sarebbe: «$3».\n' +
'Tuttavia, l\'immagine è già categorizzata per proporzioni.\nVuoi comunque sostituire la vecchia categoria con questa?',
'noMatchFound': 'FlagSense ha provato a cercate la categoria appropriata per questa immagine, ma non ha trovato corrispondenze.\n\n' +
'Dettagli:\ndimensioni immagine: $1×$2\nproporzioni: $3/$4'
}
},
easyInsert: function ( options ) {
if ( options.regex.test( this.$text.val() ) === false ) {
// add the code at the top
this.$text.val( options.code + '\n' + this.$text.val() );
} else if ( confirm( this.getMessage( options.confirm ) ) === true ) {
// replace the old code with the new one
this.$text.val( this.$text.val().replace( options.regex, options.code ) );
} else {
// go back to the file description page
window.history.back();
}
// precompiled edit summary
this.$summary.val( options.summary );
this.closeEdit();
},
start: function () {
var self = this;
// parse the file extension from the page name
self.fileExtension = mw.config.get( 'wgPageName' ).split( '.' ).slice( -1 )[0].toLowerCase();
if( $( '#editform' ).length > 0 && self.storage['FlagSense-currentAction'] ) {
// the user is editing the page and the current page was previously marked for editing
self.$text = $( '#wpTextbox1' ); // the edit textarea
self.$summary = $( '#wpSummary' ); // the summary of changes
self.$minor = $( '#wpMinoredit' ); // the checkbox for marking a minor edit
switch ( self.storage['FlagSense-currentAction'] ) {
// the user is going to mark this image as to be converted to SVG
case 'toSVG':
self.easyInsert( {
code: '{{Convert to SVG|flag}}',
regex: self.regex.VVA,
confirm: 'vvaConfirmToSVG',
summary: 'Added template {{[[Template:Convert to SVG|Convert to SVG]]|flag}} ' +
'using [[User:Ricordisamoa/FlagSense|FlagSense]]'
} );
break;
// the user is going to mark this image as to be converted to PNG
case 'toPNG':
self.easyInsert( {
code: '{{Convert to PNG}}',
regex: self.regex.VVA,
confirm: 'vvaConfirmToPNG',
summary: 'Added template {{[[Template:Convert to PNG|Convert to PNG]]}} ' +
'using [[User:Ricordisamoa/FlagSense|FlagSense]]'
} );
break;
// the user is going to point out a vector version of this image
case 'VVA':
self.easyInsert( {
code: '{{Vector version available|' +
prompt( self.getMessage( 'svgAlternative' ) ) + '}}',
regex: self.regex.VVA,
confirm: 'vvaConfirmNew',
summary: 'Added template {{[[Template:Vector version available|Vector version available]]}} ' +
'using [[User:Ricordisamoa/FlagSense|FlagSense]]'
} );
break;
// the user is going to point out a PNG version of this image
case 'ssPNG':
self.easyInsert( {
code: '{{SupersededPNG|' +
prompt( self.getMessage( 'pngAlternative' ) ) + '}}',
regex: self.regex.VVA,
confirm: 'vvaConfirmToPNG',
summary: 'Added template {{[[Template:SupersededPNG|SupersededPNG]]}} ' +
'using [[User:Ricordisamoa/FlagSense|FlagSense]]'
} );
break;
case 'cat':
var cat = self.storage['FlagSense-category'],
accuracy = parseFloat( self.storage['FlagSense-accuracy'] );
if ( self.regex.aspectRatioCat.test( self.$text.val() ) ) {
self.regex.aspectRatioCat.lastIndex = 0;
// precompiled summary
self.$summary.val(
'removed [[:Category:' +
self.regex.aspectRatioCat.exec( self.$text.val() )[0].toString()
.replace( /\[\[Category:/i, '') + '; '
);
// replace the old category with the new one
self.$text.val( self.$text.val().replace( self.regex.aspectRatioCat, '[[Category:' + cat + ']]' ) );
} else {
// add the category at the bottom
self.$text.val(
self.$text.val() + ( /\n$/.test( self.$text.val() ) ? '' : '\n' ) +
'[[Category:' + cat + ']]'
);
}
// precompiled summary
self.$summary.val(
self.$summary.val() + 'added [[:Category:' + cat + ']] using ' +
'[[User:Ricordisamoa/FlagSense|FlagSense]]' +
( accuracy > 0 ? ' with an error of ±' + accuracy : '' )
);
self.$minor.prop( 'checked', true );
self.closeEdit();
break;
}
} else {
var fileSize = $( '.filehistory > tbody > tr:nth-child(2) > td:nth-child(4) ' )[0]
.firstChild.nodeValue.split( ' ' ).join( '' ).split( '\u00A0' ).join( '' ).split( '×' ),
width = parseInt( fileSize[0] ),
height = parseInt( fileSize[1] ),
gcd = self.gcd( width, height ), // greatest common divisor of width and height
ratio = width / gcd + ':' + height / gcd,
cat = self.getCategoryByAspectRatio( width, height ),
$box = $( '<div>' )
.css( {
float: 'right',
background: 'whitesmoke',
padding: '8px'
} )
.append( '<h3>FlagSense</h3>' )
.append( 'Format: ' + self.fileExtension.toUpperCase() );
if ( self.fileExtension !== 'svg' ) {
$( '<p>' )
.attr( 'id', 'FlagSense-nonSVG' )
.append(
$( '<button>' )
.text( 'VVA' )
.attr( 'id', 'FlagSense-button-VVA' )
.attr( 'title', self.getMessage( 'add-template-VVA' ) )
.click( function () {
self.openEdit( 'VVA' );
} )
)
.appendTo( $box );
}
if ( self.fileExtension !== 'png' ) {
$( '<p>' )
.attr( 'id', 'FlagSense-nonPNG' )
.append(
$( '<button>' )
.text( 'SupersededPNG' )
.attr( 'id', 'FlagSense-button-ssPNG' )
.attr( 'title', self.getMessage( 'add-template-ssPNG' ) )
.click( function () {
self.openEdit( 'ssPNG' );
} )
)
.appendTo( $box );
}
var imgAttrs = {};
if ( cat !== null && mw.config.get( 'wgCategories' ).indexOf( cat.cat ) !== -1 ) {
imgAttrs.src = '//upload.wikimedia.org/wikipedia/commons/thumb/b/be/P_yes.svg/20px-P_yes.svg.png';
imgAttrs.title = 'already in this category';
} else if ( cat === null ) {
imgAttrs.src = '//upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no.svg/20px-P_no.svg.png';
imgAttrs.title = 'no proper category available for this image';
} else {
imgAttrs.src = '//upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Exclamation.svg/20px-Exclamation.svg.png';
imgAttrs.title = 'a category has been found' +
( self.isCategorizedByAspectRatio() ? ' but the image is already categorized by aspect ratio' : '' );
}
$( '<img>' )
.attr( imgAttrs )
.css( 'background', 'none' )// remove the default checkered background
.appendTo( $box );
$( '<p>' )
.text( 'Aspect ratio: ' )
.append(
cat !== null ? [
$( '<a>' ).text( cat.ratio ).attr( 'href', mw.util.getUrl( 'Category:' + cat.cat ) ),
( ' with' + ( cat.accuracy > 0 ? ' an error of ±' + Math.round( cat.accuracy * 100000 ) / 100000 : 'out error' ) )
] : ratio
)
.appendTo( $box );
if ( cat !== null && mw.config.get( 'wgCategories' ).indexOf( cat.cat ) === -1 ) {
$( '<button>' )
.text( self.isCategorizedByAspectRatio() ? 'Change category' : 'Add category' )
.click( function () {
self.openEdit( 'cat', cat.cat, Math.round( cat.accuracy * 100000 ) / 100000 );
} )
.appendTo( $box );
}
$( '<button>' )
.attr( 'disabled', true )
.append( 'Categorize by elements and colors<br/>(coming soon)' )
.click( function () {
var $img = $( '#file a > img' ),
canvas = $( '<canvas>' )
.attr( {
width: $img.attr( 'width' ),
height: $img.attr( 'height' )
} )
.css( 'background', $img.css( 'background' ) ),
ctx = canvas.get( 0 ).getContext( '2d' ),
imageObj = new Image();
imageObj.crossOrigin = 'Anonymous';// allow CORS images
imageObj.onload = function () {
ctx.drawImage( imageObj, 0, 0 );
canvas.click( function () {
alert( 'This feature is not available yet.\nPlease check soon.' );
} );
};
imageObj.src = $img.attr( 'src' );// get the URL of the in-page thumbnail
$img.parent().replaceWith( canvas );// replace the in-page thumb (and the link)
} )
.appendTo( $box );
$( '#file' ).prepend( $box );
if ( $( 'p#FlagSense-nonSVG' ).length > 0 ) {
self.isTemplated( self.regex.toSVG, function ( result ) {
if ( result === false ) {
$( '<button>' )
.text( 'Should be SVG' )
.attr( 'id', 'FlagSense-button-toSVG' )
.attr( 'title', self.getMessage( 'add-template-toSVG' ) )
.click( function () {
self.openEdit( 'toSVG' );
} )
.prependTo( 'p#FlagSense-nonSVG' );
}
} );
self.isTemplated( self.regex.VVA, function ( result ) {
if ( result === true ) {
$( 'button#FlagSense-button-VVA' )
.attr( 'title', $( 'button#FlagSense-button-VVA' ).attr( 'title' ) + ' (change)' )
.append( ' (change)' );
}
} );
}
if ( $( 'p#FlagSense-nonPNG' ).length > 0 ) {
self.isTemplated( self.regex.toPNG, function ( result ) {
if ( result === false ) {
$( '<button>' )
.text( 'Should be PNG' )
.attr( 'id', 'FlagSense-button-toPNG' )
.attr( 'title', self.getMessage( 'add-template-toPNG' ) )
.click( function () {
self.openEdit( 'toPNG' );
} )
.prependTo( 'p#FlagSense-nonPNG' );
}
} );
self.isTemplated( self.regex.ssPNG, function ( result ) {
if ( result === true ) {
$( 'button#FlagSense-button-ssPNG' )
.attr( 'title', $( 'button#FlagSense-button-ssPNG' ).attr( 'title' ) + ' (change)' )
.append( ' (change)' );
}
} );
}
}
},
getCategoryByAspectRatio: function ( width, height ) {
// see https://commons.wikimedia.org/wiki/Category:Flags_by_aspect_ratio
var ratios = [
'1:1', '2:1', '3:2', '4:3', '5:3', '18:11',
'18:13', '20:11', '25:14', '8:5'
],
paragorns = [];
$.each( ratios, function ( index, ratio ) {
var rs = ratio.split( ':' ),
// absolute difference between the two ratios
diff = Math.abs( parseInt( rs[0] ) / parseInt( rs[1] ) - width / height );
console.log( 'FlagSense: examining ratio: ' + rs.join( '/' ) + '; difference is: ' + diff );
if ( diff < 0.04 ) {
paragorns.push( [ratio, diff] );
}
} );
if ( paragorns.length === 0 ) {
return null;
}
console.log( 'FlagSense: unordered ratios: ' + paragorns );
// sort all ratios whose difference is lower than 0.04
var paragorn = paragorns.sort( this.sortRatios )[0];
// the first is the most appropriate
console.log( 'FlagSense: most appropriate ratio: ' + paragorn );
var ratio = paragorn[0],
accuracy = paragorn[1], // the difference between the flag's aspect ratio and the closest existing category
cat = 'Flags with an aspect ratio of ' + ratio; // proper category by aspect ratio
if ( ratio === '1:1' ) {
cat = 'Square flags';
}
if ( this.fileExtension === 'svg' ) {
if ( ratio === '1:1' ) {
cat = 'SVG square flags';
} else {
cat = cat.replace( /^Flags/, 'SVG flags' );
}
}
if ( ratio === '3:2' ) {
if ( this.fileExtension === 'png' ) {
cat = 'PNG flags - aspect ratio of 3:2';
} else if ( this.fileExtension === 'gif' ) {
cat = 'GIF flags with aspect ratio of 3:2';
}
}
return {
ratio: ratio,
cat: cat,
accuracy: accuracy
};
},
sortRatios: function ( a , b ) {
return a[1] - b[1]; // sort two aspect ratios basing on the accuracy level
},
isTemplated: function ( regex, callback ) {
$.get( mw.util.wikiScript(), { title: mw.config.get( 'wgPageName' ), action: 'raw' } )
.done( function ( data ) {
callback( regex.test( data ) );
} );
},
gcd: function ( a, b ) { // recursive function for Greatest Common Divisor
if ( b ) {
return this.gcd( b, a % b );
} else {
return Math.abs( a );
}
},
getMessage: function ( key ) {
var params = Array.prototype.slice.call( arguments, 1 );
return new mw.Message( this.messages, key, params ).text();
},
isCategorizedByAspectRatio: function () {
$.each( mw.config.get( 'wgCategories' ), function ( index, cat ) {// loop in all categories of the image
if ( cat.indexOf( 'flag' ) !== -1 && cat.indexOf( 'aspect ratio' ) !== -1 ) {
return true;// there is a category by aspect ratio
}
} );
return false;// there aren't
},
openEdit: function ( action, category, accuracy ) {
// mark this page for adding the template (just later)
this.storage['FlagSense-currentAction'] = action;
if ( category !== null && accuracy !== null ) {
this.storage['FlagSense-category'] = category;
this.storage['FlagSense-accuracy'] = accuracy;
}
document.location = $( '#ca-edit a' ).attr( 'href' ); // go to edit page
},
closeEdit: function () {
// reset the storage
this.storage.removeItem( 'FlagSense-currentAction' );
this.storage.removeItem( 'FlagSense-category' );
this.storage.removeItem( 'FlagSense-accuracy' );
if ( this.autoSubmit === true ) {
document.editform.submit(); // automatically submit the edit form
}
},
regex: {
VVA: /\{\{(Vector Version Available|Converted to SVG|SVG available|NowSVG|VVA)\|[^\n\{\}]+\}\}/i,
toSVG: /\{\{([Cc]onvert( to |[Tt]o)|[Ss]hould( be |[Bb]e)|[Tt]o)?SVG(\|[a-zA-Z]+)?\}\}/,
toPNG: /\{\{([Cc]onvert to |[Ss]hould( be |[Bb]e))PNG\}\}/,
ssPNG: /\{\{[Ss]upersededPNG\|[^\n\{\}]+\}\}/,
aspectRatioCat: /\[\[Category:.*?Flag.+?aspect ratio.+?\]\]/gi
},
autoSubmit: false
};
if ( mw.config.get( 'wgCanonicalNamespace' ) === 'File' && FlagSense.storage !== undefined ) {
// Make sure the page is a file description
FlagSense.messages = new mw.Map( $.extend(
{}, FlagSense.messages.en,
( FlagSense.messages[mw.config.get( 'wgUserLanguage' )] || {} )
) );
$( document ).ready( function () {
FlagSense.start();
} );
}
}( mediaWiki, jQuery ) );