jenkins-bot has submitted this change and it was merged. Change subject: Fix hash self-reaction ......................................................................
Fix hash self-reaction * Fixes bug where the mmv would react to its own hash changes * Adds jquery.hashchange for older browser support Change-Id: I2d23699bb444d9eb533d239e25bc47fa96c902a9 Mingle: https://wikimedia.mingle.thoughtworks.com/projects/multimedia/cards/254 --- M .jshintignore M MultimediaViewer.php A resources/jquery.hashchange/jquery.hashchange.js M resources/mmv/mmv.bootstrap.js M resources/mmv/mmv.js M resources/mmv/mmv.lightboxinterface.js M tests/qunit/mmv.bootstrap.test.js M tests/qunit/mmv.lightboxinterface.test.js M tests/qunit/mmv.test.js 9 files changed, 474 insertions(+), 32 deletions(-) Approvals: Gilles: Looks good to me, approved jenkins-bot: Verified diff --git a/.jshintignore b/.jshintignore index bbac23b..bc13f20 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1,3 +1,3 @@ resources/jquery.scrollTo -resources/jquery.throttle.debounce +resources/jquery.hashchange docs/js/* diff --git a/MultimediaViewer.php b/MultimediaViewer.php index 29abb0e..b47866e 100644 --- a/MultimediaViewer.php +++ b/MultimediaViewer.php @@ -452,6 +452,7 @@ 'mediawiki.Title', 'mmv.logger', 'mmv.ui', + 'jquery.hashchange', ), ), $moduleInfo( 'mmv' ) ); @@ -461,6 +462,12 @@ ), ), $moduleInfo( 'jquery.scrollTo' ) ); + $wgResourceModules['jquery.hashchange'] = array_merge( array( + 'scripts' => array( + 'jquery.hashchange.js', + ), + ), $moduleInfo( 'jquery.hashchange' ) ); + $wgExtensionFunctions[] = function () { global $wgResourceModules; diff --git a/resources/jquery.hashchange/jquery.hashchange.js b/resources/jquery.hashchange/jquery.hashchange.js new file mode 100644 index 0000000..1f0592b --- /dev/null +++ b/resources/jquery.hashchange/jquery.hashchange.js @@ -0,0 +1,390 @@ +/*! + * jQuery hashchange event - v1.3 - 7/21/2010 + * http://benalman.com/projects/jquery-hashchange-plugin/ + * + * Copyright (c) 2010 "Cowboy" Ben Alman + * Dual licensed under the MIT and GPL licenses. + * http://benalman.com/about/license/ + */ + +// Script: jQuery hashchange event +// +// *Version: 1.3, Last updated: 7/21/2010* +// +// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/ +// GitHub - http://github.com/cowboy/jquery-hashchange/ +// Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js +// (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped) +// +// About: License +// +// Copyright (c) 2010 "Cowboy" Ben Alman, +// Dual licensed under the MIT and GPL licenses. +// http://benalman.com/about/license/ +// +// About: Examples +// +// These working examples, complete with fully commented code, illustrate a few +// ways in which this plugin can be used. +// +// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/ +// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/ +// +// About: Support and Testing +// +// Information about what version or versions of jQuery this plugin has been +// tested with, what browsers it has been tested in, and where the unit tests +// reside (so you can test it yourself). +// +// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2 +// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5, +// Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5. +// Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/ +// +// About: Known issues +// +// While this jQuery hashchange event implementation is quite stable and +// robust, there are a few unfortunate browser bugs surrounding expected +// hashchange event-based behaviors, independent of any JavaScript +// window.onhashchange abstraction. See the following examples for more +// information: +// +// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/ +// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/ +// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/ +// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/ +// +// Also note that should a browser natively support the window.onhashchange +// event, but not report that it does, the fallback polling loop will be used. +// +// About: Release History +// +// 1.3 - (7/21/2010) Reorganized IE6/7 Iframe code to make it more +// "removable" for mobile-only development. Added IE6/7 document.title +// support. Attempted to make Iframe as hidden as possible by using +// techniques from http://www.paciellogroup.com/blog/?p=604. Added +// support for the "shortcut" format $(window).hashchange( fn ) and +// $(window).hashchange() like jQuery provides for built-in events. +// Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and +// lowered its default value to 50. Added <jQuery.fn.hashchange.domain> +// and <jQuery.fn.hashchange.src> properties plus document-domain.html +// file to address access denied issues when setting document.domain in +// IE6/7. +// 1.2 - (2/11/2010) Fixed a bug where coming back to a page using this plugin +// from a page on another domain would cause an error in Safari 4. Also, +// IE6/7 Iframe is now inserted after the body (this actually works), +// which prevents the page from scrolling when the event is first bound. +// Event can also now be bound before DOM ready, but it won't be usable +// before then in IE6/7. +// 1.1 - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug +// where browser version is incorrectly reported as 8.0, despite +// inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag. +// 1.0 - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special +// window.onhashchange functionality into a separate plugin for users +// who want just the basic event & back button support, without all the +// extra awesomeness that BBQ provides. This plugin will be included as +// part of jQuery BBQ, but also be available separately. + +(function($,window,undefined){ + '$:nomunge'; // Used by YUI compressor. + + // Reused string. + var str_hashchange = 'hashchange', + + // Method / object references. + doc = document, + fake_onhashchange, + special = $.event.special, + + // Does the browser support window.onhashchange? Note that IE8 running in + // IE7 compatibility mode reports true for 'onhashchange' in window, even + // though the event isn't supported, so also test document.documentMode. + doc_mode = doc.documentMode, + supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 ); + + // Get location.hash (or what you'd expect location.hash to be) sans any + // leading #. Thanks for making this necessary, Firefox! + function get_fragment( url ) { + url = url || location.href; + return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' ); + }; + + // Method: jQuery.fn.hashchange + // + // Bind a handler to the window.onhashchange event or trigger all bound + // window.onhashchange event handlers. This behavior is consistent with + // jQuery's built-in event handlers. + // + // Usage: + // + // > jQuery(window).hashchange( [ handler ] ); + // + // Arguments: + // + // handler - (Function) Optional handler to be bound to the hashchange + // event. This is a "shortcut" for the more verbose form: + // jQuery(window).bind( 'hashchange', handler ). If handler is omitted, + // all bound window.onhashchange event handlers will be triggered. This + // is a shortcut for the more verbose + // jQuery(window).trigger( 'hashchange' ). These forms are described in + // the <hashchange event> section. + // + // Returns: + // + // (jQuery) The initial jQuery collection of elements. + + // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and + // $(elem).hashchange() for triggering, like jQuery does for built-in events. + $.fn[ str_hashchange ] = function( fn ) { + return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange ); + }; + + // Property: jQuery.fn.hashchange.delay + // + // The numeric interval (in milliseconds) at which the <hashchange event> + // polling loop executes. Defaults to 50. + + // Property: jQuery.fn.hashchange.domain + // + // If you're setting document.domain in your JavaScript, and you want hash + // history to work in IE6/7, not only must this property be set, but you must + // also set document.domain BEFORE jQuery is loaded into the page. This + // property is only applicable if you are supporting IE6/7 (or IE8 operating + // in "IE7 compatibility" mode). + // + // In addition, the <jQuery.fn.hashchange.src> property must be set to the + // path of the included "document-domain.html" file, which can be renamed or + // modified if necessary (note that the document.domain specified must be the + // same in both your main JavaScript as well as in this file). + // + // Usage: + // + // jQuery.fn.hashchange.domain = document.domain; + + // Property: jQuery.fn.hashchange.src + // + // If, for some reason, you need to specify an Iframe src file (for example, + // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can + // do so using this property. Note that when using this property, history + // won't be recorded in IE6/7 until the Iframe src file loads. This property + // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7 + // compatibility" mode). + // + // Usage: + // + // jQuery.fn.hashchange.src = 'path/to/file.html'; + + $.fn[ str_hashchange ].delay = 50; + /* + $.fn[ str_hashchange ].domain = null; + $.fn[ str_hashchange ].src = null; + */ + + // Event: hashchange event + // + // Fired when location.hash changes. In browsers that support it, the native + // HTML5 window.onhashchange event is used, otherwise a polling loop is + // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to + // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7 + // compatibility" mode), a hidden Iframe is created to allow the back button + // and hash-based history to work. + // + // Usage as described in <jQuery.fn.hashchange>: + // + // > // Bind an event handler. + // > jQuery(window).hashchange( function(e) { + // > var hash = location.hash; + // > ... + // > }); + // > + // > // Manually trigger the event handler. + // > jQuery(window).hashchange(); + // + // A more verbose usage that allows for event namespacing: + // + // > // Bind an event handler. + // > jQuery(window).bind( 'hashchange', function(e) { + // > var hash = location.hash; + // > ... + // > }); + // > + // > // Manually trigger the event handler. + // > jQuery(window).trigger( 'hashchange' ); + // + // Additional Notes: + // + // * The polling loop and Iframe are not created until at least one handler + // is actually bound to the 'hashchange' event. + // * If you need the bound handler(s) to execute immediately, in cases where + // a location.hash exists on page load, via bookmark or page refresh for + // example, use jQuery(window).hashchange() or the more verbose + // jQuery(window).trigger( 'hashchange' ). + // * The event can be bound before DOM ready, but since it won't be usable + // before then in IE6/7 (due to the necessary Iframe), recommended usage is + // to bind it inside a DOM ready handler. + + // Override existing $.event.special.hashchange methods (allowing this plugin + // to be defined after jQuery BBQ in BBQ's source code). + special[ str_hashchange ] = $.extend( special[ str_hashchange ], { + + // Called only when the first 'hashchange' event is bound to window. + setup: function() { + // If window.onhashchange is supported natively, there's nothing to do.. + if ( supports_onhashchange ) { return false; } + + // Otherwise, we need to create our own. And we don't want to call this + // until the user binds to the event, just in case they never do, since it + // will create a polling loop and possibly even a hidden Iframe. + $( fake_onhashchange.start ); + }, + + // Called only when the last 'hashchange' event is unbound from window. + teardown: function() { + // If window.onhashchange is supported natively, there's nothing to do.. + if ( supports_onhashchange ) { return false; } + + // Otherwise, we need to stop ours (if possible). + $( fake_onhashchange.stop ); + } + + }); + + // fake_onhashchange does all the work of triggering the window.onhashchange + // event for browsers that don't natively support it, including creating a + // polling loop to watch for hash changes and in IE 6/7 creating a hidden + // Iframe to enable back and forward. + fake_onhashchange = (function(){ + var self = {}, + timeout_id, + + // Remember the initial hash so it doesn't get triggered immediately. + last_hash = get_fragment(), + + fn_retval = function(val){ return val; }, + history_set = fn_retval, + history_get = fn_retval; + + // Start the polling loop. + self.start = function() { + timeout_id || poll(); + }; + + // Stop the polling loop. + self.stop = function() { + timeout_id && clearTimeout( timeout_id ); + timeout_id = undefined; + }; + + // This polling loop checks every $.fn.hashchange.delay milliseconds to see + // if location.hash has changed, and triggers the 'hashchange' event on + // window when necessary. + function poll() { + var hash = get_fragment(), + history_hash = history_get( last_hash ); + + if ( hash !== last_hash ) { + history_set( last_hash = hash, history_hash ); + + $(window).trigger( str_hashchange ); + + } else if ( history_hash !== last_hash ) { + location.href = location.href.replace( /#.*/, '' ) + history_hash; + } + + timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay ); + }; + + // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv + // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + $.browser.msie && !supports_onhashchange && (function(){ + // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8 + // when running in "IE7 compatibility" mode. + + var iframe, + iframe_src; + + // When the event is bound and polling starts in IE 6/7, create a hidden + // Iframe for history handling. + self.start = function(){ + if ( !iframe ) { + iframe_src = $.fn[ str_hashchange ].src; + iframe_src = iframe_src && iframe_src + get_fragment(); + + // Create hidden Iframe. Attempt to make Iframe as hidden as possible + // by using techniques from http://www.paciellogroup.com/blog/?p=604. + iframe = $('<iframe tabindex="-1" title="empty"/>').hide() + + // When Iframe has completely loaded, initialize the history and + // start polling. + .one( 'load', function(){ + iframe_src || history_set( get_fragment() ); + poll(); + }) + + // Load Iframe src if specified, otherwise nothing. + .attr( 'src', iframe_src || 'javascript:0' ) + + // Append Iframe after the end of the body to prevent unnecessary + // initial page scrolling (yes, this works). + .insertAfter( 'body' )[0].contentWindow; + + // Whenever `document.title` changes, update the Iframe's title to + // prettify the back/next history menu entries. Since IE sometimes + // errors with "Unspecified error" the very first time this is set + // (yes, very useful) wrap this with a try/catch block. + doc.onpropertychange = function(){ + try { + if ( event.propertyName === 'title' ) { + iframe.document.title = doc.title; + } + } catch(e) {} + }; + + } + }; + + // Override the "stop" method since an IE6/7 Iframe was created. Even + // if there are no longer any bound event handlers, the polling loop + // is still necessary for back/next to work at all! + self.stop = fn_retval; + + // Get history by looking at the hidden Iframe's location.hash. + history_get = function() { + return get_fragment( iframe.location.href ); + }; + + // Set a new history item by opening and then closing the Iframe + // document, *then* setting its location.hash. If document.domain has + // been set, update that as well. + history_set = function( hash, history_hash ) { + var iframe_doc = iframe.document, + domain = $.fn[ str_hashchange ].domain; + + if ( hash !== history_hash ) { + // Update Iframe with any initial `document.title` that might be set. + iframe_doc.title = doc.title; + + // Opening the Iframe's document after it has been closed is what + // actually adds a history entry. + iframe_doc.open(); + + // Set document.domain for the Iframe document as well, if necessary. + domain && iframe_doc.write( '<script>document.domain="' + domain + '"</script>' ); + + iframe_doc.close(); + + // Update the Iframe's hash, for great justice. + iframe.location.hash = hash; + } + }; + + })(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^ + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + return self; + })(); + +})(jQuery,this); diff --git a/resources/mmv/mmv.bootstrap.js b/resources/mmv/mmv.bootstrap.js index cad5a87..187bac6 100755 --- a/resources/mmv/mmv.bootstrap.js +++ b/resources/mmv/mmv.bootstrap.js @@ -39,10 +39,13 @@ this.thumbs = []; this.$thumbs = $( '.gallery .image img, a.image img' ); this.processThumbs(); - this.hash(); - $( window ).on( 'popstate.mmvb', function () { + $( window ).hashchange( function () { self.hash(); + } ).hashchange(); + + $( document ).on( 'mmv.hash', function ( e ) { + self.internalHashChange( e ); } ); } @@ -159,7 +162,7 @@ }; /** - * Handles the browser location hash on pageload or popstate + * Handles the browser location hash on pageload or hash change */ MMVB.hash = function () { // There is no point loading the mmv if it isn't loaded yet for hash changes unrelated to the mmv @@ -168,11 +171,27 @@ return; } + if ( this.skipNextHashHandling ) { + this.skipNextHashHandling = false; + return; + } + this.loadViewer().then( function () { mw.mediaViewer.hash(); } ); }; + /** + * Handles hash change requests coming from mmv + * @param {jQuery.Event} e Custom mmv.hash event + */ + MMVB.internalHashChange = function ( e ) { + // Since we voluntarily changed the hash, we don't want MMVB.hash to treat it + this.skipNextHashHandling = true; + + window.location.hash = e.hash; + }; + mw.mmv.MultimediaViewerBootstrap = MultimediaViewerBootstrap; bootstrap = new MultimediaViewerBootstrap(); diff --git a/resources/mmv/mmv.js b/resources/mmv/mmv.js index 23e57e3..3878625 100755 --- a/resources/mmv/mmv.js +++ b/resources/mmv/mmv.js @@ -17,7 +17,7 @@ ( function ( mw, $ ) { var MMVP, - comingFromPopstate = false; + comingFromHashChange = false; /** * Analyses the page, looks for image content and sets up the hooks @@ -214,7 +214,7 @@ this.currentImageFilename = image.filePageTitle.getPrefixedText(); this.currentImageFileTitle = image.filePageTitle; - this.lightbox.iface.comingFromPopstate = this.comingFromPopstate; + this.lightbox.iface.comingFromHashChange = this.comingFromHashChange; if ( !this.isOpen ) { this.lightbox.open(); @@ -253,7 +253,7 @@ .then( function() { viewer.startListeningToScroll(); } ); } ); - this.comingFromPopstate = false; + this.comingFromHashChange = false; }; /** @@ -268,7 +268,7 @@ return; } - this.comingFromPopstate = !updateHash; + this.comingFromHashChange = !updateHash; $.each( this.thumbs, function ( idx, thumb ) { if ( thumb.title.getPrefixedText() === title ) { @@ -541,10 +541,10 @@ */ MMVP.close = function () { $( document.body ).removeClass( 'mw-mlb-lightbox-open' ); - if ( comingFromPopstate === false ) { - history.pushState( {}, '', '#' ); + if ( comingFromHashChange === false ) { + $( document ).trigger( $.Event( 'mmv.hash', { hash : '#' } ) ); } else { - comingFromPopstate = false; + comingFromHashChange = false; } this.isOpen = false; @@ -554,14 +554,14 @@ * Handles a hash change coming from the browser */ MMVP.hash = function () { - var hash = decodeURIComponent( document.location.hash ), + var hash = decodeURIComponent( window.location.hash ), linkState = hash.split( '/' ); if ( linkState[0] === '#mediaviewer' ) { this.loadImageByTitle( linkState[ 1 ] ); } else if ( this.isOpen ) { - // This allows us to avoid the pushState that normally happens on close - comingFromPopstate = true; + // This allows us to avoid the mmv.hash event that normally happens on close + comingFromHashChange = true; if ( this.lightbox && this.lightbox.iface ) { this.lightbox.iface.unattach(); diff --git a/resources/mmv/mmv.lightboxinterface.js b/resources/mmv/mmv.lightboxinterface.js index 9e8ea02..cfc7e1a 100644 --- a/resources/mmv/mmv.lightboxinterface.js +++ b/resources/mmv/mmv.lightboxinterface.js @@ -152,8 +152,8 @@ this.viewer.ui = this; - if ( !this.comingFromPopstate ) { - history.pushState( {}, '', hashFragment ); + if ( !this.comingFromHashChange ) { + $( document ).trigger( $.Event( 'mmv.hash', { hash : hashFragment } ) ); } this.handleEvent( 'keydown', function ( e ) { ui.keydown( e ); } ); diff --git a/tests/qunit/mmv.bootstrap.test.js b/tests/qunit/mmv.bootstrap.test.js index a2511f7..7238618 100644 --- a/tests/qunit/mmv.bootstrap.test.js +++ b/tests/qunit/mmv.bootstrap.test.js @@ -171,7 +171,7 @@ bootstrap.hash = $.noop; } ); - QUnit.test( 'Only load the viewer on a valid hash', 1, function ( assert ) { + QUnit.asyncTest( 'Only load the viewer on a valid hash', 1, function ( assert ) { var bootstrap; window.location.hash = ''; @@ -186,14 +186,38 @@ window.location.hash = 'Foo'; bootstrap.loadViewer = function () { + QUnit.start(); assert.ok( true, 'Viewer should be loaded' ); + bootstrap.hash = $.noop; + window.location.hash = ''; return $.Deferred().reject(); }; window.location.hash = 'mediaviewer/foo'; + } ); - bootstrap.hash = $.noop; - + QUnit.asyncTest( 'internalHashChange', 1, function ( assert ) { window.location.hash = ''; + + var bootstrap = new mw.mmv.MultimediaViewerBootstrap(), + hash = '#mediaviewer/foo', + oldHash = bootstrap.hash; + + bootstrap.hash = function () { + oldHash.call( this ); + + bootstrap.hash = $.noop; + window.location.hash = ''; + QUnit.start(); + }; + + bootstrap.loadViewer = function () { + assert.ok( false, 'Viewer should not be loaded' ); + return $.Deferred().reject(); + }; + + bootstrap.internalHashChange( { hash: hash } ); + + assert.strictEqual( window.location.hash, hash, 'Window\'s hash has been updated correctly' ); } ); }( mediaWiki, jQuery ) ); diff --git a/tests/qunit/mmv.lightboxinterface.test.js b/tests/qunit/mmv.lightboxinterface.test.js index 1ca8a71..ddd20dd 100644 --- a/tests/qunit/mmv.lightboxinterface.test.js +++ b/tests/qunit/mmv.lightboxinterface.test.js @@ -212,7 +212,7 @@ mw.mediaViewer.lightbox = { currentIndex: 0 }; // This lets us avoid pushing a state to the history, which might interfere with other tests - lightbox.comingFromPopstate = true; + lightbox.comingFromHashChange = true; // Load is needed to start listening to metadata events lightbox.load( { getImageElement: function() { return $.Deferred().reject(); } } ); diff --git a/tests/qunit/mmv.test.js b/tests/qunit/mmv.test.js index 8b7aafb..c404a07 100644 --- a/tests/qunit/mmv.test.js +++ b/tests/qunit/mmv.test.js @@ -87,7 +87,7 @@ imageSrc = 'Foo bar.jpg', image = { filePageTitle: new mw.Title( 'File:' + imageSrc ) }; - document.location.hash = ''; + window.location.hash = ''; oldUnattach = lightbox.unattach; @@ -102,16 +102,14 @@ assert.ok( !mw.mediaViewer.isOpen, 'Viewer is closed' ); - // The mediaViewer won't be initialized through bootstrap by any other way than a valid action - document.location.hash = 'mediaviewer/Foo'; mw.mediaViewer.isOpen = true; - // From now on we're certain that the viewer receives hash changes through bootstrap // Verify that passing an invalid mmv hash when the mmv is open triggers unattach() - document.location.hash = 'Foo'; + window.location.hash = 'Foo'; + mw.mediaViewer.hash(); // Verify that mmv doesn't reset a foreign hash - assert.strictEqual( document.location.hash, '#Foo', 'Foreign hash remains intact' ); + assert.strictEqual( window.location.hash, '#Foo', 'Foreign hash remains intact' ); assert.ok( !mw.mediaViewer.isOpen, 'Viewer is closed' ); lightbox.unattach = function () { @@ -120,10 +118,11 @@ }; // Verify that passing an invalid mmv hash when the mmv is closed doesn't trigger unattach() - document.location.hash = 'Bar'; + window.location.hash = 'Bar'; + mw.mediaViewer.hash(); // Verify that mmv doesn't reset a foreign hash - assert.strictEqual( document.location.hash, '#Bar', 'Foreign hash remains intact' ); + assert.strictEqual( window.location.hash, '#Bar', 'Foreign hash remains intact' ); mw.mediaViewer.lightbox = { images: [ image ] }; @@ -135,18 +134,21 @@ // Open a valid mmv hash link and check that the right image is requested. // imageSrc contains a space without any encoding on purpose - document.location.hash = 'mediaviewer/File:' + imageSrc; + window.location.hash = 'mediaviewer/File:' + imageSrc; + mw.mediaViewer.hash(); // Reset the hash, because for some browsers switching from the non-URI-encoded to // the non-URI-encoded version of the same text with a space will not trigger a hash change - document.location.hash = ''; + window.location.hash = ''; + mw.mediaViewer.hash(); // Try again with an URI-encoded imageSrc containing a space - document.location.hash = 'mediaviewer/File:' + encodeURIComponent( imageSrc ); + window.location.hash = 'mediaviewer/File:' + encodeURIComponent( imageSrc ); + mw.mediaViewer.hash(); mw.mediaViewer.lightbox = oldLightbox; mw.mediaViewer.loadImageByTitle = oldLoadImage; - document.location.hash = ''; + window.location.hash = ''; } ); }( mediaWiki, jQuery ) ); -- To view, visit https://gerrit.wikimedia.org/r/115129 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I2d23699bb444d9eb533d239e25bc47fa96c902a9 Gerrit-PatchSet: 3 Gerrit-Project: mediawiki/extensions/MultimediaViewer Gerrit-Branch: master Gerrit-Owner: Gilles <gdu...@wikimedia.org> Gerrit-Reviewer: Aarcos <aarcos.w...@gmail.com> Gerrit-Reviewer: Gergő Tisza <gti...@wikimedia.org> Gerrit-Reviewer: Gilles <gdu...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits