Jhernandez has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/345485 )

Change subject: WIP: Hygiene: Use Function.bind instead of self = this
......................................................................

WIP: Hygiene: Use Function.bind instead of self = this

Change-Id: If44b2bddde8794283b04e98e0e7a847911160549
---
M resources/mobile.categories.overlays/CategoryAddOverlay.js
M resources/mobile.categories.overlays/CategoryGateway.js
M resources/mobile.categories.overlays/CategoryLookupInputWidget.js
M resources/mobile.categories.overlays/CategoryOverlay.js
M resources/mobile.editor.api/EditorGateway.js
M resources/mobile.editor.common/EditorOverlayBase.js
M resources/mobile.editor.overlay/EditorOverlay.js
M resources/mobile.editor.ve/VisualEditorOverlay.js
M resources/mobile.fontchanger/FontChanger.js
M resources/mobile.gallery/PhotoList.js
M resources/mobile.gallery/PhotoListGateway.js
M resources/mobile.infiniteScroll/InfiniteScroll.js
M resources/mobile.mainMenu/MainMenu.js
M resources/mobile.mediaViewer.beta/ImageOverlayBeta.js
M resources/mobile.mediaViewer/ImageOverlay.js
M resources/mobile.nearby/Nearby.js
M resources/mobile.nearby/NearbyGateway.js
M resources/mobile.pagelist.scripts/WatchstarPageList.js
M resources/mobile.pointerOverlay/PointerOverlay.js
M resources/mobile.references.gateway/ReferencesMobileViewGateway.js
M resources/mobile.search.api/SearchGateway.js
M resources/mobile.search/SearchOverlay.js
M resources/mobile.startup/Drawer.js
M resources/mobile.startup/Overlay.js
M resources/mobile.startup/OverlayManager.js
M resources/mobile.startup/Page.js
M resources/mobile.startup/PageGateway.js
M resources/mobile.startup/PageList.js
M resources/mobile.startup/Panel.js
M resources/mobile.startup/Section.js
M resources/mobile.startup/Skin.js
M resources/mobile.startup/View.js
M resources/mobile.startup/browser.js
M resources/mobile.startup/modules.js
M resources/mobile.swipe/Swipe.js
M resources/mobile.talk.overlays/TalkOverlay.js
M resources/mobile.talk.overlays/TalkSectionAddOverlay.js
M resources/mobile.talk.overlays/TalkSectionOverlay.js
M resources/mobile.toggle/toggle.js
M resources/mobile.watchlist/WatchListGateway.js
M resources/mobile.watchstar/Watchstar.js
M resources/mobile.watchstar/WatchstarGateway.js
M tests/qunit/mobile.search/test_MobileWebSearchLogger.js
M tests/qunit/mobile.startup/test_View.js
44 files changed, 510 insertions(+), 610 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/MobileFrontend 
refs/changes/85/345485/1

diff --git a/resources/mobile.categories.overlays/CategoryAddOverlay.js 
b/resources/mobile.categories.overlays/CategoryAddOverlay.js
index ff8db0a..99bea10 100644
--- a/resources/mobile.categories.overlays/CategoryAddOverlay.js
+++ b/resources/mobile.categories.overlays/CategoryAddOverlay.js
@@ -99,8 +99,7 @@
                 * and add it to the article.
                 */
                onSaveClick: function () {
-                       var newCategories = '',
-                               self = this;
+                       var newCategories = '';
 
                        // show the loading spinner and disable the safe button
                        this.showHidden( '.saving-header' );
@@ -124,11 +123,11 @@
                                        M.emit( 'category-added' );
                                        window.location.hash = '#/categories';
                                } ).fail( function () {
-                                       self.showHidden( '.initial-header' );
-                                       self.$safeButton.prop( 'disabled', 
false );
+                                       this.showHidden( '.initial-header' );
+                                       this.$safeButton.prop( 'disabled', 
false );
                                        // FIXME: Should be a better error 
message
                                        toast.show( mw.msg( 
'mobile-frontend-categories-nodata' ), 'toast error' );
-                               } );
+                               }.bind( this ) );
                        }
                }
        } );
diff --git a/resources/mobile.categories.overlays/CategoryGateway.js 
b/resources/mobile.categories.overlays/CategoryGateway.js
index 3eef9c1..e42ae75 100644
--- a/resources/mobile.categories.overlays/CategoryGateway.js
+++ b/resources/mobile.categories.overlays/CategoryGateway.js
@@ -39,8 +39,6 @@
                 * @return {jQuery.Deferred|boolean} False, if no further 
continuation is possible, jQuery.Deferred otherwise.
                 */
                getCategories: function ( title ) {
-                       var self = this;
-
                        if ( this.canContinue === false ) {
                                return false;
                        }
@@ -53,13 +51,13 @@
                                cllimit: 50
                        }, this.continueParams ) ).then( function ( data ) {
                                if ( data.hasOwnProperty( 'continue' ) ) {
-                                       self.continueParams = data.continue;
+                                       this.continueParams = data.continue;
                                } else {
-                                       self.canContinue = false;
+                                       this.canContinue = false;
                                }
 
                                return data;
-                       } );
+                       }.bind( this ) );
                }
        };
        OO.inheritClass( CategoryGateway, SearchGateway );
diff --git a/resources/mobile.categories.overlays/CategoryLookupInputWidget.js 
b/resources/mobile.categories.overlays/CategoryLookupInputWidget.js
index 54350a4..a77eeeb 100644
--- a/resources/mobile.categories.overlays/CategoryLookupInputWidget.js
+++ b/resources/mobile.categories.overlays/CategoryLookupInputWidget.js
@@ -70,13 +70,12 @@
         * @return {Array} Array of OO.ui.MenuOptionWidget
         */
        CategoryLookupInputWidget.prototype.getLookupMenuOptionsFromData = 
function ( data ) {
-               var result = [],
-                       self = this;
+               var result = [];
 
                data.results.forEach( function ( value ) {
                        if (
                                !$( 'div[data-title="' + value.title + '"]' 
).length &&
-                               $.inArray( value.displayTitle, self.categories 
) === -1
+                               $.inArray( value.displayTitle, this.categories 
) === -1
                        ) {
                                result.push(
                                        new OO.ui.MenuOptionWidget( {
@@ -85,7 +84,7 @@
                                        } )
                                );
                        }
-               } );
+               }.bind( this ) );
                return result;
        };
 
diff --git a/resources/mobile.categories.overlays/CategoryOverlay.js 
b/resources/mobile.categories.overlays/CategoryOverlay.js
index 9015ff1..46fe5ba 100644
--- a/resources/mobile.categories.overlays/CategoryOverlay.js
+++ b/resources/mobile.categories.overlays/CategoryOverlay.js
@@ -77,8 +77,7 @@
                 * Get a list of categories the page belongs to and re-renders 
the overlay content
                 */
                _loadCategories: function () {
-                       var self = this,
-                               $normalCatlist = this.$( '.normal-catlist' ),
+                       var $normalCatlist = this.$( '.normal-catlist' ),
                                $hiddenCatlist = this.$( '.hidden-catlist' ),
                                apiResult;
 
@@ -89,7 +88,7 @@
                        this.infiniteScroll.disable();
                        apiResult = this.gateway.getCategories( 
this.options.title );
                        if ( apiResult === false ) {
-                               self.clearSpinner();
+                               this.clearSpinner();
                                return;
                        }
                        apiResult.done( function ( data ) {
@@ -101,31 +100,31 @@
                                                                var title = 
mw.Title.newFromText( category.title, category.ns );
 
                                                                if ( 
category.hidden !== undefined ) {
-                                                                       
$hiddenCatlist.append( self.templatePartials.item.render( {
+                                                                       
$hiddenCatlist.append( this.templatePartials.item.render( {
                                                                                
url: title.getUrl(),
                                                                                
title: title.getNameText()
                                                                        } ) );
                                                                } else {
-                                                                       
$normalCatlist.append( self.templatePartials.item.render( {
+                                                                       
$normalCatlist.append( this.templatePartials.item.render( {
                                                                                
url: title.getUrl(),
                                                                                
title: title.getNameText()
                                                                        } ) );
                                                                }
-                                                       } );
+                                                       }.bind( this ) );
                                                }
-                                       } );
+                                       }.bind( this ) );
 
                                        if ( $normalCatlist.length === 0 && 
$normalCatlist.length === 0 ) {
-                                               self.$( '.content-header' 
).text( mw.msg( 'mobile-frontend-categories-nocat' ) );
+                                               this.$( '.content-header' 
).text( mw.msg( 'mobile-frontend-categories-nocat' ) );
                                        } else if ( $normalCatlist.length === 0 
&& $normalCatlist.length > 0 ) {
                                                this._changeView();
                                        }
                                } else {
-                                       self.$( '.content-header' ).text( 
mw.msg( 'mobile-frontend-categories-nocat' ) );
+                                       this.$( '.content-header' ).text( 
mw.msg( 'mobile-frontend-categories-nocat' ) );
                                }
-                               self.clearSpinner();
-                               self.infiniteScroll.enable();
-                       } );
+                               this.clearSpinner();
+                               this.infiniteScroll.enable();
+                       }.bind( this ) );
                },
 
                /**
diff --git a/resources/mobile.editor.api/EditorGateway.js 
b/resources/mobile.editor.api/EditorGateway.js
index 40f73fe..a2283cf 100644
--- a/resources/mobile.editor.api/EditorGateway.js
+++ b/resources/mobile.editor.api/EditorGateway.js
@@ -29,7 +29,6 @@
                 */
                getContent: function () {
                        var options,
-                               self = this,
                                result = $.Deferred();
 
                        if ( this.content !== undefined ) {
@@ -64,17 +63,17 @@
                                        pageObj = resp.query.pages[0];
                                        // page might not exist and caller 
might not have known.
                                        if ( pageObj.hasOwnProperty( 'missing' 
) ) {
-                                               self.content = '';
+                                               this.content = '';
                                        } else {
                                                revision = pageObj.revisions[0];
-                                               self.content = revision.content;
-                                               self.timestamp = 
revision.timestamp;
+                                               this.content = revision.content;
+                                               this.timestamp = 
revision.timestamp;
                                        }
                                        // save content a second time to be 
able to check for changes
-                                       self.originalContent = self.content;
+                                       this.originalContent = this.content;
 
-                                       result.resolve( self.content, 
resp.query.userinfo );
-                               } );
+                                       result.resolve( this.content, 
resp.query.userinfo );
+                               }.bind( this ) );
                        }
 
                        return result;
@@ -120,106 +119,99 @@
                 * of error, `details` can be any object (usually error 
message).
                 */
                save: function ( options ) {
-                       var self = this,
-                               result = $.Deferred();
+                       var result = $.Deferred(),
+                               apiOptions;
 
                        options = options || {};
 
-                       /**
-                        * Save content. Make an API request.
-                        * @ignore
-                        */
-                       function saveContent() {
-                               var apiOptions = {
-                                       action: 'edit',
-                                       title: self.title,
-                                       summary: options.summary,
-                                       captchaid: options.captchaId,
-                                       captchaword: options.captchaWord,
-                                       basetimestamp: self.timestamp,
-                                       starttimestamp: self.timestamp
-                               };
+                       apiOptions = {
+                               action: 'edit',
+                               title: this.title,
+                               summary: options.summary,
+                               captchaid: options.captchaId,
+                               captchaword: options.captchaWord,
+                               basetimestamp: this.timestamp,
+                               starttimestamp: this.timestamp
+                       };
 
-                               if ( self.content !== undefined ) {
-                                       apiOptions.text = self.content;
-                               } else if ( self.prependtext ) {
-                                       apiOptions.prependtext = 
self.prependtext;
-                               }
-
-                               if ( $.isNumeric( self.sectionId ) ) {
-                                       apiOptions.section = self.sectionId;
-                               }
-
-                               self.api.postWithToken( 'edit', apiOptions 
).done( function ( data ) {
-                                       var code, warning;
-
-                                       if ( data && data.edit && 
data.edit.result === 'Success' ) {
-                                               self.hasChanged = false;
-                                               result.resolve();
-                                       } else if ( data && data.error ) {
-                                               // Edit API error
-                                               result.reject( {
-                                                       type: 'error',
-                                                       details: data.error.code
-                                               } );
-                                       } else if ( data && data.edit && 
data.edit.captcha ) {
-                                               // CAPTCHAs
-                                               result.reject( {
-                                                       type: 'captcha',
-                                                       details: 
data.edit.captcha
-                                               } );
-                                       } else if ( data && data.edit && 
data.edit.code ) {
-                                               code = data.edit.code;
-                                               warning = data.edit.warning;
-
-                                               // FIXME: AbuseFilter should 
have more consistent API responses
-                                               if ( 
/^abusefilter-warning/.test( code ) ) {
-                                                       // AbuseFilter warning
-                                                       result.reject( {
-                                                               type: 
'abusefilter',
-                                                               details: {
-                                                                       type: 
'warning',
-                                                                       
message: warning
-                                                               }
-                                                       } );
-                                               } else if ( 
/^abusefilter-disallow/.test( code ) ) {
-                                                       // AbuseFilter disallow
-                                                       result.reject( {
-                                                               type: 
'abusefilter',
-                                                               details: {
-                                                                       type: 
'disallow',
-                                                                       
message: warning
-                                                               }
-                                                       } );
-                                               } else if ( 
/^abusefilter/.test( code ) ) {
-                                                       // AbuseFilter other
-                                                       result.reject( {
-                                                               type: 
'abusefilter',
-                                                               details: {
-                                                                       type: 
'other',
-                                                                       
message: warning
-                                                               }
-                                                       } );
-                                               } else {
-                                                       // other errors
-                                                       result.reject( {
-                                                               type: 'error',
-                                                               details: code
-                                                       } );
-                                               }
-                                       } else {
-                                               result.reject( {
-                                                       type: 'error',
-                                                       details: 'unknown'
-                                               } );
-                                       }
-                               } ).fail( $.proxy( result, 'reject', {
-                                       type: 'error',
-                                       details: 'http'
-                               } ) );
+                       if ( this.content !== undefined ) {
+                               apiOptions.text = this.content;
+                       } else if ( this.prependtext ) {
+                               apiOptions.prependtext = this.prependtext;
                        }
 
-                       saveContent();
+                       if ( $.isNumeric( this.sectionId ) ) {
+                               apiOptions.section = this.sectionId;
+                       }
+
+                       this.api.postWithToken( 'edit', apiOptions ).done( 
function ( data ) {
+                               var code, warning;
+
+                               if ( data && data.edit && data.edit.result === 
'Success' ) {
+                                       this.hasChanged = false;
+                                       result.resolve();
+                               } else if ( data && data.error ) {
+                                       // Edit API error
+                                       result.reject( {
+                                               type: 'error',
+                                               details: data.error.code
+                                       } );
+                               } else if ( data && data.edit && 
data.edit.captcha ) {
+                                       // CAPTCHAs
+                                       result.reject( {
+                                               type: 'captcha',
+                                               details: data.edit.captcha
+                                       } );
+                               } else if ( data && data.edit && data.edit.code 
) {
+                                       code = data.edit.code;
+                                       warning = data.edit.warning;
+
+                                       // FIXME: AbuseFilter should have more 
consistent API responses
+                                       if ( /^abusefilter-warning/.test( code 
) ) {
+                                               // AbuseFilter warning
+                                               result.reject( {
+                                                       type: 'abusefilter',
+                                                       details: {
+                                                               type: 'warning',
+                                                               message: warning
+                                                       }
+                                               } );
+                                       } else if ( 
/^abusefilter-disallow/.test( code ) ) {
+                                               // AbuseFilter disallow
+                                               result.reject( {
+                                                       type: 'abusefilter',
+                                                       details: {
+                                                               type: 
'disallow',
+                                                               message: warning
+                                                       }
+                                               } );
+                                       } else if ( /^abusefilter/.test( code ) 
) {
+                                               // AbuseFilter other
+                                               result.reject( {
+                                                       type: 'abusefilter',
+                                                       details: {
+                                                               type: 'other',
+                                                               message: warning
+                                                       }
+                                               } );
+                                       } else {
+                                               // other errors
+                                               result.reject( {
+                                                       type: 'error',
+                                                       details: code
+                                               } );
+                                       }
+                               } else {
+                                       result.reject( {
+                                               type: 'error',
+                                               details: 'unknown'
+                                       } );
+                               }
+                       }.bind( this ) ).fail( $.proxy( result, 'reject', {
+                               type: 'error',
+                               details: 'http'
+                       } ) );
+
                        return result;
                },
 
@@ -241,8 +233,7 @@
                 */
                getPreview: function ( options ) {
                        var result = $.Deferred(),
-                               sectionLine = '',
-                               self = this;
+                               sectionLine = '';
 
                        $.extend( options, {
                                action: 'parse',
@@ -260,7 +251,7 @@
                        this._pending = this.api.post( options ).done( function 
( resp ) {
                                if ( resp && resp.parse && resp.parse.text ) {
                                        // section 0 haven't a section name so 
skip
-                                       if ( self.sectionId !== 0 &&
+                                       if ( this.sectionId !== 0 &&
                                                resp.parse.sections !== 
undefined &&
                                                resp.parse.sections[0] !== 
undefined &&
                                                resp.parse.sections[0].line !== 
undefined
@@ -271,7 +262,7 @@
                                } else {
                                        result.reject();
                                }
-                       } ).fail( $.proxy( result, 'reject' ) );
+                       }.bind( this ) ).fail( $.proxy( result, 'reject' ) );
 
                        return result;
                }
diff --git a/resources/mobile.editor.common/EditorOverlayBase.js 
b/resources/mobile.editor.common/EditorOverlayBase.js
index c01d2b5..918a889 100644
--- a/resources/mobile.editor.common/EditorOverlayBase.js
+++ b/resources/mobile.editor.common/EditorOverlayBase.js
@@ -45,8 +45,6 @@
         * @param {Object} options Configuration options
         */
        function EditorOverlayBase( options ) {
-               var self = this;
-
                if ( options.isNewPage ) {
                        options.placeholder = mw.msg( 
'mobile-frontend-editor-placeholder-new-page', mw.user );
                }
@@ -66,8 +64,8 @@
                        // Returns true if content has changed
                        test: function () {
                                // Check if content has changed
-                               return self.hasChanged();
-                       },
+                               return this.hasChanged();
+                       }.bind( this ),
 
                        // Message to show the user, if content has changed
                        message: mw.msg( 
'mobile-frontend-editor-cancel-confirm' ),
@@ -175,8 +173,7 @@
                 */
                onSaveComplete: function () {
                        var msg,
-                               title = this.options.title,
-                               self = this;
+                               title = this.options.title;
 
                        // FIXME: use generic method for following 3 lines
                        this.pageGateway.invalidatePage( title );
@@ -194,8 +191,8 @@
                        this.log( {
                                action: 'saveSuccess'
                        } );
-                       if ( self.sectionLine ) {
-                               title = title + '#' + self.sectionLine;
+                       if ( this.sectionLine ) {
+                               title = title + '#' + this.sectionLine;
                        }
 
                        $( window ).off( 'beforeunload.mfeditorwarning' );
@@ -207,7 +204,7 @@
                        } );
 
                        window.location = mw.util.getUrl( title );
-                       if ( self.sectionLine ) {
+                       if ( this.sectionLine ) {
                                // since the path and only the hash has changed 
it has not triggered a refresh so forcefully refresh
                                window.location.reload();
                        }
@@ -308,14 +305,13 @@
                 * @inheritdoc
                 */
                hide: function () {
-                       var self = this;
                        if ( this.hasChanged() ) {
                                OO.ui.confirm( mw.msg( 
'mobile-frontend-editor-cancel-confirm' ) ).done( function ( confirmed ) {
                                        if ( confirmed ) {
-                                               self.allowCloseWindow.release();
-                                               Overlay.prototype.hide.call( 
self );
+                                               this.allowCloseWindow.release();
+                                               Overlay.prototype.hide.call( 
this );
                                        }
-                               } );
+                               }.bind( this ) );
                        } else {
                                this.allowCloseWindow.release();
                                Overlay.prototype.hide.call( this );
@@ -346,15 +342,14 @@
                 * @param {Object} details Details returned from the api.
                 */
                handleCaptcha: function ( details ) {
-                       var self = this,
-                               $input = this.$( '.captcha-word' );
+                       var $input = this.$( '.captcha-word' );
 
                        if ( this.captchaShown ) {
                                $input.val( '' );
                                $input.attr( 'placeholder', 
this.options.captchaTryAgainMsg );
                                setTimeout( function () {
-                                       $input.attr( 'placeholder', 
self.options.captchaMsg );
-                               }, 2000 );
+                                       $input.attr( 'placeholder', 
this.options.captchaMsg );
+                               }.bind( this ), 2000 );
                        }
 
                        // handle different mime types different
diff --git a/resources/mobile.editor.overlay/EditorOverlay.js 
b/resources/mobile.editor.overlay/EditorOverlay.js
index c13f96b..d9d6a0a 100644
--- a/resources/mobile.editor.overlay/EditorOverlay.js
+++ b/resources/mobile.editor.overlay/EditorOverlay.js
@@ -130,8 +130,6 @@
                },
                /** @inheritdoc **/
                postRender: function () {
-                       var self = this;
-
                        if ( this.isVisualEditorEnabled() ) {
                                mw.loader.using( 'ext.visualEditor.switching' 
).then( function () {
                                        var switchToolbar,
@@ -148,15 +146,15 @@
                                                if ( mode === 'visual' ) {
                                                        // If the user tries to 
switch to the VisualEditor, check if any changes have
                                                        // been made, and if 
so, tell the user they have to save first.
-                                                       if ( 
!self.gateway.hasChanged ) {
-                                                               
self._switchToVisualEditor( self.options );
+                                                       if ( 
!this.gateway.hasChanged ) {
+                                                               
this._switchToVisualEditor( this.options );
                                                        } else {
                                                                if ( 
window.confirm( mw.msg( 'mobile-frontend-editor-switch-confirm' ) ) ) {
-                                                                       
self.onStageChanges();
+                                                                       
this.onStageChanges();
                                                                }
                                                        }
                                                }
-                                       } );
+                                       }.bind( this ) );
 
                                        switchToolbar.setup( [
                                                {
@@ -167,16 +165,16 @@
                                                }
                                        ] );
 
-                                       self.$el.find( '.switcher-container' 
).html( switchToolbar.$element );
+                                       this.$el.find( '.switcher-container' 
).html( switchToolbar.$element );
                                        switchToolbar.emit( 'updateState' );
-                               } );
+                               }.bind( this ) );
                        }
 
                        EditorOverlayBase.prototype.postRender.apply( this );
 
                        this.$preview = this.$( '.preview' );
                        this.$content = this.$( '.wikitext-editor' );
-                       if ( self.options.isAnon ) {
+                       if ( this.options.isAnon ) {
                                this.$anonWarning = this.$( '.anonwarning' );
                                this.$content.hide();
                                // the user has to click login, signup or edit 
without login, disable "Next" button on top right
@@ -197,7 +195,7 @@
 
                        this.$content.on( 'input', this._resizeEditor.bind( 
this ) );
 
-                       if ( !self.options.isAnon ) {
+                       if ( !this.options.isAnon ) {
                                this._loadContent();
                        }
                },
@@ -250,10 +248,9 @@
                 * @inheritdoc
                 */
                onStageChanges: function () {
-                       var self = this,
-                               params = {
-                                       text: this.getContent()
-                               };
+                       var params = {
+                               text: this.getContent()
+                       };
 
                        this.scrollTop = $( 'body' ).scrollTop();
                        this.$content.hide();
@@ -264,19 +261,19 @@
                        }
                        this.gateway.getPreview( params ).done( function ( 
parsedText, parsedSectionLine ) {
                                // On desktop edit summaries strip tags. Mimic 
this behavior on mobile devices
-                               self.sectionLine = $( '<div/>' ).html( 
parsedSectionLine ).text();
+                               this.sectionLine = $( '<div/>' ).html( 
parsedSectionLine ).text();
                                new Section( {
-                                       el: self.$preview,
+                                       el: this.$preview,
                                        text: parsedText
                                } ).$( 'a' ).on( 'click', false );
                                // Emit event so we can perform enhancements to 
page
-                               M.emit( 'edit-preview', self );
-                       } ).fail( function () {
-                               self.$preview.addClass( 'error' ).text( mw.msg( 
'mobile-frontend-editor-error-preview' ) );
-                       } ).always( function () {
-                               self.clearSpinner();
-                               self.$preview.show();
-                       } );
+                               M.emit( 'edit-preview', this );
+                       }.bind( this ) ).fail( function () {
+                               this.$preview.addClass( 'error' ).text( mw.msg( 
'mobile-frontend-editor-error-preview' ) );
+                       }.bind( this ) ).always( function () {
+                               this.clearSpinner();
+                               this.$preview.show();
+                       }.bind( this ) );
 
                        EditorOverlayBase.prototype.onStageChanges.apply( this, 
arguments );
                },
@@ -342,8 +339,6 @@
                 * @private
                 */
                _loadContent: function () {
-                       var self = this;
-
                        this.$content.hide();
                        this.showSpinner();
 
@@ -351,7 +346,7 @@
                                .done( function ( content, userinfo ) {
                                        var parser, ast, parsedBlockReason;
 
-                                       self.setContent( content );
+                                       this.setContent( content );
                                        // check if user is blocked
                                        if ( userinfo && 
userinfo.hasOwnProperty( 'blockid' ) ) {
                                                // Workaround to parse a 
message parameter for mw.message, see T96885
@@ -366,13 +361,13 @@
                                                                
parsedBlockReason
                                                        ).parse()
                                                );
-                                               self.hide();
+                                               this.hide();
                                        }
-                                       self.clearSpinner();
-                               } )
+                                       this.clearSpinner();
+                               }.bind( this ) )
                                .fail( function () {
-                                       self.reportError( mw.msg( 
'mobile-frontend-editor-error-loading' ) );
-                               } );
+                                       this.reportError( mw.msg( 
'mobile-frontend-editor-error-loading' ) );
+                               }.bind( this ) );
                },
 
                /**
@@ -383,7 +378,6 @@
                 * @param {Object} options Object passed to the constructor
                 */
                _switchToVisualEditor: function ( options ) {
-                       var self = this;
                        this.log( {
                                action: 'abort',
                                type: 'switchnochange',
@@ -399,15 +393,15 @@
                                function () {
                                        var VisualEditorOverlay = M.require( 
'mobile.editor.ve/VisualEditorOverlay' );
 
-                                       self.clearSpinner();
-                                       self.overlayManager.replaceCurrent( new 
VisualEditorOverlay( options ) );
-                               },
+                                       this.clearSpinner();
+                                       this.overlayManager.replaceCurrent( new 
VisualEditorOverlay( options ) );
+                               }.bind( this ),
                                function () {
-                                       self.clearSpinner();
-                                       self.$content.show();
+                                       this.clearSpinner();
+                                       this.$content.show();
                                        // FIXME: We should show an error 
notification, but right now toast
                                        // notifications are not dismissible 
when shown within the editor.
-                               }
+                               }.bind( this )
                        );
                },
 
@@ -431,13 +425,12 @@
                 * @inheritdoc
                 */
                onSaveBegin: function () {
-                       var self = this,
-                               options = {
-                                       summary: this.$( '.summary' ).val()
-                               };
+                       var options = {
+                               summary: this.$( '.summary' ).val()
+                       };
 
-                       if ( self.sectionLine !== '' ) {
-                               options.summary = '/* ' + self.sectionLine + ' 
*/' + options.summary;
+                       if ( this.sectionLine !== '' ) {
+                               options.summary = '/* ' + this.sectionLine + ' 
*/' + options.summary;
                        }
                        EditorOverlayBase.prototype.onSaveBegin.apply( this, 
arguments );
                        if ( this.confirmAborted ) {
@@ -452,15 +445,15 @@
 
                        this.gateway.save( options )
                                .done( function () {
-                                       var title = self.options.title;
+                                       var title = this.options.title;
                                        // Special case behaviour of main page
                                        if ( mw.config.get( 'wgIsMainPage' ) ) {
                                                window.location = 
mw.util.getUrl( title );
                                                return;
                                        }
 
-                                       self.onSaveComplete();
-                               } )
+                                       this.onSaveComplete();
+                               }.bind( this ) )
                                .fail( function ( data, code, response ) {
                                        var msg,
                                                // When save failed with one of 
these error codes, the returned
@@ -482,11 +475,11 @@
                                                };
 
                                        if ( data.type === 'captcha' ) {
-                                               self.captchaId = 
data.details.id;
-                                               self.handleCaptcha( 
data.details );
+                                               this.captchaId = 
data.details.id;
+                                               this.handleCaptcha( 
data.details );
                                                key = 'captcha';
                                        } else if ( data.type === 'abusefilter' 
) {
-                                               self._showAbuseFilter( 
data.details.type, data.details.message );
+                                               this._showAbuseFilter( 
data.details.type, data.details.message );
                                        } else {
                                                if ( key === 'editconflict' ) {
                                                        msg = mw.msg( 
'mobile-frontend-editor-error-conflict' );
@@ -496,16 +489,16 @@
                                                        msg = mw.msg( 
'mobile-frontend-editor-error' );
                                                }
 
-                                               self.reportError( msg );
-                                               self.showHidden( '.save-header, 
.save-panel' );
+                                               this.reportError( msg );
+                                               this.showHidden( '.save-header, 
.save-panel' );
                                        }
 
-                                       self.log( {
+                                       this.log( {
                                                action: 'saveFailure',
                                                message: msg,
                                                type: typeMap[key] || 
'responseUnknown'
                                        } );
-                               } );
+                               }.bind( this ) );
                },
 
                /**
diff --git a/resources/mobile.editor.ve/VisualEditorOverlay.js 
b/resources/mobile.editor.ve/VisualEditorOverlay.js
index edc9661..fbbd3ac 100644
--- a/resources/mobile.editor.ve/VisualEditorOverlay.js
+++ b/resources/mobile.editor.ve/VisualEditorOverlay.js
@@ -113,7 +113,6 @@
                 * @method
                 */
                switchToSourceEditor: function () {
-                       var self = this;
                        this.log( {
                                action: 'abort',
                                type: 'switchnochange',
@@ -127,10 +126,10 @@
                        mw.loader.using( 'mobile.editor.overlay', function () {
                                var EditorOverlay = M.require( 
'mobile.editor.overlay/EditorOverlay' );
 
-                               self.clearSpinner();
-                               self.applyHeaderOptions( self.options, false );
-                               self.overlayManager.replaceCurrent( new 
EditorOverlay( self.options ) );
-                       } );
+                               this.clearSpinner();
+                               this.applyHeaderOptions( this.options, false );
+                               this.overlayManager.replaceCurrent( new 
EditorOverlay( this.options ) );
+                       }.bind( this ) );
                },
                /** @inheritdoc **/
                onSaveComplete: function () {
diff --git a/resources/mobile.fontchanger/FontChanger.js 
b/resources/mobile.fontchanger/FontChanger.js
index 4432110..90c5e62 100644
--- a/resources/mobile.fontchanger/FontChanger.js
+++ b/resources/mobile.fontchanger/FontChanger.js
@@ -51,27 +51,25 @@
                 * @inheritdoc
                 */
                postRender: function () {
-                       var self = this;
-
                        this.fontchanger = this.$( '.fontchanger-value' );
                        this.changePlus = this.$( '.fontchanger.plus' );
                        this.changeMinus = this.$( '.fontchanger.minus' );
                        this.setPercentage( settings.get( this.options.name, 
true ) || 100 );
 
                        this.fontchanger.on( 'click', function () {
-                               self.setPercentage( 100 );
+                               this.setPercentage( 100 );
                                return false;
-                       } );
+                       }.bind( this ) );
 
                        this.changeMinus.on( 'click', function () {
-                               self.setPercentage( self.fontchanger.val() - 10 
);
+                               this.setPercentage( this.fontchanger.val() - 10 
);
                                return false;
-                       } );
+                       }.bind( this ) );
 
                        this.changePlus.on( 'click', function () {
-                               self.setPercentage( parseInt( 
self.fontchanger.val() ) + 10 );
+                               this.setPercentage( parseInt( 
this.fontchanger.val() ) + 10 );
                                return false;
-                       } );
+                       }.bind( this ) );
                        $( 'form.mw-mf-settings' ).on( 'submit', $.proxy( this, 
'save' ) );
                },
 
diff --git a/resources/mobile.gallery/PhotoList.js 
b/resources/mobile.gallery/PhotoList.js
index 1e67282..8614fb8 100644
--- a/resources/mobile.gallery/PhotoList.js
+++ b/resources/mobile.gallery/PhotoList.js
@@ -117,26 +117,24 @@
                 * @private
                 */
                _loadPhotos: function () {
-                       var self = this;
-
                        this.gateway.getPhotos().done( function ( photos ) {
                                if ( photos.length ) {
                                        photos.forEach( function ( photo ) {
-                                               self.appendPhoto( photo );
-                                       } );
+                                               this.appendPhoto( photo );
+                                       }.bind( this ) );
                                        // try loading more when end is near 
only if we got photos last time
-                                       self.infiniteScroll.enable();
+                                       this.infiniteScroll.enable();
                                } else {
-                                       self.$end.remove();
-                                       if ( self.isEmpty() ) {
-                                               self.emit( 'empty' );
-                                               self.showEmptyMessage();
+                                       this.$end.remove();
+                                       if ( this.isEmpty() ) {
+                                               this.emit( 'empty' );
+                                               this.showEmptyMessage();
                                        }
                                }
-                       } ).fail( function () {
+                       }.bind( this ) ).fail( function () {
                                // try loading again if request failed
-                               self.infiniteScroll.enable();
-                       } );
+                               this.infiniteScroll.enable();
+                       }.bind( this ) );
                }
        } );
 
diff --git a/resources/mobile.gallery/PhotoListGateway.js 
b/resources/mobile.gallery/PhotoListGateway.js
index be101f2..a78a7c5 100644
--- a/resources/mobile.gallery/PhotoListGateway.js
+++ b/resources/mobile.gallery/PhotoListGateway.js
@@ -103,8 +103,7 @@
                 * @return {jQuery.Deferred} where parameter is a list of 
JavaScript objects describing an image.
                 */
                getPhotos: function () {
-                       var self = this,
-                               result = $.Deferred();
+                       var result = $.Deferred();
 
                        if ( this.canContinue === true ) {
                                this.api.ajax( this.getQuery() ).done( function 
( resp ) {
@@ -113,15 +112,15 @@
                                                // FIXME: [API] in an ideal 
world imageData would be a sorted array
                                                // but it is a map of {[id]: 
page}
                                                photos = Object.keys( 
resp.query.pages ).map( function ( id ) {
-                                                       return 
self._getImageDataFromPage.call( self, resp.query.pages[id] );
-                                               } ).sort( function ( a, b ) {
+                                                       return 
this._getImageDataFromPage.call( this, resp.query.pages[id] );
+                                               }.bind( this ) ).sort( function 
( a, b ) {
                                                        return a.timestamp < 
b.timestamp ? 1 : -1;
                                                } );
 
                                                if ( resp.hasOwnProperty( 
'continue' ) ) {
-                                                       self.continueParams = 
resp.continue;
+                                                       this.continueParams = 
resp.continue;
                                                } else {
-                                                       self.canContinue = 
false;
+                                                       this.canContinue = 
false;
                                                }
 
                                                // FIXME: Should reply with a 
list of PhotoItem or Photo classes.
@@ -129,7 +128,7 @@
                                        } else {
                                                result.resolve( [] );
                                        }
-                               } ).fail( $.proxy( result, 'reject' ) );
+                               }.bind( this ) ).fail( $.proxy( result, 
'reject' ) );
                        } else {
                                result.resolve( [] );
                        }
diff --git a/resources/mobile.infiniteScroll/InfiniteScroll.js 
b/resources/mobile.infiniteScroll/InfiniteScroll.js
index 954f63f..81fbff5 100644
--- a/resources/mobile.infiniteScroll/InfiniteScroll.js
+++ b/resources/mobile.infiniteScroll/InfiniteScroll.js
@@ -39,12 +39,11 @@
         *           this.infiniteScroll.disable();
         *         },
         *         _loadPhotos: function () {
-        *           var self = this;
         *           this.gateway.getPhotos().done( function ( photos ) {
         *             // load photos into the DOM ...
         *             // 3. and (re-)enable infinite scrolling
-        *             self.infiniteScroll.enable();
-        *           } );
+        *             this.infiniteScroll.enable();
+        *           }.bind( this ) );
         *         }
         *       } );
         *     </code>
diff --git a/resources/mobile.mainMenu/MainMenu.js 
b/resources/mobile.mainMenu/MainMenu.js
index 73737b8..5beb641 100644
--- a/resources/mobile.mainMenu/MainMenu.js
+++ b/resources/mobile.mainMenu/MainMenu.js
@@ -42,15 +42,14 @@
                 * @throws exception when you try to advertise more than one 
feature.
                 */
                advertiseNewFeature: function ( selector, msg ) {
-                       var d = $.Deferred(),
-                               self = this;
+                       var d = $.Deferred();
                        if ( this._hasNewFeature ) {
                                throw 'A new feature is already being 
advertised.';
                        } else {
                                this._hasNewFeature = true;
                        }
                        $( function () {
-                               var $activator = $( self.activator ).eq( 0 );
+                               var $activator = $( this.activator ).eq( 0 );
                                $activator.addClass( 'indicator-circle' );
                                mw.loader.using( 'mobile.pointerOverlay' 
).done( function () {
                                        $activator.one( 'click', function () {
@@ -58,17 +57,17 @@
                                                        PointerOverlay = 
require( 'mobile.pointerOverlay' );
 
                                                po = new PointerOverlay( {
-                                                       appendToElement: 
self.$el.parent(),
+                                                       appendToElement: 
this.$el.parent(),
                                                        alignment: 'left',
                                                        summary: msg,
-                                                       target: self.$( 
selector )
+                                                       target: this.$( 
selector )
                                                } );
                                                po.show();
                                                d.resolve( po );
                                                $activator.removeClass( 
'indicator-circle' );
-                                       } );
-                               } );
-                       } );
+                                       }.bind( this ) );
+                               }.bind( this ) );
+                       }.bind( this ) );
                        return d;
                },
 
@@ -104,21 +103,19 @@
                 * Registers events for opening and closing the main menu
                 */
                registerClickEvents: function () {
-                       var self = this;
-
                        // Listen to the main menu button clicks
                        $( this.activator )
                                .off( 'click' )
                                .on( 'click', function ( ev ) {
-                                       if ( self.isOpen() ) {
-                                               self.closeNavigationDrawers();
+                                       if ( this.isOpen() ) {
+                                               this.closeNavigationDrawers();
                                        } else {
-                                               self.openNavigationDrawer();
+                                               this.openNavigationDrawer();
                                        }
                                        ev.preventDefault();
                                        // Stop propagation, otherwise the Skin 
will close the open menus on page center click
                                        ev.stopPropagation();
-                               } );
+                               }.bind( this ) );
                },
 
                /**
diff --git a/resources/mobile.mediaViewer.beta/ImageOverlayBeta.js 
b/resources/mobile.mediaViewer.beta/ImageOverlayBeta.js
index b1ba8b6..8da21cc 100644
--- a/resources/mobile.mediaViewer.beta/ImageOverlayBeta.js
+++ b/resources/mobile.mediaViewer.beta/ImageOverlayBeta.js
@@ -14,16 +14,14 @@
        OO.mfExtend( ImageOverlayBeta, ImageOverlay, {
                /** @inheritdoc */
                _enableArrowImages: function ( thumbs ) {
-                       var self = this;
-
                        this.swipe = new Swipe();
                        this.swipe
                                .on( 'swipe-right', function () {
-                                       self.setNewImage( $( 
'.slider-button.prev' ).data( 'thumbnail' ) );
-                               } )
+                                       this.setNewImage( $( 
'.slider-button.prev' ).data( 'thumbnail' ) );
+                               }.bind( this ) )
                                .on( 'swipe-left', function () {
-                                       self.setNewImage( $( 
'.slider-button.next' ).data( 'thumbnail' ) );
-                               } )
+                                       this.setNewImage( $( 
'.slider-button.next' ).data( 'thumbnail' ) );
+                               }.bind( this ) )
                                .setElement( this.$el );
 
                        ImageOverlay.prototype._enableArrowImages.call( this, 
thumbs );
diff --git a/resources/mobile.mediaViewer/ImageOverlay.js 
b/resources/mobile.mediaViewer/ImageOverlay.js
index 286fdc7..b744eba 100644
--- a/resources/mobile.mediaViewer/ImageOverlay.js
+++ b/resources/mobile.mediaViewer/ImageOverlay.js
@@ -83,13 +83,12 @@
                },
                /** @inheritdoc */
                preRender: function () {
-                       var self = this;
                        this.options.thumbnails.forEach( function ( thumbnail, 
i ) {
-                               if ( thumbnail.getFileName() === 
self.options.title ) {
-                                       self.options.caption = 
thumbnail.getDescription();
-                                       self.galleryOffset = i;
+                               if ( thumbnail.getFileName() === 
this.options.title ) {
+                                       this.options.caption = 
thumbnail.getDescription();
+                                       this.galleryOffset = i;
                                }
-                       } );
+                       }.bind( this ) );
                },
 
                /**
@@ -120,8 +119,7 @@
                /** @inheritdoc */
                postRender: function () {
                        var $img,
-                               thumbs = this.options.thumbnails || [],
-                               self = this;
+                               thumbs = this.options.thumbnails || [];
 
                        if ( thumbs.length < 2 ) {
                                this._disableArrowImages();
@@ -133,7 +131,7 @@
 
                        Overlay.prototype.postRender.apply( this );
 
-                       this.gateway.getThumb( self.options.title ).done( 
function ( data ) {
+                       this.gateway.getThumb( this.options.title ).done( 
function ( data ) {
                                var author, url = data.descriptionurl + 
'#mw-jump-to-license';
 
                                /**
@@ -142,29 +140,29 @@
                                 * @ignore
                                 */
                                function removeLoader() {
-                                       self.$( '.spinner' ).hide();
+                                       this.$( '.spinner' ).hide();
                                }
 
-                               self.thumbWidth = data.thumbwidth;
-                               self.thumbHeight = data.thumbheight;
-                               self.imgRatio = data.thumbwidth / 
data.thumbheight;
-                               $img = $( '<img>' ).attr( 'src', data.thumburl 
).attr( 'alt', self.options.caption );
-                               self.$( '.image' ).append( $img );
+                               this.thumbWidth = data.thumbwidth;
+                               this.thumbHeight = data.thumbheight;
+                               this.imgRatio = data.thumbwidth / 
data.thumbheight;
+                               $img = $( '<img>' ).attr( 'src', data.thumburl 
).attr( 'alt', this.options.caption );
+                               this.$( '.image' ).append( $img );
 
                                if ( $img.prop( 'complete' ) ) {
                                        // if the image is loaded from browser 
cache, "load" event may not fire
                                        // 
(http://stackoverflow.com/questions/910727/jquery-event-for-images-loaded#comment10616132_1110094)
-                                       removeLoader();
+                                       removeLoader.bind( this )();
                                } else {
                                        // remove the loader when the image is 
loaded
-                                       $img.on( 'load', removeLoader );
+                                       $img.on( 'load', removeLoader.bind( 
this ) );
                                }
-                               self._positionImage();
-                               self.$( '.details a' ).attr( 'href', url );
+                               this._positionImage();
+                               this.$( '.details a' ).attr( 'href', url );
                                if ( data.extmetadata ) {
                                        // Add license information
                                        if ( data.extmetadata.LicenseShortName 
) {
-                                               self.$( '.license a' )
+                                               this.$( '.license a' )
                                                        .text( 
data.extmetadata.LicenseShortName.value )
                                                        .attr( 'href', url );
                                        }
@@ -172,11 +170,11 @@
                                        if ( data.extmetadata.Artist ) {
                                                // Strip any tags
                                                author = 
data.extmetadata.Artist.value.replace( /<.*?>/g, '' );
-                                               self.$( '.license' ).prepend( 
author + ' &bull; ' );
+                                               this.$( '.license' ).prepend( 
author + ' &bull; ' );
                                        }
                                }
-                               self.adjustDetails();
-                       } );
+                               this.adjustDetails();
+                       }.bind( this ) );
 
                        M.on( 'resize:throttled', $.proxy( this, 
'_positionImage' ) );
                },
diff --git a/resources/mobile.nearby/Nearby.js 
b/resources/mobile.nearby/Nearby.js
index a3f39a2..3ff115e 100644
--- a/resources/mobile.nearby/Nearby.js
+++ b/resources/mobile.nearby/Nearby.js
@@ -15,8 +15,7 @@
         * @param {Object} options Configuration options
         */
        function Nearby( options ) {
-               var self = this,
-                       _super = WatchstarPageList;
+               var _super = WatchstarPageList;
 
                this.range = options.range || mw.config.get( 'wgMFNearbyRange' 
) || 1000;
                this.source = options.source || 'nearby';
@@ -25,7 +24,7 @@
                } );
 
                if ( options.errorType ) {
-                       options.errorOptions = self._errorOptions( 
options.errorType );
+                       options.errorOptions = this._errorOptions( 
options.errorType );
                }
                _super.apply( this, arguments );
 
@@ -119,8 +118,7 @@
                 * @private
                 */
                _find: function ( options ) {
-                       var result = $.Deferred(),
-                               self = this;
+                       var result = $.Deferred();
 
                        /**
                         * Handler for successful query
@@ -130,9 +128,9 @@
                        function pagesSuccess( pages ) {
                                options.pages = pages;
                                if ( pages && pages.length === 0 ) {
-                                       options.errorOptions = 
self._errorOptions( 'empty' );
+                                       options.errorOptions = 
this._errorOptions( 'empty' );
                                }
-                               self._isLoading = false;
+                               this._isLoading = false;
                                result.resolve( options );
                        }
 
@@ -144,8 +142,8 @@
                         * @ignore
                         */
                        function pagesError( code, details ) {
-                               self._isLoading = false;
-                               options.errorOptions = self._errorOptions( 
code, details );
+                               this._isLoading = false;
+                               options.errorOptions = this._errorOptions( 
code, details );
                                result.resolve( options );
                        }
 
@@ -156,12 +154,12 @@
                                },
                                                this.range, options.exclude
                                        )
-                                       .done( pagesSuccess )
-                                       .fail( pagesError );
+                                       .done( pagesSuccess.bind( this ) )
+                                       .fail( pagesError.bind( this ) );
                        } else if ( options.pageTitle ) {
                                this.nearbyApi.getPagesAroundPage( 
options.pageTitle, this.range )
-                                       .done( pagesSuccess )
-                                       .fail( pagesError );
+                                       .done( pagesSuccess.bind( this ) )
+                                       .fail( pagesError.bind( this ) );
                        } else {
                                if ( options.errorType ) {
                                        options.errorOptions = 
this._errorOptions( options.errorType );
@@ -242,8 +240,7 @@
                 * @param {Object} options Configuration options
                 */
                refresh: function ( options ) {
-                       var self = this,
-                               _super = WatchstarPageList;
+                       var _super = WatchstarPageList;
 
                        this.$( '.spinner' ).removeClass( 'hidden' );
                        this.$( '.page-list' ).addClass( 'hidden' );
@@ -256,26 +253,26 @@
                                // Get some new pages
                                this.getCurrentPosition().done( function ( 
coordOptions ) {
                                        $.extend( options, coordOptions );
-                                       self._find( options ).done( function ( 
options ) {
-                                               _super.call( self, options );
-                                       } );
-                               } ).fail( function ( errorType ) {
-                                       options.errorOptions = 
self._errorOptions( errorType );
-                                       self._isLoading = false;
-                                       _super.call( self, options );
-                               } );
+                                       this._find( options ).done( function ( 
options ) {
+                                               _super.call( this, options );
+                                       }.bind( this ) );
+                               }.bind( this ) ).fail( function ( errorType ) {
+                                       options.errorOptions = 
this._errorOptions( errorType );
+                                       this._isLoading = false;
+                                       _super.call( this, options );
+                               }.bind( this ) );
                        } else if ( ( options.latitude && options.longitude ) 
|| options.pageTitle ) {
                                // Flush any existing list of pages
                                options.pages = [];
 
                                // Get some new pages
                                this._find( options ).done( function ( options 
) {
-                                       _super.call( self, options );
-                               } ).fail( function ( errorType ) {
-                                       options.errorOptions = 
self._errorOptions( errorType );
-                                       self._isLoading = false;
-                                       _super.call( self, options );
-                               } );
+                                       _super.call( this, options );
+                               }.bind( this ) ).fail( function ( errorType ) {
+                                       options.errorOptions = 
this._errorOptions( errorType );
+                                       this._isLoading = false;
+                                       _super.call( this, options );
+                               }.bind( this ) );
                        }
 
                        // Run it once for loader etc
diff --git a/resources/mobile.nearby/NearbyGateway.js 
b/resources/mobile.nearby/NearbyGateway.js
index 39638a5..beba466 100644
--- a/resources/mobile.nearby/NearbyGateway.js
+++ b/resources/mobile.nearby/NearbyGateway.js
@@ -83,8 +83,7 @@
                 */
                _search: function ( params, range, exclude ) {
                        var requestParams,
-                               d = $.Deferred(),
-                               self = this;
+                               d = $.Deferred();
 
                        requestParams = extendSearchParams( 'nearby', {
                                colimit: 'max',
@@ -124,7 +123,7 @@
                                                p.dist = coords.dist / 1000;
                                                p.latitude = coords.lat;
                                                p.longitude = coords.lon;
-                                               p.proximity = 
self._distanceMessage( p.dist );
+                                               p.proximity = 
this._distanceMessage( p.dist );
                                        } else {
                                                p.dist = 0;
                                        }
@@ -133,13 +132,13 @@
                                        } else {
                                                return null;
                                        }
-                               } ).filter( function ( page ) { return !!page; 
} );
+                               }.bind( this ) ).filter( function ( page ) { 
return !!page; } );
 
                                pages.sort( function ( a, b ) {
                                        return a.dist > b.dist ? 1 : -1;
                                } );
                                d.resolve( pages );
-                       }, function ( error, details ) {
+                       }.bind( this ), function ( error, details ) {
                                if ( details && details.error && 
details.error.info ) {
                                        d.reject( error, details.error.info );
                                } else {
diff --git a/resources/mobile.pagelist.scripts/WatchstarPageList.js 
b/resources/mobile.pagelist.scripts/WatchstarPageList.js
index ab41157..b51d234 100644
--- a/resources/mobile.pagelist.scripts/WatchstarPageList.js
+++ b/resources/mobile.pagelist.scripts/WatchstarPageList.js
@@ -43,7 +43,6 @@
                 */
                postRender: function () {
                        var $li,
-                               self = this,
                                pages = [],
                                gateway = this.wsGateway;
 
@@ -60,39 +59,39 @@
                        // Create watch stars for each entry in list
                        if ( !user.isAnon() && pages.length > 0 ) {
                                // FIXME: This should be moved out of here so 
other extensions can override this behaviour.
-                               self.getPages( pages ).done( function () {
-                                       $li.each( function () {
+                               this.getPages( pages ).done( function () {
+                                       $li.each( function (i, li) {
                                                var watchstar,
                                                        page = new Page( {
                                                                // FIXME: Set 
sections so we don't hit the api (hacky)
                                                                sections: [],
-                                                               title: $( this 
).attr( 'title' ),
-                                                               id: $( this 
).data( 'id' )
+                                                               title: $( li 
).attr( 'title' ),
+                                                               id: $( li 
).data( 'id' )
                                                        } );
 
                                                watchstar = new Watchstar( {
-                                                       api: self.options.api,
-                                                       funnel: 
self.options.funnel,
+                                                       api: this.options.api,
+                                                       funnel: 
this.options.funnel,
                                                        isAnon: false,
                                                        isWatched: 
gateway.isWatchedPage( page ),
                                                        page: page,
-                                                       el: $( '<div>' 
).appendTo( this )
+                                                       el: $( '<div>' 
).appendTo( li )
                                                } );
 
-                                               $( this ).addClass( 
'with-watchstar' );
+                                               $( li ).addClass( 
'with-watchstar' );
 
                                                /**
                                                 * @event watch
                                                 * Fired when an article in the 
PageList is watched.
                                                 */
-                                               watchstar.on( 'watch', $.proxy( 
self, 'emit', 'watch' ) );
+                                               watchstar.on( 'watch', $.proxy( 
this, 'emit', 'watch' ) );
                                                /**
                                                 * @event unwatch
                                                 * Fired when an article in the 
PageList is watched.
                                                 */
-                                               watchstar.on( 'unwatch', 
$.proxy( self, 'emit', 'unwatch' ) );
-                                       } );
-                               } );
+                                               watchstar.on( 'unwatch', 
$.proxy( this, 'emit', 'unwatch' ) );
+                                       }.bind( this ) );
+                               }.bind( this ) );
                        }
                }
        } );
diff --git a/resources/mobile.pointerOverlay/PointerOverlay.js 
b/resources/mobile.pointerOverlay/PointerOverlay.js
index e602ff5..9c0bcf5 100644
--- a/resources/mobile.pointerOverlay/PointerOverlay.js
+++ b/resources/mobile.pointerOverlay/PointerOverlay.js
@@ -58,8 +58,7 @@
                },
                /** @inheritdoc */
                postRender: function () {
-                       var $target,
-                               self = this;
+                       var $target;
 
                        Overlay.prototype.postRender.apply( this );
 
@@ -71,13 +70,13 @@
                        }
                        if ( this.options.timeout ) {
                                setTimeout( function () {
-                                       self.hide();
-                               }, this.options.timeout );
+                                       this.hide();
+                               }.bind( this ), this.options.timeout );
                        }
-                       if ( self.options.target ) {
-                               $target = $( self.options.target );
+                       if ( this.options.target ) {
+                               $target = $( this.options.target );
                                // Ensure we position the overlay correctly but 
do not show the arrow
-                               self._position( $target );
+                               this._position( $target );
                                this.addPointerArrow( $target );
                        }
                },
diff --git a/resources/mobile.references.gateway/ReferencesMobileViewGateway.js 
b/resources/mobile.references.gateway/ReferencesMobileViewGateway.js
index a50c09e..8f63bfb 100644
--- a/resources/mobile.references.gateway/ReferencesMobileViewGateway.js
+++ b/resources/mobile.references.gateway/ReferencesMobileViewGateway.js
@@ -41,8 +41,7 @@
                 *  sections on the page
                 */
                getReferencesLists: function ( page ) {
-                       var self = this,
-                               cachedReferencesSections = this.cache.get( 
page.id );
+                       var cachedReferencesSections = this.cache.get( page.id 
);
 
                        if ( cachedReferencesSections ) {
                                return $.Deferred().resolve( 
cachedReferencesSections ).promise();
@@ -63,10 +62,10 @@
                                        sections[ $section.find( '.mw-headline' 
).attr( 'id' ) ] = $section.find( '.references' );
                                } );
 
-                               self.cache.set( page.id, sections );
+                               this.cache.set( page.id, sections );
 
                                return sections;
-                       } );
+                       }.bind( this ) );
                },
                /**
                 * Retrieve all the references lists for a given page and 
section ID.
@@ -86,8 +85,6 @@
                 * @inheritdoc
                 */
                getReference: function ( id, page ) {
-                       var self = this;
-
                        return this.getReferencesLists( page ).then( function ( 
sections ) {
                                var $container = $( '<div>' );
 
@@ -95,8 +92,8 @@
                                        $container.append( sections[ sectionId 
] );
                                } );
 
-                               return self.getReferenceFromContainer( id, 
$container );
-                       } );
+                               return this.getReferenceFromContainer( id, 
$container );
+                       }.bind( this ) );
                }
        } );
 
diff --git a/resources/mobile.search.api/SearchGateway.js 
b/resources/mobile.search.api/SearchGateway.js
index 3405269..5f60367 100644
--- a/resources/mobile.search.api/SearchGateway.js
+++ b/resources/mobile.search.api/SearchGateway.js
@@ -103,15 +103,14 @@
                 * @private
                 */
                _processData: function ( query, data ) {
-                       var self = this,
-                               results = [];
+                       var results = [];
 
                        if ( data.query ) {
 
                                results = data.query.pages || {};
                                results = Object.keys( results ).map( function 
( id ) {
-                                       return self._getPage( query, results[ 
id ] );
-                               } );
+                                       return this._getPage( query, results[ 
id ] );
+                               }.bind( this ) );
                                // sort in order of index
                                results.sort( function ( a, b ) {
                                        return a.index < b.index ? -1 : 1;
@@ -129,8 +128,7 @@
                 */
                search: function ( query ) {
                        var result = $.Deferred(),
-                               request,
-                               self = this;
+                               request;
 
                        if ( !this.isCached( query ) ) {
                                request = this.api.get( this.getApiData( query 
) )
@@ -138,15 +136,15 @@
                                                // resolve the Deferred object
                                                result.resolve( {
                                                        query: query,
-                                                       results: 
self._processData( query, data )
+                                                       results: 
this._processData( query, data )
                                                } );
-                                       } )
+                                       }.bind( this ) )
                                        .fail( function () {
                                                // reset cached result, it 
maybe contains no value
-                                               self.searchCache[query] = 
undefined;
+                                               this.searchCache[query] = 
undefined;
                                                // reject
                                                result.reject();
-                                       } );
+                                       }.bind( this ) );
 
                                // cache the result to prevent the execution of 
one search query twice in one session
                                this.searchCache[query] = result.promise( {
diff --git a/resources/mobile.search/SearchOverlay.js 
b/resources/mobile.search/SearchOverlay.js
index 1d9d711..28c5df7 100644
--- a/resources/mobile.search/SearchOverlay.js
+++ b/resources/mobile.search/SearchOverlay.js
@@ -21,7 +21,6 @@
         * @param {Object} options Configuration options
         */
        function SearchOverlay( options ) {
-               var self = this;
                Overlay.call( this, options );
                this.api = options.api;
                // eslint-disable-next-line new-cap
@@ -31,8 +30,8 @@
                // FIXME: Remove when search registers route with overlay 
manager
                // we need this because of the focus/delay hack in search.js
                this.router.once( 'route', function () {
-                       self._hideOnRoute();
-               } );
+                       this._hideOnRoute();
+               }.bind( this ) );
        }
 
        OO.mfExtend( SearchOverlay, Overlay, {
@@ -113,13 +112,12 @@
                 * FIXME: Remove when search registers route with overlay 
manager
                 */
                _hideOnRoute: function () {
-                       var self = this;
                        this.router.once( 'route', function ( ev ) {
-                               if ( !self.hide() ) {
+                               if ( !this.hide() ) {
                                        ev.preventDefault();
-                                       self._hideOnRoute();
+                                       this._hideOnRoute();
                                }
-                       } );
+                       }.bind( this ) );
                },
 
                /**
@@ -241,8 +239,7 @@
 
                /** @inheritdoc */
                postRender: function () {
-                       var self = this,
-                               timer;
+                       var timer;
 
                        Overlay.prototype.postRender.call( this );
 
@@ -257,7 +254,7 @@
                         * @ignore
                         */
                        function clearSearch() {
-                               self.$spinner.hide();
+                               this.$spinner.hide();
                                clearTimeout( timer );
                        }
 
@@ -266,28 +263,28 @@
                                this.$spinner = this.$( '.spinner-container' );
                                M.on( 'search-start', function ( searchData ) {
                                        if ( timer ) {
-                                               clearSearch();
+                                               clearSearch.bind( this )();
                                        }
                                        timer = setTimeout( function () {
-                                               self.$spinner.show();
-                                       }, 2000 - searchData.delay );
-                               } );
-                               M.on( 'search-results', clearSearch );
+                                               this.$spinner.show();
+                                       }.bind( this ), 2000 - searchData.delay 
);
+                               }.bind( this ) );
+                               M.on( 'search-results', clearSearch.bind( this 
) );
                        } else {
                                // Show a spinner in place search results
                                this.$spinner = this.$( '.spinner' );
                                M.on( 'search-start', function () {
-                                       self.resetSearch();
-                                       self.$spinner.show();
-                               } );
+                                       this.resetSearch();
+                                       this.$spinner.show();
+                               }.bind( this ) );
                                M.on( 'search-results', function () {
-                                       self.$searchFeedback.show();
-                                       self.$spinner.hide();
-                               } );
+                                       this.$searchFeedback.show();
+                                       this.$spinner.hide();
+                               }.bind( this ) );
                        }
 
                        // Hide the clear button if the search input is empty
-                       if ( self.$input.val() === '' ) {
+                       if ( this.$input.val() === '' ) {
                                this.$clear.hide();
                        }
                },
@@ -313,15 +310,13 @@
                 * @inheritdoc
                 */
                hide: function () {
-                       var self = this;
-
                        if ( $html.hasClass( 'animations' ) ) {
-                               self.$el.addClass( 'fade-out' );
+                               this.$el.addClass( 'fade-out' );
                                setTimeout( function () {
-                                       Overlay.prototype.hide.apply( self, 
arguments );
-                               }, 500 );
+                                       Overlay.prototype.hide.apply( this, 
arguments );
+                               }.bind( this ), 500 );
                        } else {
-                               Overlay.prototype.hide.apply( self, arguments );
+                               Overlay.prototype.hide.apply( this, arguments );
                        }
                        return true;
                },
@@ -334,7 +329,6 @@
                 */
                performSearch: function () {
                        var
-                               self = this,
                                api = this.api,
                                query = this.$input.val(),
                                delay = this.gateway.isCached( query ) ? 0 : 
SEARCH_DELAY;
@@ -342,8 +336,8 @@
                        // it seems the input event can be fired when virtual 
keyboard is closed
                        // (Chrome for Android)
                        if ( query !== this.lastQuery ) {
-                               if ( self._pendingQuery ) {
-                                       self._pendingQuery.abort();
+                               if ( this._pendingQuery ) {
+                                       this._pendingQuery.abort();
                                }
                                clearTimeout( this.timer );
 
@@ -359,12 +353,12 @@
                                                        delay: delay
                                                } );
 
-                                               self._pendingQuery = 
self.gateway.search( query ).done( function ( data ) {
+                                               this._pendingQuery = 
this.gateway.search( query ).done( function ( data ) {
                                                        // check if we're 
getting the rights response in case of out of
                                                        // order responses 
(need to get the current value of the input)
-                                                       if ( data.query === 
self.$input.val() ) {
-                                                               
self.$el.toggleClass( 'no-results', data.results.length === 0 );
-                                                               
self.$searchContent
+                                                       if ( data.query === 
this.$input.val() ) {
+                                                               
this.$el.toggleClass( 'no-results', data.results.length === 0 );
+                                                               
this.$searchContent
                                                                        .show()
                                                                        .find( 
'p' )
                                                                        .hide()
@@ -376,10 +370,10 @@
                                                                        api: 
api,
                                                                        funnel: 
'search',
                                                                        pages: 
data.results,
-                                                                       el: 
self.$resultContainer
+                                                                       el: 
this.$resultContainer
                                                                } );
 
-                                                               self.$results = 
self.$resultContainer.find( 'li' );
+                                                               this.$results = 
this.$resultContainer.find( 'li' );
 
                                                                /**
                                                                 * @event 
search-results Fired when search API returns results
@@ -391,10 +385,10 @@
                                                                        
results: data.results
                                                                } );
                                                        }
-                                               } );
-                                       }, delay );
+                                               }.bind( this ) );
+                                       }.bind( this ), delay );
                                } else {
-                                       self.resetSearch();
+                                       this.resetSearch();
                                }
 
                                this.lastQuery = query;
diff --git a/resources/mobile.startup/Drawer.js 
b/resources/mobile.startup/Drawer.js
index 295abc0..46c41a9 100644
--- a/resources/mobile.startup/Drawer.js
+++ b/resources/mobile.startup/Drawer.js
@@ -43,12 +43,11 @@
 
                /** @inheritdoc */
                postRender: function () {
-                       var self = this;
                        // This module might be loaded at the top of the page 
e.g. Special:Uploads
                        // Thus ensure we wait for the DOM to be loaded
                        $( function () {
-                               self.appendTo( self.appendToElement );
-                       } );
+                               this.appendTo( this.appendToElement );
+                       }.bind( this ) );
                        this.on( 'show', $.proxy( this, 'onShowDrawer' ) );
                        this.on( 'hide', $.proxy( this, 'onHideDrawer' ) );
                },
@@ -66,14 +65,13 @@
                 * ShowDrawer event handler
                 */
                onShowDrawer: function () {
-                       var self = this,
-                               $window = $( window );
+                       var $window = $( window );
                        setTimeout( function () {
-                               $window.one( 'click.drawer', $.proxy( self, 
'hide' ) );
-                               if ( self.closeOnScroll ) {
-                                       $window.one( 'scroll.drawer', $.proxy( 
self, 'hide' ) );
+                               $window.one( 'click.drawer', $.proxy( this, 
'hide' ) );
+                               if ( this.closeOnScroll ) {
+                                       $window.one( 'scroll.drawer', $.proxy( 
this, 'hide' ) );
                                }
-                       }, self.minHideDelay );
+                       }.bind( this ), this.minHideDelay );
                },
 
                /**
diff --git a/resources/mobile.startup/Overlay.js 
b/resources/mobile.startup/Overlay.js
index b12bdc5..e8cb681 100644
--- a/resources/mobile.startup/Overlay.js
+++ b/resources/mobile.startup/Overlay.js
@@ -128,14 +128,13 @@
                 * @method
                 */
                setupEmulatedIosOverlayScrolling: function () {
-                       var self = this;
                        if ( this.isIos && this.hasFixedHeader ) {
                                this.$( '.overlay-content' ).on( 'touchstart', 
$.proxy( this, 'onTouchStart' ) )
                                        .on( 'touchmove', $.proxy( this, 
'onTouchMove' ) );
                                // wait for things to render before doing any 
calculations
                                setTimeout( function () {
-                                       self._fixIosHeader( 'textarea, input' );
-                               }, 0 );
+                                       this._fixIosHeader( 'textarea, input' );
+                               }.bind( this ), 0 );
                        }
                },
                /**
@@ -186,7 +185,6 @@
                 * @method
                 */
                show: function () {
-                       var self = this;
                        this.$el.appendTo( this.appendToElement );
                        this.scrollTop = $( document ).scrollTop();
 
@@ -207,8 +205,8 @@
                                                ev.preventDefault();
                                        } )
                                        .on( 'resize.ios', function () {
-                                               self._resizeContent( 
$window.height() );
-                                       } );
+                                               this._resizeContent( 
$window.height() );
+                                       }.bind( this ) );
                        }
 
                        this.$el.addClass( 'visible' );
@@ -273,8 +271,6 @@
                 * keyboard (usually inputs, textareas, contenteditables).
                 */
                _fixIosHeader: function ( el ) {
-                       var self = this;
-
                        if ( this.isIos ) {
                                this._resizeContent( $( window ).height() );
                                $( el )
@@ -283,7 +279,7 @@
                                                        var keyboardHeight = 0;
 
                                                        // detect virtual 
keyboard height
-                                                       if ( 
self.useVirtualKeyboardHack ) {
+                                                       if ( 
this.useVirtualKeyboardHack ) {
                                                                // this method 
does not work in iOS 8.02
                                                                
$window.scrollTop( 999 );
                                                                keyboardHeight 
= $window.scrollTop();
@@ -291,15 +287,15 @@
                                                        }
 
                                                        if ( $window.height() > 
keyboardHeight ) {
-                                                               
self._resizeContent( $window.height() - keyboardHeight );
+                                                               
this._resizeContent( $window.height() - keyboardHeight );
                                                        }
-                                               }, 0 );
-                                       } )
+                                               }.bind( this ), 0 );
+                                       }.bind( this ) )
                                        .on( 'blur', function () {
-                                               self._resizeContent( 
$window.height() );
+                                               this._resizeContent( 
$window.height() );
                                                // restore the fixed header in 
view.
                                                $window.scrollTop( 0 );
-                                       } );
+                                       }.bind( this ) );
                        }
                },
 
diff --git a/resources/mobile.startup/OverlayManager.js 
b/resources/mobile.startup/OverlayManager.js
index 893c79b..0629dbc 100644
--- a/resources/mobile.startup/OverlayManager.js
+++ b/resources/mobile.startup/OverlayManager.js
@@ -78,8 +78,7 @@
                 * @param {Object|null} match Object with factory function's 
result. null if no match.
                 */
                _processMatch: function ( match ) {
-                       var factoryResult,
-                               self = this;
+                       var factoryResult;
 
                        /**
                         * Attach an event to the overlays hide event
@@ -95,7 +94,7 @@
                        if ( match ) {
                                if ( match.overlay ) {
                                        // if the match is an overlay that was 
previously opened, reuse it
-                                       self._showOverlay( match.overlay );
+                                       this._showOverlay( match.overlay );
                                } else {
                                        // else create an overlay using the 
factory function result (either
                                        // a promise or an overlay)
@@ -105,12 +104,12 @@
                                                factoryResult.done( function ( 
overlay ) {
                                                        match.overlay = overlay;
                                                        attachHideEvent( 
overlay );
-                                                       self._showOverlay( 
overlay );
-                                               } );
+                                                       this._showOverlay( 
overlay );
+                                               }.bind( this ) );
                                        } else {
                                                match.overlay = factoryResult;
                                                attachHideEvent( match.overlay 
);
-                                               self._showOverlay( 
factoryResult );
+                                               this._showOverlay( 
factoryResult );
                                        }
                                }
                        }
@@ -207,18 +206,17 @@
                 * which resolves to an overlay.
                 */
                add: function ( route, factory ) {
-                       var self = this,
-                               entry = {
-                                       route: route,
-                                       factory: factory
-                               };
+                       var entry = {
+                               route: route,
+                               factory: factory
+                       };
 
                        this.entries[route] = entry;
                        // Check if overlay should be shown for the current 
path.
                        // The DOM must fully load before we can show the 
overlay because Overlay relies on it.
                        $( function () {
-                               self._processMatch( self._matchRoute( 
self.router.getPath(), entry ) );
-                       } );
+                               this._processMatch( this._matchRoute( 
this.router.getPath(), entry ) );
+                       }.bind( this ) );
                },
 
                /**
diff --git a/resources/mobile.startup/Page.js b/resources/mobile.startup/Page.js
index 9a9d8bf..f489c2a 100644
--- a/resources/mobile.startup/Page.js
+++ b/resources/mobile.startup/Page.js
@@ -229,13 +229,13 @@
                 * @inheritdoc
                 */
                postRender: function () {
-                       var self = this;
+                       var hash = this.options.hash;
                        // Restore anchor position after everything on page has 
been loaded.
                        // Otherwise, images that load after a while will push 
the anchor
                        // from the top of the viewport.
                        if ( this.options.hash ) {
                                $( window ).on( 'load', function () {
-                                       window.location.hash = 
self.options.hash;
+                                       window.location.hash = hash;
                                } );
                        }
                },
diff --git a/resources/mobile.startup/PageGateway.js 
b/resources/mobile.startup/PageGateway.js
index 5df091e..c131276 100644
--- a/resources/mobile.startup/PageGateway.js
+++ b/resources/mobile.startup/PageGateway.js
@@ -244,8 +244,7 @@
                 * and variant links as defined @ 
https://en.m.wikipedia.org/w/api.php?action=help&modules=query%2Blanglinks
                 */
                getPageLanguages: function ( title, language ) {
-                       var self = this,
-                               result = $.Deferred(),
+                       var result = $.Deferred(),
                                args = {
                                        action: 'query',
                                        meta: 'siteinfo',
@@ -265,9 +264,9 @@
                        this.api.get( args ).done( function ( resp ) {
                                result.resolve( {
                                        languages: 
resp.query.pages[0].langlinks || [],
-                                       variants: 
self._getLanguageVariantsFromApiResponse( title, resp )
+                                       variants: 
this._getLanguageVariantsFromApiResponse( title, resp )
                                } );
-                       } ).fail( $.proxy( result, 'reject' ) );
+                       }.bind( this ) ).fail( $.proxy( result, 'reject' ) );
 
                        return result;
                },
diff --git a/resources/mobile.startup/PageList.js 
b/resources/mobile.startup/PageList.js
index 5ed519e..80d588e 100644
--- a/resources/mobile.startup/PageList.js
+++ b/resources/mobile.startup/PageList.js
@@ -44,8 +44,7 @@
                 * @method
                 */
                renderPageImages: function () {
-                       var delay,
-                               self = this;
+                       var delay;
 
                        if ( !this.options.imagesDisabled ) {
                                // Delay an unnecessary load of images on 
mobile (slower?) connections
@@ -53,11 +52,11 @@
                                delay = browser.isWideScreen() ? 0 : 1000;
 
                                window.setTimeout( function () {
-                                       self.$( '.list-thumb' ).each( function 
() {
+                                       this.$( '.list-thumb' ).each( function 
() {
                                                var style = $( this ).data( 
'style' );
                                                $( this ).attr( 'style', style 
);
                                        } );
-                               }, delay );
+                               }.bind( this ), delay );
                        }
                },
                /**
diff --git a/resources/mobile.startup/Panel.js 
b/resources/mobile.startup/Panel.js
index 3099514..f69b793 100644
--- a/resources/mobile.startup/Panel.js
+++ b/resources/mobile.startup/Panel.js
@@ -37,16 +37,14 @@
                 * @method
                 */
                show: function () {
-                       var self = this;
-
-                       if ( !self.isVisible() ) {
+                       if ( !this.isVisible() ) {
                                // use setTimeout to allow the browser to 
redraw if render() was called
                                // just before show(); this is important for 
animations to work
                                // (0ms doesn't work on Firefox, 10ms is enough)
                                setTimeout( function () {
-                                       self.$el.addClass( 'visible animated' );
-                                       self.emit( 'show' );
-                               }, self.minHideDelay );
+                                       this.$el.addClass( 'visible animated' );
+                                       this.emit( 'show' );
+                               }.bind( this ), this.minHideDelay );
                        }
                },
 
@@ -55,13 +53,11 @@
                 * @method
                 */
                hide: function () {
-                       var self = this;
-
                        // see comment in show()
                        setTimeout( function () {
-                               self.$el.removeClass( 'visible' );
-                               self.emit( 'hide' );
-                       }, self.minHideDelay );
+                               this.$el.removeClass( 'visible' );
+                               this.emit( 'hide' );
+                       }.bind( this ), this.minHideDelay );
                },
 
                /**
diff --git a/resources/mobile.startup/Section.js 
b/resources/mobile.startup/Section.js
index 3393265..603280d 100644
--- a/resources/mobile.startup/Section.js
+++ b/resources/mobile.startup/Section.js
@@ -12,17 +12,17 @@
         * @param {Object} options Configuration options
         */
        function Section( options ) {
-               var self = this;
+               var children = [];
                options.tag = 'h' + options.level;
                this.line = options.line;
                this.text = options.text;
                this.hasReferences = options.hasReferences || false;
                this.id = options.id || null;
                this.anchor = options.anchor;
-               this.children = [];
                ( options.children || [] ).forEach( function ( section ) {
-                       self.children.push( new Section( section ) );
+                       children.push( new Section( section ) );
                } );
+               this.children = children;
                View.call( this, options );
        }
 
diff --git a/resources/mobile.startup/Skin.js b/resources/mobile.startup/Skin.js
index 01e5072..cf8f0c8 100644
--- a/resources/mobile.startup/Skin.js
+++ b/resources/mobile.startup/Skin.js
@@ -46,8 +46,6 @@
         * @param {Object} options Configuration options
         */
        function Skin( options ) {
-               var self = this;
-
                this.page = options.page;
                this.name = options.name;
                this.mainMenu = options.mainMenu;
@@ -65,16 +63,16 @@
                function loadWideScreenModules() {
                        if ( browser.isWideScreen() ) {
                                // Adjust screen for tablets
-                               if ( self.page.inNamespace( '' ) ) {
-                                       mw.loader.using( self.tabletModules 
).always( function () {
-                                               self.off( '_resize' );
-                                               self.emit.call( self, 'changed' 
);
-                                       } );
+                               if ( this.page.inNamespace( '' ) ) {
+                                       mw.loader.using( this.tabletModules 
).always( function () {
+                                               this.off( '_resize' );
+                                               this.emit.call( this, 'changed' 
);
+                                       }.bind( this ) );
                                }
                        }
                }
                M.on( 'resize', $.proxy( this, 'emit', '_resize' ) );
-               this.on( '_resize', loadWideScreenModules );
+               this.on( '_resize', loadWideScreenModules.bind( this ) );
                this.emit( '_resize' );
 
                if (
@@ -82,8 +80,8 @@
                        mw.config.get( 'wgMFLazyLoadImages' )
                ) {
                        $( function () {
-                               self.loadImages();
-                       } );
+                               this.loadImages();
+                       }.bind( this ) );
                }
 
                if ( mw.config.get( 'wgMFLazyLoadReferences' ) ) {
@@ -170,9 +168,9 @@
                 *  searched for image placeholders. Defaults to "#content".
                 */
                loadImages: function ( $container ) {
-                       var self = this,
-                               offset = $( window ).height() * 1.5,
-                               imagePlaceholders;
+                       var offset = $( window ).height() * 1.5,
+                               imagePlaceholders,
+                               _loadImages;
 
                        $container = $container || this.$( '#content' );
                        imagePlaceholders = $container.find( 
'.lazy-image-placeholder' ).toArray();
@@ -180,7 +178,7 @@
                        /**
                         * Load remaining images in viewport
                         */
-                       function _loadImages() {
+                       _loadImages = function () {
 
                                imagePlaceholders = $.grep( imagePlaceholders, 
function ( placeholder ) {
                                        var $placeholder = $( placeholder );
@@ -191,21 +189,21 @@
                                                // to circumvent this we also 
need to test the height (see T143768).
                                                ( $placeholder.is( ':visible' ) 
|| $placeholder.height() === 0 )
                                        ) {
-                                               self.loadImage( $placeholder );
+                                               this.loadImage( $placeholder );
                                                return false;
                                        }
 
                                        return true;
-                               } );
+                               }.bind( this ) );
 
                                if ( !imagePlaceholders.length ) {
                                        M.off( 'scroll:throttled', _loadImages 
);
                                        M.off( 'resize:throttled', _loadImages 
);
                                        M.off( 'section-toggled', _loadImages );
-                                       self.off( 'changed', _loadImages );
+                                       this.off( 'changed', _loadImages );
                                }
 
-                       }
+                       }.bind( this );
 
                        M.on( 'scroll:throttled', _loadImages );
                        M.on( 'resize:throttled', _loadImages );
@@ -263,8 +261,7 @@
                 */
                lazyLoadReferences: function ( data ) {
                        var $content, $spinner,
-                               gateway = this.referencesGateway,
-                               self = this;
+                               gateway = this.referencesGateway;
 
                        // If the section was expanded before toggling, do not 
load anything as
                        // section is being collapsed now.
@@ -320,8 +317,8 @@
                                                 * @event references-loaded
                                                 * Fired when references list 
is loaded into the HTML
                                                 */
-                                               self.emit( 'references-loaded', 
self.page );
-                                       } )
+                                               this.emit( 'references-loaded', 
this.page );
+                                       }.bind( this ) )
                                        .fail( function () {
                                                $spinner.remove();
                                                // unhide on a failure
@@ -329,10 +326,10 @@
                                        } )
                                        .always( function () {
                                                // lazy load images if any
-                                               self.loadImages( $content );
+                                               this.loadImages( $content );
                                                // Do not attempt further 
loading even if we're unable to load this time.
                                                $content.data( 
'are-references-loaded', 1 );
-                                       } );
+                                       }.bind( this ) );
                        } else {
                                return $.Deferred().reject();
                        }
diff --git a/resources/mobile.startup/View.js b/resources/mobile.startup/View.js
index 99fb7a8..bf36874 100644
--- a/resources/mobile.startup/View.js
+++ b/resources/mobile.startup/View.js
@@ -159,8 +159,6 @@
                 * @param {Object} options Object passed to the constructor.
                 */
                initialize: function ( options ) {
-                       var self = this;
-
                        OO.EventEmitter.call( this );
                        options = $.extend( {}, this.defaults, options );
                        this.options = options;
@@ -185,9 +183,9 @@
                                this._postInitialize();
                        } else {
                                $( function () {
-                                       self.$el = $( options.el );
-                                       self._postInitialize();
-                               } );
+                                       this.$el = $( options.el );
+                                       this._postInitialize();
+                               }.bind( this ) );
                        }
                },
 
diff --git a/resources/mobile.startup/browser.js 
b/resources/mobile.startup/browser.js
index cf0ff9d..c5ed83a 100644
--- a/resources/mobile.startup/browser.js
+++ b/resources/mobile.startup/browser.js
@@ -48,15 +48,14 @@
                 * @private
                 */
                _fixIosLandscapeBug: function () {
-                       var self = this,
-                               viewport = this.$el.find( 
'meta[name="viewport"]' )[0];
+                       var viewport = this.$el.find( 'meta[name="viewport"]' 
)[0];
 
                        // see http://adactio.com/journal/4470/ (fixed in ios 6)
                        if ( viewport && ( this.isIos( 4 ) || this.isIos( 5 ) ) 
) {
                                this.lockViewport();
                                document.addEventListener( 'gesturestart', 
function () {
-                                       self.lockViewport();
-                               }, false );
+                                       this.lockViewport();
+                               }.bind( this ), false );
                        }
                },
                /**
diff --git a/resources/mobile.startup/modules.js 
b/resources/mobile.startup/modules.js
index 6b9a0ab..08d9d4d 100644
--- a/resources/mobile.startup/modules.js
+++ b/resources/mobile.startup/modules.js
@@ -61,8 +61,6 @@
                 * @return {Object}
                 */
                define: function ( id, obj ) {
-                       var self = this;
-
                        if ( this._register.hasOwnProperty( id ) ) {
                                throw new Error( 'Module already exists: ' + id 
);
                        }
@@ -74,8 +72,8 @@
                                 * @param {string} deprecatedId Defined module 
id, which is deprecated.
                                 */
                                deprecate: function ( deprecatedId ) {
-                                       self.deprecate( deprecatedId, obj, id );
-                               }
+                                       this.deprecate( deprecatedId, obj, id );
+                               }.bind( this )
                        };
                },
 
diff --git a/resources/mobile.swipe/Swipe.js b/resources/mobile.swipe/Swipe.js
index 7c19c7e..f2b034a 100644
--- a/resources/mobile.swipe/Swipe.js
+++ b/resources/mobile.swipe/Swipe.js
@@ -18,15 +18,14 @@
         *       OO.mfExtend( ImageOverlayNew, ImageOverlay, {
         *         //...
         *         initialize: function ( options ) {
-        *           var self = this;
         *           this.swipe = new Swipe();
         *           this.swipe
         *             .on( 'swipe-right', function () {
-        *               self.setNewImage( $( '.slider-button.prev' ).data( 
'thumbnail' ) );
-        *             } )
+        *               this.setNewImage( $( '.slider-button.prev' ).data( 
'thumbnail' ) );
+        *             }.bind( this ) )
         *             .on( 'swipe-left', function () {
-        *               self.setNewImage( $( '.slider-button.next' ).data( 
'thumbnail' ) );
-        *             } );
+        *               this.setNewImage( $( '.slider-button.next' ).data( 
'thumbnail' ) );
+        *             }.bind( this ) );
         *           ImageOverlay.prototype.initialize.apply( this, arguments );
         *         },
         *         postRender: function () {
diff --git a/resources/mobile.talk.overlays/TalkOverlay.js 
b/resources/mobile.talk.overlays/TalkOverlay.js
index f6844ca..224ca1a 100644
--- a/resources/mobile.talk.overlays/TalkOverlay.js
+++ b/resources/mobile.talk.overlays/TalkOverlay.js
@@ -88,8 +88,6 @@
                 * @private
                 */
                _loadContent: function ( options ) {
-                       var self = this;
-
                        // show a spinner
                        this.showSpinner();
 
@@ -101,7 +99,7 @@
                                // talk page doesn't exist yet.
                                if ( resp === 'missingtitle' ) {
                                        // Create an empty page for new pages
-                                       self._addContent( {
+                                       this._addContent( {
                                                title: options.title,
                                                sections: []
                                        }, options );
@@ -110,9 +108,9 @@
                                        // page manually rather than leaving 
the spinner spinning.
                                        window.location = mw.util.getUrl( 
options.title );
                                }
-                       } ).done( function ( pageData ) {
-                               self._addContent( pageData, options );
-                       } );
+                       }.bind( this ) ).done( function ( pageData ) {
+                               this._addContent( pageData, options );
+                       }.bind( this ) );
                },
 
                /**
diff --git a/resources/mobile.talk.overlays/TalkSectionAddOverlay.js 
b/resources/mobile.talk.overlays/TalkSectionAddOverlay.js
index 261a91b..554356c 100644
--- a/resources/mobile.talk.overlays/TalkSectionAddOverlay.js
+++ b/resources/mobile.talk.overlays/TalkSectionAddOverlay.js
@@ -77,29 +77,25 @@
                 * Handles an input into a textarea and enables or disables the 
submit button
                 */
                onTextInput: function () {
-                       var self = this;
-
                        clearTimeout( this.timer );
                        this.timer = setTimeout( function () {
-                               if ( !self.$ta.val().trim() || 
!self.$subject.val().trim() ) {
-                                       self.$confirm.prop( 'disabled', true );
+                               if ( !this.$ta.val().trim() || 
!this.$subject.val().trim() ) {
+                                       this.$confirm.prop( 'disabled', true );
                                } else {
-                                       self.$confirm.prop( 'disabled', false );
+                                       this.$confirm.prop( 'disabled', false );
                                }
-                       }, 250 );
+                       }.bind( this ), 250 );
                },
                /**
                 * Handles a click on the save button
                 */
                onSaveClick: function () {
-                       var self = this;
-
                        this.showHidden( '.saving-header' );
                        this.save().done( function ( status ) {
                                if ( status === 'ok' ) {
                                        // Check if the user was previously on 
the talk overlay
-                                       if ( self.title !== mw.config.get( 
'wgPageName' ) ) {
-                                               
self.pageGateway.invalidatePage( self.title );
+                                       if ( this.title !== mw.config.get( 
'wgPageName' ) ) {
+                                               
this.pageGateway.invalidatePage( this.title );
                                                toast.show( mw.msg( 
'mobile-frontend-talk-topic-feedback' ) );
                                                M.emit( 'talk-discussion-added' 
);
                                                window.history.back();
@@ -107,10 +103,10 @@
                                                M.emit( 'talk-added-wo-overlay' 
);
                                        }
                                }
-                       } ).fail( function ( error ) {
+                       }.bind( this ) ).fail( function ( error ) {
                                var editMsg = mw.msg( 
'mobile-frontend-talk-topic-error' );
 
-                               self.$confirm.prop( 'disabled', false );
+                               this.$confirm.prop( 'disabled', false );
                                switch ( error.details ) {
                                        case 'protectedpage':
                                                editMsg = mw.msg( 
'mobile-frontend-talk-topic-error-protected' );
@@ -131,8 +127,8 @@
                                }
 
                                toast.show( editMsg, 'error' );
-                               self.showHidden( '.save-header, .save-panel' );
-                       } );
+                               this.showHidden( '.save-header, .save-panel' );
+                       }.bind( this ) );
                },
                /**
                 * Save new talk section
@@ -142,7 +138,6 @@
                 */
                save: function () {
                        var heading = this.$subject.val(),
-                               self = this,
                                text = this.$ta.val(),
                                result = $.Deferred();
                        this.$ta.removeClass( 'error' );
@@ -158,7 +153,7 @@
                                action: 'edit',
                                section: 'new',
                                sectiontitle: heading,
-                               title: self.title,
+                               title: this.title,
                                summary: mw.msg( 'newsectionsummary', heading ),
                                text: text + ' ~~~~'
                        } ).done( function () {
diff --git a/resources/mobile.talk.overlays/TalkSectionOverlay.js 
b/resources/mobile.talk.overlays/TalkSectionOverlay.js
index 167e0f9..d61931b 100644
--- a/resources/mobile.talk.overlays/TalkSectionOverlay.js
+++ b/resources/mobile.talk.overlays/TalkSectionOverlay.js
@@ -80,14 +80,12 @@
                 * @param {Object} options Render options
                 */
                renderFromApi: function ( options ) {
-                       var self = this;
-
                        this.pageGateway.getPage( options.title ).done( 
function ( pageData ) {
                                var page = new Page( pageData );
                                options.section = page.getSection( options.id );
-                               self.render( options );
-                               self.clearSpinner();
-                       } );
+                               this.render( options );
+                               this.clearSpinner();
+                       }.bind( this ) );
                },
                /**
                 * Handler for focus of textarea
@@ -99,8 +97,7 @@
                 * Handle a click on the save button
                 */
                onSaveClick: function () {
-                       var val = this.$textarea.val(),
-                               self = this;
+                       var val = this.$textarea.val();
 
                        if ( val ) {
                                // show a spinner
@@ -117,10 +114,10 @@
                                } ).done( function () {
                                        popup.show( mw.msg( 
'mobile-frontend-talk-reply-success' ) );
                                        // invalidate the cache
-                                       self.pageGateway.invalidatePage( 
self.options.title );
+                                       this.pageGateway.invalidatePage( 
this.options.title );
 
-                                       self.renderFromApi( self.options );
-                               } ).fail( function ( data, response ) {
+                                       this.renderFromApi( this.options );
+                               }.bind( this ) ).fail( function ( data, 
response ) {
                                        // FIXME: Code sharing with 
EditorOverlay?
                                        var msg,
                                                // When save failed with one of 
these error codes, the returned
@@ -141,11 +138,11 @@
                                                msg = mw.msg( 
'mobile-frontend-editor-error' );
                                        }
 
-                                       self.clearSpinner();
+                                       this.clearSpinner();
                                        popup.show( msg, 'toast error' );
-                               } ).always( function () {
-                                       self.$saveButton.prop( 'disabled', 
false );
-                               } );
+                               }.bind( this ) ).always( function () {
+                                       this.$saveButton.prop( 'disabled', 
false );
+                               }.bind( this ) );
                        } else {
                                this.$textarea.addClass( 'error' );
                        }
diff --git a/resources/mobile.toggle/toggle.js 
b/resources/mobile.toggle/toggle.js
index c18c3a5..b4ece00 100644
--- a/resources/mobile.toggle/toggle.js
+++ b/resources/mobile.toggle/toggle.js
@@ -233,7 +233,6 @@
        Toggler.prototype._enable = function ( $container, prefix, page, 
isClosed ) {
                var tagName, expandSections, indicator, $content,
                        $firstHeading,
-                       self = this,
                        collapseSectionsByDefault = mw.config.get( 
'wgMFCollapseSectionsByDefault' );
 
                // Also allow .section-heading if some extensions like Wikibase
@@ -248,9 +247,9 @@
                expandSections = !collapseSectionsByDefault ||
                        ( context.isBetaGroupMember() && settings.get( 
'expandSections', true ) === 'true' );
 
-               $container.children( tagName ).each( function ( i ) {
+               $container.children( tagName ).each( function ( i, heading ) {
                        var isReferenceSection,
-                               $heading = $( this ),
+                               $heading = $( heading ),
                                $indicator = $heading.find( '.indicator' ),
                                id = prefix + 'collapsible-block-' + i;
                        // Be sure there is a div wrapping the section content.
@@ -272,9 +271,9 @@
                                                if ( !ev.target.href ) {
                                                        // prevent taps/clicks 
on edit button after toggling (bug 56209)
                                                        ev.preventDefault();
-                                                       self.toggle.call( self, 
$( this ) );
+                                                       this.toggle.call( this, 
$( ev.target ) );
                                                }
-                                       } );
+                                       }.bind( this ) );
 
                                indicator = new Icon( arrowOptions );
                                if ( $indicator.length ) {
@@ -296,13 +295,13 @@
                                                'aria-expanded': 'false'
                                        } );
 
-                               enableKeyboardActions( self, $heading );
+                               enableKeyboardActions( this, $heading );
                                if ( !isReferenceSection && ( !isClosed && 
browser.isWideScreen() || expandSections ) ) {
                                        // Expand sections by default on wide 
screen devices or if the expand sections setting is set
-                                       self.toggle.call( self, $heading );
+                                       this.toggle.call( this, $heading );
                                }
                        }
-               } );
+               }.bind( this ) );
 
                /**
                 * Checks the existing hash and toggles open any section that 
contains the fragment.
@@ -313,7 +312,7 @@
                function checkHash() {
                        var hash = window.location.hash;
                        if ( hash.indexOf( '#' ) === 0 ) {
-                               self.reveal( hash, $container );
+                               this.reveal( hash, $container );
                        }
                }
 
@@ -330,22 +329,23 @@
 
                        if ( internalRedirectHash ) {
                                window.location.hash = internalRedirectHash;
-                               self.reveal( internalRedirectHash, $container );
+                               this.reveal( internalRedirectHash, $container );
                        }
                }
 
-               checkInternalRedirectAndHash();
-               checkHash( this );
+               checkInternalRedirectAndHash.bind( this )();
+               checkHash.bind( this )();
                // Restricted to links created by editors and thus outside our 
control
-               $container.find( 'a' ).on( 'click', function () {
+               $container.find( 'a' ).on( 'click', function ( ev ) {
+                       var $link = $( ev.target );
                        // the link might be an internal link with a hash.
                        // if it is check if we need to reveal any sections.
-                       if ( $( this ).attr( 'href' ) !== undefined &&
-                               $( this ).attr( 'href' ).indexOf( '#' ) > -1
+                       if ( $link.attr( 'href' ) !== undefined &&
+                               $link.attr( 'href' ).indexOf( '#' ) > -1
                        ) {
-                               checkHash( this );
+                               checkHash.bind( this )();
                        }
-               } );
+               }.bind( this ) );
 
                if ( !browser.isWideScreen() ) {
                        expandStoredSections( this, $container, page );
diff --git a/resources/mobile.watchlist/WatchListGateway.js 
b/resources/mobile.watchlist/WatchListGateway.js
index c0c3799..1ba1c83 100644
--- a/resources/mobile.watchlist/WatchListGateway.js
+++ b/resources/mobile.watchlist/WatchListGateway.js
@@ -34,16 +34,15 @@
                 * @return {jQuery.Deferred}
                 */
                loadWatchlist: function () {
-                       var self = this,
-                               params = extendSearchParams( 'watchlist', {
-                                       prop: [ 'info', 'revisions' ],
-                                       format: 'json',
-                                       formatversion: 2,
-                                       rvprop: 'timestamp|user',
-                                       generator: 'watchlistraw',
-                                       gwrnamespace: '0',
-                                       gwrlimit: this.limit
-                               }, this.continueParams );
+                       var params = extendSearchParams( 'watchlist', {
+                               prop: [ 'info', 'revisions' ],
+                               format: 'json',
+                               formatversion: 2,
+                               rvprop: 'timestamp|user',
+                               generator: 'watchlistraw',
+                               gwrnamespace: '0',
+                               gwrlimit: this.limit
+                       }, this.continueParams );
 
                        if ( this.canContinue === false ) {
                                return $.Deferred();
@@ -60,13 +59,13 @@
                                url: this.apiUrl
                        } ).then( function ( data ) {
                                if ( data.hasOwnProperty( 'continue' ) ) {
-                                       self.continueParams = data.continue;
+                                       this.continueParams = data.continue;
                                } else {
-                                       self.canContinue = false;
+                                       this.canContinue = false;
                                }
 
-                               return self.parseData( data );
-                       } );
+                               return this.parseData( data );
+                       }.bind( this ) );
                },
 
                /**
diff --git a/resources/mobile.watchstar/Watchstar.js 
b/resources/mobile.watchstar/Watchstar.js
index f2ccfb3..d03cfcb 100644
--- a/resources/mobile.watchstar/Watchstar.js
+++ b/resources/mobile.watchstar/Watchstar.js
@@ -28,22 +28,21 @@
         * @param {Object} options Configuration options
         */
        function Watchstar( options ) {
-               var self = this,
-                       _super = View,
+               var _super = View,
                        page = options.page;
 
                this.gateway = new WatchstarGateway( options.api );
 
                if ( user.isAnon() ) {
-                       _super.call( self, options );
+                       _super.call( this, options );
                } else if ( options.isWatched === undefined ) {
                        this.gateway.loadWatchStatus( page.getId() ).done( 
function () {
-                               options.isWatched = self.gateway.isWatchedPage( 
page );
-                               _super.call( self, options );
-                       } );
+                               options.isWatched = this.gateway.isWatchedPage( 
page );
+                               _super.call( this, options );
+                       }.bind( this ) );
                } else {
                        this.gateway.setWatchedPage( options.page, 
options.isWatched );
-                       _super.call( self, options );
+                       _super.call( this, options );
                }
        }
 
@@ -85,22 +84,21 @@
                template: mw.template.compile( '<button>{{tooltip}}</button>', 
'hogan' ),
                /** @inheritdoc */
                initialize: function ( options ) {
-                       var self = this,
-                               _super = View.prototype.initialize,
+                       var _super = View.prototype.initialize,
                                page = options.page;
 
                        this.gateway = new WatchstarGateway( options.api );
 
                        if ( user.isAnon() ) {
-                               _super.call( self, options );
+                               _super.call( this, options );
                        } else if ( options.isWatched === undefined ) {
                                this.gateway.loadWatchStatus( page.getId() 
).done( function () {
-                                       options.isWatched = 
self.gateway.isWatchedPage( page );
-                                       _super.call( self, options );
-                               } );
+                                       options.isWatched = 
this.gateway.isWatchedPage( page );
+                                       _super.call( this, options );
+                               }.bind( this ) );
                        } else {
                                this.gateway.setWatchedPage( options.page, 
options.isWatched );
-                               _super.call( self, options );
+                               _super.call( this, options );
                        }
                },
                /** @inheritdoc */
@@ -109,15 +107,14 @@
                },
                /** @inheritdoc */
                postRender: function () {
-                       var self = this,
-                               gateway = this.gateway,
+                       var gateway = this.gateway,
                                unwatchedClass = watchIcon.getGlyphClassName(),
                                watchedClass = watchedIcon.getGlyphClassName() 
+ ' watched',
-                               page = self.options.page,
-                               $el = self.$el;
+                               page = this.options.page,
+                               $el = this.$el;
 
                        // add tooltip to the div, not the <a> inside because 
that the <a> doesn't have dimensions
-                       this.$el.attr( 'title', self.options.tooltip );
+                       this.$el.attr( 'title', this.options.tooltip );
 
                        // Add watched class if necessary
                        if ( !user.isAnon() && gateway.isWatchedPage( page ) ) {
@@ -153,8 +150,7 @@
                 * @method
                 */
                onStatusToggleUser: function () {
-                       var self = this,
-                               gateway = this.gateway,
+                       var gateway = this.gateway,
                                page = this.options.page,
                                checker;
 
@@ -165,25 +161,25 @@
                                clearInterval( checker );
                        } ).done( function () {
                                if ( gateway.isWatchedPage( page ) ) {
-                                       self.options.isWatched = true;
-                                       self.render();
+                                       this.options.isWatched = true;
+                                       this.render();
                                        /**
                                         * @event watch
                                         * Fired when the watch star is changed 
to watched status
                                         */
-                                       self.emit( 'watch' );
+                                       this.emit( 'watch' );
                                        toast.show( mw.msg( 
'mobile-frontend-watchlist-add', page.title ) );
                                } else {
-                                       self.options.isWatched = false;
+                                       this.options.isWatched = false;
                                        /**
                                         * @event unwatch
                                         * Fired when the watch star is changed 
to unwatched status
                                         */
-                                       self.emit( 'unwatch' );
-                                       self.render();
+                                       this.emit( 'unwatch' );
+                                       this.render();
                                        toast.show( mw.msg( 
'mobile-frontend-watchlist-removed', page.title ) );
                                }
-                       } ).fail( function () {
+                       }.bind( this ) ).fail( function () {
                                toast.show( 'mobile-frontend-watchlist-error', 
'error' );
                        } );
                },
diff --git a/resources/mobile.watchstar/WatchstarGateway.js 
b/resources/mobile.watchstar/WatchstarGateway.js
index 0177ce2..a519d52 100644
--- a/resources/mobile.watchstar/WatchstarGateway.js
+++ b/resources/mobile.watchstar/WatchstarGateway.js
@@ -21,10 +21,10 @@
                 * @param {Object} resp Response from the server
                 */
                _loadIntoCache: function ( resp ) {
-                       var self = this;
+                       var cache = this._cache;
                        if ( resp.query && resp.query.pages ) {
                                Object.keys( resp.query.pages ).forEach( 
function ( id ) {
-                                       self._cache[ id ] = resp.query.pages[ 
id ].hasOwnProperty( 'watched' );
+                                       cache[ id ] = resp.query.pages[ id 
].hasOwnProperty( 'watched' );
                                } );
                        }
                },
@@ -36,13 +36,12 @@
                 * @return {jQuery.Deferred}
                 */
                loadWatchStatus: function ( ids, markAsAllWatched ) {
-                       var self = this,
-                               result = $.Deferred();
+                       var result = $.Deferred();
 
                        if ( markAsAllWatched ) {
                                ids.forEach( function ( id ) {
-                                       self._cache[ id ] = true;
-                               } );
+                                       this._cache[ id ] = true;
+                               }.bind( this ) );
                                result.resolve();
                        } else {
                                this.api.get( {
@@ -51,9 +50,9 @@
                                        inprop: 'watched',
                                        pageids: ids
                                } ).done( function ( resp ) {
-                                       self._loadIntoCache( resp );
+                                       this._loadIntoCache( resp );
                                        result.resolve();
-                               } );
+                               }.bind( this ) );
                        }
                        return result;
                },
@@ -87,7 +86,6 @@
                 */
                toggleStatus: function ( page ) {
                        var data,
-                               self = this,
                                id = page.getId();
 
                        data = {
@@ -104,10 +102,10 @@
                                data.unwatch = true;
                        }
                        return this.api.postWithToken( 'watch', data ).done( 
function () {
-                               var newStatus = !self.isWatchedPage( page );
-                               self.setWatchedPage( page, newStatus );
+                               var newStatus = !this.isWatchedPage( page );
+                               this.setWatchedPage( page, newStatus );
                                M.emit( 'watched', page, newStatus );
-                       } );
+                       }.bind( this ) );
                }
        };
 
diff --git a/tests/qunit/mobile.search/test_MobileWebSearchLogger.js 
b/tests/qunit/mobile.search/test_MobileWebSearchLogger.js
index bb04895..af46c6b 100644
--- a/tests/qunit/mobile.search/test_MobileWebSearchLogger.js
+++ b/tests/qunit/mobile.search/test_MobileWebSearchLogger.js
@@ -10,16 +10,14 @@
        } );
 
        QUnit.test( 'it should log when the search is shown', 1, function ( 
assert ) {
-               var self = this;
-
                // The user opens the search overlay.
                this.logger.onSearchShow();
                this.logger.onSearchStart();
 
                assert.ok( this.spy.calledWith( 'mf.schemaMobileWebSearch', {
                        action: 'session-start',
-                       userSessionToken: self.logger.userSessionToken,
-                       searchSessionToken: self.logger.searchSessionToken,
+                       userSessionToken: this.logger.userSessionToken,
+                       searchSessionToken: this.logger.searchSessionToken,
                        timeOffsetSinceStart: 0
                } ), 'Search start is logged correctly.' );
        } );
diff --git a/tests/qunit/mobile.startup/test_View.js 
b/tests/qunit/mobile.startup/test_View.js
index a9ad6f4..4dcac05 100644
--- a/tests/qunit/mobile.startup/test_View.js
+++ b/tests/qunit/mobile.startup/test_View.js
@@ -33,10 +33,9 @@
        } );
 
        QUnit.test( 'View, jQuery proxy functions', 10, function ( assert ) {
-               var self = this,
-                       view = new View( {
-                               el: 'body'
-                       } );
+               var view = new View( {
+                       el: 'body'
+               } );
                [
                        'append',
                        'prepend',
@@ -49,11 +48,11 @@
                        'remove',
                        'detach'
                ].forEach( function ( prop ) {
-                       var stub = self.sandbox.stub( view.$el, prop );
+                       var stub = this.sandbox.stub( view.$el, prop );
                        view[ prop ]( 'test', 1 );
                        assert.ok( stub.calledWith( 'test', 1 ) );
                        stub.restore();
-               } );
+               }.bind( this ) );
        } );
 
        QUnit.test( 'View extended, with el property', 1, function ( assert ) {

-- 
To view, visit https://gerrit.wikimedia.org/r/345485
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: If44b2bddde8794283b04e98e0e7a847911160549
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: Jhernandez <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to