diff --git a/extension.json b/extension.json
index a424484af..304b7b5f0 100644
--- a/extension.json
+++ b/extension.json
@@ -48,6 +48,8 @@
"formats/ext.srf.formats.eventcalendar.tests.js",
"formats/ext.srf.formats.filtered.test.js",
"formats/ext.srf.formats.gallery.test.js",
+ "formats/ext.srf.formats.gallery.overlay.test.js",
+ "formats/ext.srf.formats.gallery.redirect.test.js",
"formats/ext.srf.formats.media.test.js",
"formats/ext.srf.formats.tagcloud.test.js",
"widgets/ext.srf.widgets.eventcalendar.tests.js",
diff --git a/formats/gallery/resources/ext.srf.gallery.overlay.js b/formats/gallery/resources/ext.srf.gallery.overlay.js
index 77844a3e1..7159a4e4a 100644
--- a/formats/gallery/resources/ext.srf.gallery.overlay.js
+++ b/formats/gallery/resources/ext.srf.gallery.overlay.js
@@ -76,7 +76,7 @@
$this.find( '.gallerybox' ).each( function () {
var $this = $( this ),
h = mw.html,
- image = $this.find( 'a.image' ),
+ image = $this.find( 'a.mw-file-description' ),
imageText = $.trim( $this.find( '.gallerytext p' ).text() );
// Group images
diff --git a/formats/gallery/resources/ext.srf.gallery.redirect.js b/formats/gallery/resources/ext.srf.gallery.redirect.js
index a8f2dfc51..ee28a017f 100644
--- a/formats/gallery/resources/ext.srf.gallery.redirect.js
+++ b/formats/gallery/resources/ext.srf.gallery.redirect.js
@@ -59,7 +59,7 @@
return context.find( '.gallerybox' ).each( function() {
var $this = $( this ),
h = mw.html,
- image = $this.find( 'a.image' );
+ image = $this.find( 'a.mw-file-description' );
// Avoid undefined error
if ( image.attr( 'href' ) === undefined ) {
diff --git a/resources/jquery/fancybox/jquery.fancybox-1.3.4.pack.js b/resources/jquery/fancybox/jquery.fancybox-1.3.4.pack.js
index 1373ed083..1daa83fd0 100644
--- a/resources/jquery/fancybox/jquery.fancybox-1.3.4.pack.js
+++ b/resources/jquery/fancybox/jquery.fancybox-1.3.4.pack.js
@@ -28,7 +28,7 @@ d.titlePosition=="float"?'
0&&n.show();j.css({width:i.width-d.padding*2,height:e.autoDimensions?"auto":i.height-y-d.padding*2}).html(m.contents());f.css(i).fadeIn(d.transitionIn=="none"?0:d.speedIn,S)}}}},Y=function(){if(d.enableEscapeButton||d.enableKeyboardNav)b(document).bind("keydown.fb",function(a){if(a.keyCode==27&&d.enableEscapeButton){a.preventDefault();b.fancybox.close()}else if((a.keyCode==
-37||a.keyCode==39)&&d.enableKeyboardNav&&a.target.tagName!=="INPUT"&&a.target.tagName!=="TEXTAREA"&&a.target.tagName!=="SELECT"){a.preventDefault();b.fancybox[a.keyCode==37?"prev":"next"]()}});if(d.showNavArrows){if(d.cyclic&&l.length>1||p!==0)z.show();if(d.cyclic&&l.length>1||p!=l.length-1)A.show()}else{z.hide();A.hide()}},S=function(){if(!b.support.opacity){j.get(0).style.removeAttribute("filter");f.get(0).style.removeAttribute("filter")}e.autoDimensions&&j.css("height","auto");f.css("height","auto");
+37||a.keyCode==39)&&d.enableKeyboardNav&&a.target.tagName!=="INPUT"&&a.target.tagName!=="TEXTAREA"&&a.target.tagName!=="SELECT"){a.preventDefault();b.fancybox[a.keyCode==37?"prev":"next"]()}});if(d.showNavArrows){if(d.cyclic&&l.length>1||p!==0)z.show();if(d.cyclic&&l.length>1||p!=l.length-1)A.show()}else{z.hide();A.hide()}},S=function(){if(!b.support.opacity){j.css("filter",0);f.css("filter",0)}e.autoDimensions&&j.css("height","auto");f.css("height","auto");
s&&s.length&&n.show();d.showCloseButton&&E.show();Y();d.hideOnContentClick&&j.bind("click",b.fancybox.close);d.hideOnOverlayClick&&u.bind("click",b.fancybox.close);b(window).bind("resize.fb",b.fancybox.resize);d.centerOnScroll&&b(window).bind("scroll.fb",b.fancybox.center);if(d.type=="iframe")b('').appendTo(j);
f.show();h=false;b.fancybox.center();d.onComplete(l,p,d);var a,c;if(l.length-1>p){a=l[p+1].href;if(typeof a!=="undefined"&&a.match(J)){c=new Image;c.src=a}}if(p>0){a=l[p-1].href;if(typeof a!=="undefined"&&a.match(J)){c=new Image;c.src=a}}},T=function(a){var c={width:parseInt(r.width+(i.width-r.width)*a,10),height:parseInt(r.height+(i.height-r.height)*a,10),top:parseInt(r.top+(i.top-r.top)*a,10),left:parseInt(r.left+(i.left-r.left)*a,10)};if(typeof i.opacity!=="undefined")c.opacity=a<0.5?0.5:a;f.css(c);
j.css({width:c.width-d.padding*2,height:c.height-y*a-d.padding*2})},U=function(){return[b(window).width()-d.margin*2,b(window).height()-d.margin*2,b(document).scrollLeft()+d.margin,b(document).scrollTop()+d.margin]},X=function(){var a=U(),c={},g=d.autoScale,k=d.padding*2;c.width=d.width.toString().indexOf("%")>-1?parseInt(a[0]*parseFloat(d.width)/100,10):d.width+k;c.height=d.height.toString().indexOf("%")>-1?parseInt(a[1]*parseFloat(d.height)/100,10):d.height+k;if(g&&(c.width>a[0]||c.height>a[1]))if(e.type==
diff --git a/tests/qunit/formats/ext.srf.formats.gallery.overlay.test.js b/tests/qunit/formats/ext.srf.formats.gallery.overlay.test.js
new file mode 100644
index 000000000..53f488e65
--- /dev/null
+++ b/tests/qunit/formats/ext.srf.formats.gallery.overlay.test.js
@@ -0,0 +1,105 @@
+/**
+ * QUnit tests for the SRF gallery overlay functionality
+ *
+ * @file
+ * @since 5.1.0
+ *
+ * @ingroup SRF
+ *
+ * @licence GPL-2.0-or-later
+ * @author GitHub Copilot Assistant
+ */
+
+( function ( $, mw, srf ) {
+ 'use strict';
+
+ QUnit.module( 'ext.srf.formats.gallery.overlay', QUnit.newMwEnvironment() );
+
+ /**
+ * Test overlay initialization with mw-file-description selector
+ */
+ QUnit.test( 'overlay handles mw-file-description selector', function ( assert ) {
+ assert.expect( 4 );
+
+ // Create mock HTML structure with new MediaWiki gallery format
+ var mockHtml = $(
+ '' +
+ '- ' +
+ '
' +
+ '
' +
+ '
' +
+ '
Test image description
' +
+ '
' +
+ '
' +
+ ' ' +
+ '
'
+ );
+
+ $( '#qunit-fixture' ).append( mockHtml );
+ var context = $( '#test-gallery' );
+ var gallery = new srf.formats.gallery();
+
+ // Test that the overlay function finds the correct selector
+ gallery.overlay( context, 'File' );
+
+ var imageLink = context.find( 'a.mw-file-description' );
+ assert.ok( imageLink.length > 0, 'Found image link with mw-file-description class' );
+ assert.equal( imageLink.attr( 'rel' ), 'test-gallery', 'Image link has correct rel attribute for grouping' );
+ assert.equal( imageLink.attr( 'title' ), 'Test image description', 'Image link has correct title from gallery text' );
+ assert.ok( imageLink.attr( 'href' ).indexOf( 'File:Test.jpg' ) > -1, 'Image link href points to correct file' );
+ } );
+
+ /**
+ * Test overlay with empty gallery
+ */
+ QUnit.test( 'overlay handles empty gallery gracefully', function ( assert ) {
+ assert.expect( 1 );
+
+ var emptyGallery = $( '' );
+ $( '#qunit-fixture' ).append( emptyGallery );
+
+ var gallery = new srf.formats.gallery();
+
+ // Should not throw error with empty gallery
+ gallery.overlay( emptyGallery, 'File' );
+
+ assert.ok( true, 'Overlay handles empty gallery without errors' );
+ } );
+
+ /**
+ * Test overlay with missing href attribute
+ */
+ QUnit.test( 'overlay handles missing href gracefully', function ( assert ) {
+ assert.expect( 2 );
+
+ var mockHtml = $(
+ '' +
+ '- ' +
+ '' +
+ '' +
+ '
' +
+ '
'
+ );
+
+ $( '#qunit-fixture' ).append( mockHtml );
+ var context = $( '#test-gallery-no-href' );
+ var gallery = new srf.formats.gallery();
+
+ gallery.overlay( context, 'File' );
+
+ var galleryBox = context.find( '.gallerybox' );
+ assert.ok( galleryBox.find( '.error' ).length > 0, 'Error message displayed for missing href' );
+ assert.ok( galleryBox.html().indexOf( 'srf-gallery-image-url-error' ) > -1, 'Correct error message key used' );
+ } );
+
+}( jQuery, mediaWiki, semanticFormats ) );
\ No newline at end of file
diff --git a/tests/qunit/formats/ext.srf.formats.gallery.redirect.test.js b/tests/qunit/formats/ext.srf.formats.gallery.redirect.test.js
new file mode 100644
index 000000000..cec61076f
--- /dev/null
+++ b/tests/qunit/formats/ext.srf.formats.gallery.redirect.test.js
@@ -0,0 +1,189 @@
+/**
+ * QUnit tests for the SRF gallery redirect functionality
+ *
+ * @file
+ * @since 5.1.0
+ *
+ * @ingroup SRF
+ *
+ * @licence GPL-2.0-or-later
+ * @author GitHub Copilot Assistant
+ */
+
+( function ( $, mw, srf ) {
+ 'use strict';
+
+ QUnit.module( 'ext.srf.formats.gallery.redirect', QUnit.newMwEnvironment() );
+
+ /**
+ * Mock srf.util for testing
+ */
+ var mockUtil = function() {
+ return {
+ spinner: {
+ create: function() {},
+ replace: function() {}
+ },
+ getTitleURL: function( params, callback ) {
+ // Mock successful URL retrieval
+ if ( params.title === 'Valid_Title' ) {
+ callback( '/wiki/Valid_Title' );
+ } else {
+ callback( false );
+ }
+ }
+ };
+ };
+
+ // Mock srf.util
+ var originalUtil = srf.util;
+ srf.util = mockUtil;
+
+ /**
+ * Test redirect functionality with mw-file-description selector
+ */
+ QUnit.test( 'redirect handles mw-file-description selector', function ( assert ) {
+ assert.expect( 3 );
+
+ var mockHtml = $(
+ '' +
+ '
' +
+ '- ' +
+ '' +
+ '
' +
+ '
' +
+ '
'
+ );
+
+ $( '#qunit-fixture' ).append( mockHtml );
+ var context = mockHtml;
+ var gallery = new srf.formats.gallery();
+
+ gallery.redirect( context );
+
+ var imageLink = context.find( 'a.mw-file-description' );
+ var redirectIcon = context.find( '.redirecticon' );
+
+ assert.ok( imageLink.length > 0, 'Found image link with mw-file-description class' );
+ assert.equal( imageLink.attr( 'href' ), 'http://example.com/redirect-target', 'Image href updated to redirect URL from alt attribute' );
+ assert.ok( redirectIcon.is( ':visible' ), 'Redirect icon is visible for URI type redirect' );
+ } );
+
+ /**
+ * Test redirect with article title resolution
+ */
+ QUnit.test( 'redirect resolves article titles', function ( assert ) {
+ var done = assert.async();
+ assert.expect( 3 );
+
+ var mockHtml = $(
+ '' +
+ '
' +
+ '- ' +
+ '' +
+ '
' +
+ '
' +
+ '
'
+ );
+
+ $( '#qunit-fixture' ).append( mockHtml );
+ var context = mockHtml;
+ var gallery = new srf.formats.gallery();
+
+ gallery.redirect( context );
+
+ // Wait for async URL resolution
+ setTimeout( function() {
+ var imageLink = context.find( 'a.mw-file-description' );
+ var redirectIcon = context.find( '.redirecticon' );
+
+ assert.ok( imageLink.length > 0, 'Found image link with mw-file-description class' );
+ assert.equal( imageLink.attr( 'href' ), '/wiki/Valid_Title', 'Image href updated to resolved article URL' );
+ assert.ok( redirectIcon.is( ':visible' ), 'Redirect icon is visible after successful title resolution' );
+ done();
+ }, 100 );
+ } );
+
+ /**
+ * Test redirect with missing href attribute
+ */
+ QUnit.test( 'redirect handles missing href gracefully', function ( assert ) {
+ assert.expect( 2 );
+
+ var mockHtml = $(
+ '' +
+ '
' +
+ '- ' +
+ '' +
+ '
' +
+ '
' +
+ '
'
+ );
+
+ $( '#qunit-fixture' ).append( mockHtml );
+ var context = mockHtml;
+ var gallery = new srf.formats.gallery();
+
+ gallery.redirect( context );
+
+ var galleryBox = context.find( '.gallerybox' );
+ assert.ok( galleryBox.find( '.error' ).length > 0, 'Error message displayed for missing href' );
+ assert.ok( galleryBox.html().indexOf( 'srf-gallery-image-url-error' ) > -1, 'Correct error message key used' );
+ } );
+
+ /**
+ * Test redirect with failed title resolution
+ */
+ QUnit.test( 'redirect handles failed title resolution', function ( assert ) {
+ var done = assert.async();
+ assert.expect( 2 );
+
+ var mockHtml = $(
+ '' +
+ '
' +
+ '- ' +
+ '' +
+ '
' +
+ '
' +
+ '
'
+ );
+
+ $( '#qunit-fixture' ).append( mockHtml );
+ var context = mockHtml;
+ var gallery = new srf.formats.gallery();
+
+ gallery.redirect( context );
+
+ // Wait for async URL resolution
+ setTimeout( function() {
+ var imageLink = context.find( 'a.mw-file-description' );
+ var redirectIcon = context.find( '.redirecticon' );
+
+ assert.equal( imageLink.attr( 'href' ), '', 'Image href cleared on failed title resolution' );
+ assert.ok( redirectIcon.is( ':hidden' ), 'Redirect icon hidden on failed title resolution' );
+ done();
+ }, 100 );
+ } );
+
+ // Restore original srf.util after tests
+ QUnit.testDone( function() {
+ srf.util = originalUtil;
+ } );
+
+}( jQuery, mediaWiki, semanticFormats ) );
\ No newline at end of file