wp/wp-admin/js/theme.js
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
--- a/wp/wp-admin/js/theme.js	Tue Oct 22 16:11:46 2019 +0200
+++ b/wp/wp-admin/js/theme.js	Tue Dec 15 13:49:49 2020 +0100
@@ -11,36 +11,43 @@
 var themes, l10n;
 themes = wp.themes = wp.themes || {};
 
-// Store the theme data and settings for organized and quick access
-// themes.data.settings, themes.data.themes, themes.data.l10n
+// Store the theme data and settings for organized and quick access.
+// themes.data.settings, themes.data.themes, themes.data.l10n.
 themes.data = _wpThemeSettings;
 l10n = themes.data.l10n;
 
-// Shortcut for isInstall check
+// Shortcut for isInstall check.
 themes.isInstall = !! themes.data.settings.isInstall;
 
-// Setup app structure
+// Setup app structure.
 _.extend( themes, { model: {}, view: {}, routes: {}, router: {}, template: wp.template });
 
 themes.Model = Backbone.Model.extend({
-	// Adds attributes to the default data coming through the .org themes api
-	// Map `id` to `slug` for shared code
+	// Adds attributes to the default data coming through the .org themes api.
+	// Map `id` to `slug` for shared code.
 	initialize: function() {
 		var description;
 
-		// If theme is already installed, set an attribute.
-		if ( _.indexOf( themes.data.installedThemes, this.get( 'slug' ) ) !== -1 ) {
-			this.set({ installed: true });
+		if ( this.get( 'slug' ) ) {
+			// If the theme is already installed, set an attribute.
+			if ( _.indexOf( themes.data.installedThemes, this.get( 'slug' ) ) !== -1 ) {
+				this.set({ installed: true });
+			}
+
+			// If the theme is active, set an attribute.
+			if ( themes.data.activeTheme === this.get( 'slug' ) ) {
+				this.set({ active: true });
+			}
 		}
 
-		// Set the attributes
+		// Set the attributes.
 		this.set({
-			// slug is for installation, id is for existing.
+			// `slug` is for installation, `id` is for existing.
 			id: this.get( 'slug' ) || this.get( 'id' )
 		});
 
 		// Map `section.description` to `description`
-		// as the API sometimes returns it differently
+		// as the API sometimes returns it differently.
 		if ( this.has( 'sections' ) ) {
 			description = this.get( 'sections' ).description;
 			this.set({ description: description });
@@ -48,31 +55,31 @@
 	}
 });
 
-// Main view controller for themes.php
-// Unifies and renders all available views
+// Main view controller for themes.php.
+// Unifies and renders all available views.
 themes.view.Appearance = wp.Backbone.View.extend({
 
 	el: '#wpbody-content .wrap .theme-browser',
 
 	window: $( window ),
-	// Pagination instance
+	// Pagination instance.
 	page: 0,
 
-	// Sets up a throttler for binding to 'scroll'
+	// Sets up a throttler for binding to 'scroll'.
 	initialize: function( options ) {
-		// Scroller checks how far the scroll position is
+		// Scroller checks how far the scroll position is.
 		_.bindAll( this, 'scroller' );
 
 		this.SearchView = options.SearchView ? options.SearchView : themes.view.Search;
 		// Bind to the scroll event and throttle
-		// the results from this.scroller
+		// the results from this.scroller.
 		this.window.bind( 'scroll', _.throttle( this.scroller, 300 ) );
 	},
 
-	// Main render control
+	// Main render control.
 	render: function() {
 		// Setup the main theme view
-		// with the current theme collection
+		// with the current theme collection.
 		this.view = new themes.view.Themes({
 			collection: this.collection,
 			parent: this
@@ -83,21 +90,21 @@
 
 		this.$el.removeClass( 'search-loading' );
 
-		// Render and append
+		// Render and append.
 		this.view.render();
 		this.$el.empty().append( this.view.el ).addClass( 'rendered' );
 	},
 
-	// Defines search element container
+	// Defines search element container.
 	searchContainer: $( '.search-form' ),
 
 	// Search input and view
-	// for current theme collection
+	// for current theme collection.
 	search: function() {
 		var view,
 			self = this;
 
-		// Don't render the search if there is only one theme
+		// Don't render the search if there is only one theme.
 		if ( themes.data.themes.length === 1 ) {
 			return;
 		}
@@ -108,7 +115,7 @@
 		});
 		self.SearchView = view;
 
-		// Render and append after screen title
+		// Render and append after screen title.
 		view.render();
 		this.searchContainer
 			.append( $.parseHTML( '<label class="screen-reader-text" for="wp-filter-search-input">' + l10n.search + '</label>' ) )
@@ -119,7 +126,7 @@
 	},
 
 	// Checks when the user gets close to the bottom
-	// of the mage and triggers a theme:scroll event
+	// of the mage and triggers a theme:scroll event.
 	scroller: function() {
 		var self = this,
 			bottom, threshold;
@@ -134,26 +141,26 @@
 	}
 });
 
-// Set up the Collection for our theme data
+// Set up the Collection for our theme data.
 // @has 'id' 'name' 'screenshot' 'author' 'authorURI' 'version' 'active' ...
 themes.Collection = Backbone.Collection.extend({
 
 	model: themes.Model,
 
-	// Search terms
+	// Search terms.
 	terms: '',
 
 	// Controls searching on the current theme collection
-	// and triggers an update event
+	// and triggers an update event.
 	doSearch: function( value ) {
 
-		// Don't do anything if we've already done this search
-		// Useful because the Search handler fires multiple times per keystroke
+		// Don't do anything if we've already done this search.
+		// Useful because the Search handler fires multiple times per keystroke.
 		if ( this.terms === value ) {
 			return;
 		}
 
-		// Updates terms with the value passed
+		// Updates terms with the value passed.
 		this.terms = value;
 
 		// If we have terms, run a search...
@@ -161,35 +168,41 @@
 			this.search( this.terms );
 		}
 
-		// If search is blank, show all themes
-		// Useful for resetting the views when you clean the input
+		// If search is blank, show all themes.
+		// Useful for resetting the views when you clean the input.
 		if ( this.terms === '' ) {
 			this.reset( themes.data.themes );
 			$( 'body' ).removeClass( 'no-results' );
 		}
 
-		// Trigger a 'themes:update' event
+		// Trigger a 'themes:update' event.
 		this.trigger( 'themes:update' );
 	},
 
-	// Performs a search within the collection
-	// @uses RegExp
+	/**
+	 * Performs a search within the collection.
+	 *
+	 * @uses RegExp
+	 */
 	search: function( term ) {
 		var match, results, haystack, name, description, author;
 
-		// Start with a full collection
+		// Start with a full collection.
 		this.reset( themes.data.themes, { silent: true } );
 
-		// Escape the term string for RegExp meta characters
+		// Trim the term.
+		term = term.trim();
+
+		// Escape the term string for RegExp meta characters.
 		term = term.replace( /[-\/\\^$*+?.()|[\]{}]/g, '\\$&' );
 
 		// Consider spaces as word delimiters and match the whole string
-		// so matching terms can be combined
+		// so matching terms can be combined.
 		term = term.replace( / /g, ')(?=.*' );
 		match = new RegExp( '^(?=.*' + term + ').+', 'i' );
 
-		// Find results
-		// _.filter and .test
+		// Find results.
+		// _.filter() and .test().
 		results = this.filter( function( data ) {
 			name        = data.get( 'name' ).replace( /(<([^>]+)>)/ig, '' );
 			description = data.get( 'description' ).replace( /(<([^>]+)>)/ig, '' );
@@ -214,12 +227,12 @@
 	},
 
 	// Paginates the collection with a helper method
-	// that slices the collection
+	// that slices the collection.
 	paginate: function( instance ) {
 		var collection = this;
 		instance = instance || 0;
 
-		// Themes per instance are set at 20
+		// Themes per instance are set at 20.
 		collection = _( collection.rest( 20 * instance ) );
 		collection = _( collection.first( 20 ) );
 
@@ -228,11 +241,13 @@
 
 	count: false,
 
-	// Handles requests for more themes
-	// and caches results
-	//
-	// When we are missing a cache object we fire an apiCall()
-	// which triggers events of `query:success` or `query:fail`
+	/*
+	 * Handles requests for more themes and caches results.
+	 *
+	 *
+	 * When we are missing a cache object we fire an apiCall()
+	 * which triggers events of `query:success` or `query:fail`.
+	 */
 	query: function( request ) {
 		/**
 		 * @static
@@ -243,7 +258,7 @@
 			query, isPaginated, count;
 
 		// Store current query request args
-		// for later use with the event `theme:end`
+		// for later use with the event `theme:end`.
 		this.currentQuery.request = request;
 
 		// Search the query cache for matches.
@@ -255,7 +270,7 @@
 		// it means we have a paginated request.
 		isPaginated = _.has( request, 'page' );
 
-		// Reset the internal api page counter for non paginated queries.
+		// Reset the internal api page counter for non-paginated queries.
 		if ( ! isPaginated ) {
 			this.currentQuery.page = 1;
 		}
@@ -268,7 +283,7 @@
 				if ( data.themes ) {
 					self.reset( data.themes );
 					count = data.info.results;
-					// Store the results and the query request
+					// Store the results and the query request.
 					queries.push( { themes: data.themes, request: request, total: count } );
 				}
 
@@ -288,8 +303,8 @@
 			// If it's a paginated request we need to fetch more themes...
 			if ( isPaginated ) {
 				return this.apiCall( request, isPaginated ).done( function( data ) {
-					// Add the new themes to the current collection
-					// @todo update counter
+					// Add the new themes to the current collection.
+					// @todo Update counter.
 					self.add( data.themes );
 					self.trigger( 'query:success' );
 
@@ -308,7 +323,7 @@
 			}
 
 			// Only trigger an update event since we already have the themes
-			// on our cached object
+			// on our cached object.
 			if ( _.isNumber( query.total ) ) {
 				this.count = query.total;
 			}
@@ -323,20 +338,20 @@
 		}
 	},
 
-	// Local cache array for API queries
+	// Local cache array for API queries.
 	queries: [],
 
-	// Keep track of current query so we can handle pagination
+	// Keep track of current query so we can handle pagination.
 	currentQuery: {
 		page: 1,
 		request: {}
 	},
 
-	// Send request to api.wordpress.org/themes
+	// Send request to api.wordpress.org/themes.
 	apiCall: function( request, paginated ) {
 		return wp.ajax.send( 'query-themes', {
 			data: {
-			// Request data
+				// Request data.
 				request: _.extend({
 					per_page: 100
 				}, request)
@@ -344,7 +359,7 @@
 
 			beforeSend: function() {
 				if ( ! paginated ) {
-					// Spin it
+					// Spin it.
 					$( 'body' ).addClass( 'loading-content' ).removeClass( 'no-results' );
 				}
 			}
@@ -356,17 +371,17 @@
 });
 
 // This is the view that controls each theme item
-// that will be displayed on the screen
+// that will be displayed on the screen.
 themes.view.Theme = wp.Backbone.View.extend({
 
-	// Wrap theme data on a div.theme element
+	// Wrap theme data on a div.theme element.
 	className: 'theme',
 
-	// Reflects which theme view we have
-	// 'grid' (default) or 'detail'
+	// Reflects which theme view we have.
+	// 'grid' (default) or 'detail'.
 	state: 'grid',
 
-	// The HTML template for each element to be rendered
+	// The HTML template for each element to be rendered.
 	html: themes.template( 'theme' ),
 
 	events: {
@@ -388,14 +403,14 @@
 	render: function() {
 		var data = this.model.toJSON();
 
-		// Render themes using the html template
+		// Render themes using the html template.
 		this.$el.html( this.html( data ) ).attr({
 			tabindex: 0,
 			'aria-describedby' : data.id + '-action ' + data.id + '-name',
 			'data-slug': data.id
 		});
 
-		// Renders active theme styles
+		// Renders active theme styles.
 		this.activeTheme();
 
 		if ( this.model.get( 'displayAuthor' ) ) {
@@ -404,7 +419,7 @@
 	},
 
 	// Adds a class to the currently active theme
-	// and to the overlay in detailed view mode
+	// and to the overlay in detailed view mode.
 	activeTheme: function() {
 		if ( this.model.get( 'active' ) ) {
 			this.$el.addClass( 'active' );
@@ -419,25 +434,25 @@
 		$themeToFocus.addClass('focus');
 	},
 
-	// Single theme overlay screen
-	// It's shown when clicking a theme
+	// Single theme overlay screen.
+	// It's shown when clicking a theme.
 	expand: function( event ) {
 		var self = this;
 
 		event = event || window.event;
 
-		// 'enter' and 'space' keys expand the details view when a theme is :focused
+		// 'Enter' and 'Space' keys expand the details view when a theme is :focused.
 		if ( event.type === 'keydown' && ( event.which !== 13 && event.which !== 32 ) ) {
 			return;
 		}
 
-		// Bail if the user scrolled on a touch device
+		// Bail if the user scrolled on a touch device.
 		if ( this.touchDrag === true ) {
 			return this.touchDrag = false;
 		}
 
 		// Prevent the modal from showing when the user clicks
-		// one of the direct action buttons
+		// one of the direct action buttons.
 		if ( $( event.target ).is( '.theme-actions a' ) ) {
 			return;
 		}
@@ -447,7 +462,7 @@
 			return;
 		}
 
-		// Set focused theme to current element
+		// Set focused theme to current element.
 		themes.focusedTheme = this.$el;
 
 		this.trigger( 'theme:expand', self.model.cid );
@@ -463,7 +478,7 @@
 
 		event = event || window.event;
 
-		// Bail if the user scrolled on a touch device
+		// Bail if the user scrolled on a touch device.
 		if ( this.touchDrag === true ) {
 			return this.touchDrag = false;
 		}
@@ -473,12 +488,12 @@
 			return;
 		}
 
-		// 'enter' and 'space' keys expand the details view when a theme is :focused
+		// 'Enter' and 'Space' keys expand the details view when a theme is :focused.
 		if ( event.type === 'keydown' && ( event.which !== 13 && event.which !== 32 ) ) {
 			return;
 		}
 
-		// pressing enter while focused on the buttons shouldn't open the preview
+		// Pressing Enter while focused on the buttons shouldn't open the preview.
 		if ( event.type === 'keydown' && event.which !== 13 && $( ':focus' ).hasClass( 'button' ) ) {
 			return;
 		}
@@ -499,14 +514,14 @@
 		preview.render();
 		this.setNavButtonsState();
 
-		// Hide previous/next navigation if there is only one theme
+		// Hide previous/next navigation if there is only one theme.
 		if ( this.model.collection.length === 1 ) {
 			preview.$el.addClass( 'no-navigation' );
 		} else {
 			preview.$el.removeClass( 'no-navigation' );
 		}
 
-		// Append preview
+		// Append preview.
 		$( 'div.wrap' ).append( preview.el );
 
 		// Listen to our preview object
@@ -542,7 +557,7 @@
 			// Keep track of current theme model.
 			current = self.model;
 
-			// Bail early if we are at the beginning of the collection
+			// Bail early if we are at the beginning of the collection.
 			if ( self.model.collection.indexOf( self.current ) === 0 ) {
 				return;
 			}
@@ -574,14 +589,14 @@
 
 	},
 
-	// Handles .disabled classes for previous/next buttons in theme installer preview
+	// Handles .disabled classes for previous/next buttons in theme installer preview.
 	setNavButtonsState: function() {
 		var $themeInstaller = $( '.theme-install-overlay' ),
 			current = _.isUndefined( this.current ) ? this.model : this.current,
 			previousThemeButton = $themeInstaller.find( '.previous-theme' ),
 			nextThemeButton = $themeInstaller.find( '.next-theme' );
 
-		// Disable previous at the zero position
+		// Disable previous at the zero position.
 		if ( 0 === this.model.collection.indexOf( current ) ) {
 			previousThemeButton
 				.addClass( 'disabled' )
@@ -590,7 +605,7 @@
 			nextThemeButton.focus();
 		}
 
-		// Disable next if the next model is undefined
+		// Disable next if the next model is undefined.
 		if ( _.isUndefined( this.model.collection.at( this.model.collection.indexOf( current ) + 1 ) ) ) {
 			nextThemeButton
 				.addClass( 'disabled' )
@@ -646,11 +661,11 @@
 	}
 });
 
-// Theme Details view
-// Set ups a modal overlay with the expanded theme data
+// Theme Details view.
+// Sets up a modal overlay with the expanded theme data.
 themes.view.Details = wp.Backbone.View.extend({
 
-	// Wrap theme data on a div.theme element
+	// Wrap theme data on a div.theme element.
 	className: 'theme-overlay',
 
 	events: {
@@ -658,29 +673,30 @@
 		'click .delete-theme': 'deleteTheme',
 		'click .left': 'previousTheme',
 		'click .right': 'nextTheme',
-		'click #update-theme': 'updateTheme'
+		'click #update-theme': 'updateTheme',
+		'click .toggle-auto-update': 'autoupdateState'
 	},
 
-	// The HTML template for the theme overlay
+	// The HTML template for the theme overlay.
 	html: themes.template( 'theme-single' ),
 
 	render: function() {
 		var data = this.model.toJSON();
 		this.$el.html( this.html( data ) );
-		// Renders active theme styles
+		// Renders active theme styles.
 		this.activeTheme();
-		// Set up navigation events
+		// Set up navigation events.
 		this.navigation();
-		// Checks screenshot size
+		// Checks screenshot size.
 		this.screenshotCheck( this.$el );
-		// Contain "tabbing" inside the overlay
+		// Contain "tabbing" inside the overlay.
 		this.containFocus( this.$el );
 	},
 
 	// Adds a class to the currently active theme
-	// and to the overlay in detailed view mode
+	// and to the overlay in detailed view mode.
 	activeTheme: function() {
-		// Check the model has the active property
+		// Check the model has the active property.
 		this.$el.toggleClass( 'active', this.model.get( 'active' ) );
 	},
 
@@ -710,44 +726,43 @@
 		});
 	},
 
-	// Single theme overlay screen
-	// It's shown when clicking a theme
+	// Single theme overlay screen.
+	// It's shown when clicking a theme.
 	collapse: function( event ) {
 		var self = this,
 			scroll;
 
 		event = event || window.event;
 
-		// Prevent collapsing detailed view when there is only one theme available
+		// Prevent collapsing detailed view when there is only one theme available.
 		if ( themes.data.themes.length === 1 ) {
 			return;
 		}
 
-		// Detect if the click is inside the overlay
-		// and don't close it unless the target was
-		// the div.back button
+		// Detect if the click is inside the overlay and don't close it
+		// unless the target was the div.back button.
 		if ( $( event.target ).is( '.theme-backdrop' ) || $( event.target ).is( '.close' ) || event.keyCode === 27 ) {
 
-			// Add a temporary closing class while overlay fades out
+			// Add a temporary closing class while overlay fades out.
 			$( 'body' ).addClass( 'closing-overlay' );
 
-			// With a quick fade out animation
+			// With a quick fade out animation.
 			this.$el.fadeOut( 130, function() {
-				// Clicking outside the modal box closes the overlay
+				// Clicking outside the modal box closes the overlay.
 				$( 'body' ).removeClass( 'closing-overlay' );
-				// Handle event cleanup
+				// Handle event cleanup.
 				self.closeOverlay();
 
-				// Get scroll position to avoid jumping to the top
+				// Get scroll position to avoid jumping to the top.
 				scroll = document.body.scrollTop;
 
-				// Clean the url structure
+				// Clean the URL structure.
 				themes.router.navigate( themes.router.baseUrl( '' ) );
 
-				// Restore scroll position
+				// Restore scroll position.
 				document.body.scrollTop = scroll;
 
-				// Return focus to the theme div
+				// Return focus to the theme div.
 				if ( themes.focusedTheme ) {
 					themes.focusedTheme.focus();
 				}
@@ -755,10 +770,10 @@
 		}
 	},
 
-	// Handles .disabled classes for next/previous buttons
+	// Handles .disabled classes for next/previous buttons.
 	navigation: function() {
 
-		// Disable Left/Right when at the start or end of the collection
+		// Disable Left/Right when at the start or end of the collection.
 		if ( this.model.cid === this.model.collection.at(0).cid ) {
 			this.$el.find( '.left' )
 				.addClass( 'disabled' )
@@ -772,7 +787,7 @@
 	},
 
 	// Performs the actions to effectively close
-	// the theme details overlay
+	// the theme details overlay.
 	closeOverlay: function() {
 		$( 'body' ).removeClass( 'modal-open' );
 		this.remove();
@@ -780,6 +795,26 @@
 		this.trigger( 'theme:collapse' );
 	},
 
+	// Set state of the auto-update settings link after it has been changed and saved.
+	autoupdateState: function() {
+		var callback,
+			_this = this;
+
+		// Support concurrent clicks in different Theme Details overlays.
+		callback = function( event, data ) {
+			var autoupdate;
+			if ( _this.model.get( 'id' ) === data.asset ) {
+				autoupdate = _this.model.get( 'autoupdate' );
+				autoupdate.enabled = 'enable' === data.state;
+				_this.model.set( { autoupdate: autoupdate } );
+				$( document ).off( 'wp-auto-update-setting-changed', callback );
+			}
+		};
+
+		// Triggered in updates.js
+		$( document ).on( 'wp-auto-update-setting-changed', callback );
+	},
+
 	updateTheme: function( event ) {
 		var _this = this;
 		event.preventDefault();
@@ -845,7 +880,7 @@
 	},
 
 	// Checks if the theme screenshot is the old 300px width version
-	// and adds a corresponding class if it's true
+	// and adds a corresponding class if it's true.
 	screenshotCheck: function( el ) {
 		var screenshot, image;
 
@@ -853,15 +888,15 @@
 		image = new Image();
 		image.src = screenshot.attr( 'src' );
 
-		// Width check
+		// Width check.
 		if ( image.width && image.width <= 300 ) {
 			el.addClass( 'small-screenshot' );
 		}
 	}
 });
 
-// Theme Preview view
-// Set ups a modal overlay with the expanded theme data
+// Theme Preview view.
+// Sets up a modal overlay with the expanded theme data.
 themes.view.Preview = themes.view.Details.extend({
 
 	className: 'wp-full-overlay expanded',
@@ -877,7 +912,7 @@
 		'click .theme-install': 'installTheme'
 	},
 
-	// The HTML template for the theme preview
+	// The HTML template for the theme preview.
 	html: themes.template( 'theme-preview' ),
 
 	render: function() {
@@ -915,7 +950,7 @@
 		this.$el.fadeOut( 200, function() {
 			$( 'body' ).removeClass( 'theme-installer-active full-overlay-active' );
 
-			// Return focus to the theme div
+			// Return focus to the theme div.
 			if ( themes.focusedTheme ) {
 				themes.focusedTheme.focus();
 			}
@@ -970,17 +1005,17 @@
 	},
 
 	keyEvent: function( event ) {
-		// The escape key closes the preview
+		// The escape key closes the preview.
 		if ( event.keyCode === 27 ) {
 			this.undelegateEvents();
 			this.close();
 		}
-		// The right arrow key, next theme
+		// The right arrow key, next theme.
 		if ( event.keyCode === 39 ) {
 			_.once( this.nextTheme() );
 		}
 
-		// The left arrow key, previous theme
+		// The left arrow key, previous theme.
 		if ( event.keyCode === 37 ) {
 			this.previousTheme();
 		}
@@ -1008,32 +1043,32 @@
 });
 
 // Controls the rendering of div.themes,
-// a wrapper that will hold all the theme elements
+// a wrapper that will hold all the theme elements.
 themes.view.Themes = wp.Backbone.View.extend({
 
 	className: 'themes wp-clearfix',
 	$overlay: $( 'div.theme-overlay' ),
 
 	// Number to keep track of scroll position
-	// while in theme-overlay mode
+	// while in theme-overlay mode.
 	index: 0,
 
-	// The theme count element
+	// The theme count element.
 	count: $( '.wrap .theme-count' ),
 
-	// The live themes count
+	// The live themes count.
 	liveThemeCount: 0,
 
 	initialize: function( options ) {
 		var self = this;
 
-		// Set up parent
+		// Set up parent.
 		this.parent = options.parent;
 
-		// Set current view to [grid]
+		// Set current view to [grid].
 		this.setView( 'grid' );
 
-		// Move the active theme to the beginning of the collection
+		// Move the active theme to the beginning of the collection.
 		self.currentTheme();
 
 		// When the collection is updated by user input...
@@ -1079,17 +1114,17 @@
 				return;
 			}
 
-			// Pressing the right arrow key fires a theme:next event
+			// Pressing the right arrow key fires a theme:next event.
 			if ( event.keyCode === 39 ) {
 				self.overlay.nextTheme();
 			}
 
-			// Pressing the left arrow key fires a theme:previous event
+			// Pressing the left arrow key fires a theme:previous event.
 			if ( event.keyCode === 37 ) {
 				self.overlay.previousTheme();
 			}
 
-			// Pressing the escape key fires a theme:collapse event
+			// Pressing the escape key fires a theme:collapse event.
 			if ( event.keyCode === 27 ) {
 				self.overlay.collapse( event );
 			}
@@ -1097,35 +1132,33 @@
 	},
 
 	// Manages rendering of theme pages
-	// and keeping theme count in sync
+	// and keeping theme count in sync.
 	render: function() {
-		// Clear the DOM, please
+		// Clear the DOM, please.
 		this.$el.empty();
 
-		// If the user doesn't have switch capabilities
-		// or there is only one theme in the collection
-		// render the detailed view of the active theme
+		// If the user doesn't have switch capabilities or there is only one theme
+		// in the collection, render the detailed view of the active theme.
 		if ( themes.data.themes.length === 1 ) {
 
-			// Constructs the view
+			// Constructs the view.
 			this.singleTheme = new themes.view.Details({
 				model: this.collection.models[0]
 			});
 
-			// Render and apply a 'single-theme' class to our container
+			// Render and apply a 'single-theme' class to our container.
 			this.singleTheme.render();
 			this.$el.addClass( 'single-theme' );
 			this.$el.append( this.singleTheme.el );
 		}
 
-		// Generate the themes
-		// Using page instance
-		// While checking the collection has items
+		// Generate the themes using page instance
+		// while checking the collection has items.
 		if ( this.options.collection.size() > 0 ) {
 			this.renderThemes( this.parent.page );
 		}
 
-		// Display a live theme count for the collection
+		// Display a live theme count for the collection.
 		this.liveThemeCount = this.collection.count ? this.collection.count : this.collection.length;
 		this.count.text( this.liveThemeCount );
 
@@ -1139,25 +1172,25 @@
 	},
 
 	// Iterates through each instance of the collection
-	// and renders each theme module
+	// and renders each theme module.
 	renderThemes: function( page ) {
 		var self = this;
 
 		self.instance = self.collection.paginate( page );
 
-		// If we have no more themes bail
+		// If we have no more themes, bail.
 		if ( self.instance.size() === 0 ) {
 			// Fire a no-more-themes event.
 			this.parent.trigger( 'theme:end' );
 			return;
 		}
 
-		// Make sure the add-new stays at the end
+		// Make sure the add-new stays at the end.
 		if ( ! themes.isInstall && page >= 1 ) {
 			$( '.add-new-theme' ).remove();
 		}
 
-		// Loop through the themes and setup each theme view
+		// Loop through the themes and setup each theme view.
 		self.instance.each( function( theme ) {
 			self.theme = new themes.view.Theme({
 				model: theme,
@@ -1166,15 +1199,15 @@
 
 			// Render the views...
 			self.theme.render();
-			// and append them to div.themes
+			// ...and append them to div.themes.
 			self.$el.append( self.theme.el );
 
 			// Binds to theme:expand to show the modal box
-			// with the theme details
+			// with the theme details.
 			self.listenTo( self.theme, 'theme:expand', self.expand, self );
 		});
 
-		// 'Add new theme' element shown at the end of the grid
+		// 'Add new theme' element shown at the end of the grid.
 		if ( ! themes.isInstall && themes.data.settings.canInstall ) {
 			this.$el.append( '<div class="theme add-new-theme"><a href="' + themes.data.settings.installURI + '"><div class="theme-screenshot"><span></span></div><h2 class="theme-name">' + l10n.addNew + '</h2></a></div>' );
 		}
@@ -1182,41 +1215,41 @@
 		this.parent.page++;
 	},
 
-	// Grabs current theme and puts it at the beginning of the collection
+	// Grabs current theme and puts it at the beginning of the collection.
 	currentTheme: function() {
 		var self = this,
 			current;
 
 		current = self.collection.findWhere({ active: true });
 
-		// Move the active theme to the beginning of the collection
+		// Move the active theme to the beginning of the collection.
 		if ( current ) {
 			self.collection.remove( current );
 			self.collection.add( current, { at:0 } );
 		}
 	},
 
-	// Sets current view
+	// Sets current view.
 	setView: function( view ) {
 		return view;
 	},
 
-	// Renders the overlay with the ThemeDetails view
-	// Uses the current model data
+	// Renders the overlay with the ThemeDetails view.
+	// Uses the current model data.
 	expand: function( id ) {
 		var self = this, $card, $modal;
 
-		// Set the current theme model
+		// Set the current theme model.
 		this.model = self.collection.get( id );
 
-		// Trigger a route update for the current model
+		// Trigger a route update for the current model.
 		themes.router.navigate( themes.router.baseUrl( themes.router.themePath + this.model.id ) );
 
-		// Sets this.view to 'detail'
+		// Sets this.view to 'detail'.
 		this.setView( 'detail' );
 		$( 'body' ).addClass( 'modal-open' );
 
-		// Set up the theme details view
+		// Set up the theme details view.
 		this.overlay = new themes.view.Details({
 			model: self.model
 		});
@@ -1240,72 +1273,75 @@
 
 		this.$overlay.html( this.overlay.el );
 
-		// Bind to theme:next and theme:previous
-		// triggered by the arrow keys
-		//
-		// Keep track of the current model so we
-		// can infer an index position
+		// Bind to theme:next and theme:previous triggered by the arrow keys.
+		// Keep track of the current model so we can infer an index position.
 		this.listenTo( this.overlay, 'theme:next', function() {
-			// Renders the next theme on the overlay
+			// Renders the next theme on the overlay.
 			self.next( [ self.model.cid ] );
 
 		})
 		.listenTo( this.overlay, 'theme:previous', function() {
-			// Renders the previous theme on the overlay
+			// Renders the previous theme on the overlay.
 			self.previous( [ self.model.cid ] );
 		});
 	},
 
-	// This method renders the next theme on the overlay modal
-	// based on the current position in the collection
-	// @params [model cid]
+	/*
+	 * This method renders the next theme on the overlay modal
+	 * based on the current position in the collection.
+	 *
+	 * @params [model cid]
+	 */
 	next: function( args ) {
 		var self = this,
 			model, nextModel;
 
-		// Get the current theme
+		// Get the current theme.
 		model = self.collection.get( args[0] );
-		// Find the next model within the collection
+		// Find the next model within the collection.
 		nextModel = self.collection.at( self.collection.indexOf( model ) + 1 );
 
-		// Sanity check which also serves as a boundary test
+		// Sanity check which also serves as a boundary test.
 		if ( nextModel !== undefined ) {
 
 			// We have a new theme...
-			// Close the overlay
+			// Close the overlay.
 			this.overlay.closeOverlay();
 
-			// Trigger a route update for the current model
+			// Trigger a route update for the current model.
 			self.theme.trigger( 'theme:expand', nextModel.cid );
 
 		}
 	},
 
-	// This method renders the previous theme on the overlay modal
-	// based on the current position in the collection
-	// @params [model cid]
+	/*
+	 * This method renders the previous theme on the overlay modal
+	 * based on the current position in the collection.
+	 *
+	 * @params [model cid]
+	 */
 	previous: function( args ) {
 		var self = this,
 			model, previousModel;
 
-		// Get the current theme
+		// Get the current theme.
 		model = self.collection.get( args[0] );
-		// Find the previous model within the collection
+		// Find the previous model within the collection.
 		previousModel = self.collection.at( self.collection.indexOf( model ) - 1 );
 
 		if ( previousModel !== undefined ) {
 
 			// We have a new theme...
-			// Close the overlay
+			// Close the overlay.
 			this.overlay.closeOverlay();
 
-			// Trigger a route update for the current model
+			// Trigger a route update for the current model.
 			self.theme.trigger( 'theme:expand', previousModel.cid );
 
 		}
 	},
 
-	// Dispatch audible search results feedback message
+	// Dispatch audible search results feedback message.
 	announceSearchResults: function( count ) {
 		if ( 0 === count ) {
 			wp.a11y.speak( l10n.noThemesFound );
@@ -1361,14 +1397,14 @@
 
 		this.collection.doSearch( event.target.value.replace( /\+/g, ' ' ) );
 
-		// if search is initiated and key is not return
+		// if search is initiated and key is not return.
 		if ( this.searching && event.which !== 13 ) {
 			options.replace = true;
 		} else {
 			this.searching = true;
 		}
 
-		// Update the URL hash
+		// Update the URL hash.
 		if ( event.target.value ) {
 			themes.router.navigate( themes.router.baseUrl( themes.router.searchPath + event.target.value ), options );
 		} else {
@@ -1395,8 +1431,8 @@
  * @since 4.9.0
  *
  * @param {string} url - URL to navigate to.
- * @param {object} state - State.
- * @returns {void}
+ * @param {Object} state - State.
+ * @return {void}
  */
 function navigateRouter( url, state ) {
 	var router = this;
@@ -1405,8 +1441,8 @@
 	}
 }
 
-// Sets up the routes events for relevant url queries
-// Listens to [theme] and [search] params
+// Sets up the routes events for relevant url queries.
+// Listens to [theme] and [search] params.
 themes.Router = Backbone.Router.extend({
 
 	routes: {
@@ -1436,14 +1472,14 @@
 
 });
 
-// Execute and setup the application
+// Execute and setup the application.
 themes.Run = {
 	init: function() {
-		// Initializes the blog's theme library view
-		// Create a new collection with data
+		// Initializes the blog's theme library view.
+		// Create a new collection with data.
 		this.themes = new themes.Collection( themes.data.themes );
 
-		// Set up the view
+		// Set up the view.
 		this.view = new themes.view.Appearance({
 			collection: this.themes
 		});
@@ -1456,7 +1492,7 @@
 
 	render: function() {
 
-		// Render results
+		// Render results.
 		this.view.render();
 		this.routes();
 
@@ -1473,10 +1509,10 @@
 	routes: function() {
 		var self = this;
 		// Bind to our global thx object
-		// so that the object is available to sub-views
+		// so that the object is available to sub-views.
 		themes.router = new themes.Router();
 
-		// Handles theme details route event
+		// Handles theme details route event.
 		themes.router.on( 'route:theme', function( slug ) {
 			self.view.view.expand( slug );
 		});
@@ -1486,7 +1522,7 @@
 			self.view.trigger( 'theme:close' );
 		});
 
-		// Handles search route event
+		// Handles search route event.
 		themes.router.on( 'route:search', function() {
 			$( '.wp-filter-search' ).trigger( 'keyup' );
 		});
@@ -1499,7 +1535,7 @@
 	}
 };
 
-// Extend the main Search view
+// Extend the main Search view.
 themes.view.InstallerSearch =  themes.view.Search.extend({
 
 	events: {
@@ -1509,10 +1545,10 @@
 
 	terms: '',
 
-	// Handles Ajax request for searching through themes in public repo
+	// Handles Ajax request for searching through themes in public repo.
 	search: function( event ) {
 
-		// Tabbing or reverse tabbing into the search input shouldn't trigger a search
+		// Tabbing or reverse tabbing into the search input shouldn't trigger a search.
 		if ( event.type === 'keyup' && ( event.which === 9 || event.which === 16 ) ) {
 			return;
 		}
@@ -1540,19 +1576,23 @@
 
 		request.search = value;
 
-		// Intercept an [author] search.
-		//
-		// If input value starts with `author:` send a request
-		// for `author` instead of a regular `search`
+		/*
+		 * Intercept an [author] search.
+		 *
+		 * If input value starts with `author:` send a request
+		 * for `author` instead of a regular `search`.
+		 */
 		if ( value.substring( 0, 7 ) === 'author:' ) {
 			request.search = '';
 			request.author = value.slice( 7 );
 		}
 
-		// Intercept a [tag] search.
-		//
-		// If input value starts with `tag:` send a request
-		// for `tag` instead of a regular `search`
+		/*
+		 * Intercept a [tag] search.
+		 *
+		 * If input value starts with `tag:` send a request
+		 * for `tag` instead of a regular `search`.
+		 */
 		if ( value.substring( 0, 4 ) === 'tag:' ) {
 			request.search = '';
 			request.tag = [ value.slice( 4 ) ];
@@ -1566,10 +1606,10 @@
 		$( '.drawer-toggle' ).attr( 'aria-expanded', 'false' );
 
 		// Get the themes by sending Ajax POST request to api.wordpress.org/themes
-		// or searching the local cache
+		// or searching the local cache.
 		this.collection.query( request );
 
-		// Set route
+		// Set route.
 		themes.router.navigate( themes.router.baseUrl( themes.router.searchPath + encodeURIComponent( value ) ), { replace: true } );
 	}
 });
@@ -1578,7 +1618,7 @@
 
 	el: '#wpbody-content .wrap',
 
-	// Register events for sorting and filters in theme-navigation
+	// Register events for sorting and filters in theme-navigation.
 	events: {
 		'click .filter-links li > a': 'onSort',
 		'click .theme-filter': 'onFilter',
@@ -1591,7 +1631,7 @@
 		'keyup #wporg-username-input': 'saveUsername'
 	},
 
-	// Initial render method
+	// Initial render method.
 	render: function() {
 		var self = this;
 
@@ -1603,7 +1643,7 @@
 		// Bump `collection.currentQuery.page` and request more themes if we hit the end of the page.
 		this.listenTo( this, 'theme:end', function() {
 
-			// Make sure we are not already loading
+			// Make sure we are not already loading.
 			if ( self.collection.loadingThemes ) {
 				return;
 			}
@@ -1636,29 +1676,29 @@
 			this.view.remove();
 		}
 
-		// Set ups the view and passes the section argument
+		// Sets up the view and passes the section argument.
 		this.view = new themes.view.Themes({
 			collection: this.collection,
 			parent: this
 		});
 
-		// Reset pagination every time the install view handler is run
+		// Reset pagination every time the install view handler is run.
 		this.page = 0;
 
-		// Render and append
+		// Render and append.
 		this.$el.find( '.themes' ).remove();
 		this.view.render();
 		this.$el.find( '.theme-browser' ).append( this.view.el ).addClass( 'rendered' );
 	},
 
-	// Handles all the rendering of the public theme directory
+	// Handles all the rendering of the public theme directory.
 	browse: function( section ) {
 		// Create a new collection with the proper theme data
-		// for each section
+		// for each section.
 		this.collection.query( { browse: section } );
 	},
 
-	// Sorting navigation
+	// Sorting navigation.
 	onSort: function( event ) {
 		var $el = $( event.target ),
 			sort = $el.data( 'sort' );
@@ -1668,14 +1708,14 @@
 		$( 'body' ).removeClass( 'filters-applied show-filters' );
 		$( '.drawer-toggle' ).attr( 'aria-expanded', 'false' );
 
-		// Bail if this is already active
+		// Bail if this is already active.
 		if ( $el.hasClass( this.activeClass ) ) {
 			return;
 		}
 
 		this.sort( sort );
 
-		// Trigger a router.naviagte update
+		// Trigger a router.navigate update.
 		themes.router.navigate( themes.router.baseUrl( themes.router.browsePath + sort ) );
 	},
 
@@ -1702,13 +1742,13 @@
 		this.browse( sort );
 	},
 
-	// Filters and Tags
+	// Filters and Tags.
 	onFilter: function( event ) {
 		var request,
 			$el = $( event.target ),
 			filter = $el.data( 'filter' );
 
-		// Bail if this is already active
+		// Bail if this is already active.
 		if ( $el.hasClass( this.activeClass ) ) {
 			return;
 		}
@@ -1725,21 +1765,21 @@
 		}
 
 		// Construct the filter request
-		// using the default values
+		// using the default values.
 		filter = _.union( [ filter, this.filtersChecked() ] );
 		request = { tag: [ filter ] };
 
 		// Get the themes by sending Ajax POST request to api.wordpress.org/themes
-		// or searching the local cache
+		// or searching the local cache.
 		this.collection.query( request );
 	},
 
-	// Clicking on a checkbox to add another filter to the request
+	// Clicking on a checkbox to add another filter to the request.
 	addFilter: function() {
 		this.filtersChecked();
 	},
 
-	// Applying filters triggers a tag request
+	// Applying filters triggers a tag request.
 	applyFilters: function( event ) {
 		var name,
 			tags = this.filtersChecked(),
@@ -1768,7 +1808,7 @@
 		});
 
 		// Get the themes by sending Ajax POST request to api.wordpress.org/themes
-		// or searching the local cache
+		// or searching the local cache.
 		this.collection.query( request );
 	},
 
@@ -1783,7 +1823,7 @@
 			event.preventDefault();
 		}
 
-		// save username on enter
+		// Save username on enter.
 		if ( event.type === 'keyup' && event.which !== 13 ) {
 			return;
 		}
@@ -1795,14 +1835,17 @@
 			},
 			success: function () {
 				// Get the themes by sending Ajax POST request to api.wordpress.org/themes
-				// or searching the local cache
+				// or searching the local cache.
 				that.collection.query( request );
 			}
 		} );
 	},
 
-	// Get the checked filters
-	// @return {array} of tags or false
+	/**
+	 * Get the checked filters.
+	 *
+	 * @return {Array} of tags or false
+	 */
 	filtersChecked: function() {
 		var items = $( '.filter-group' ).find( ':checkbox' ),
 			tags = [];
@@ -1811,7 +1854,7 @@
 			tags.push( $( item ).prop( 'value' ) );
 		});
 
-		// When no filters are checked, restore initial state and return
+		// When no filters are checked, restore initial state and return.
 		if ( tags.length === 0 ) {
 			$( '.filter-drawer .apply-filters' ).find( 'span' ).text( '' );
 			$( '.filter-drawer .clear-filters' ).hide();
@@ -1827,7 +1870,7 @@
 
 	activeClass: 'current',
 
-	/*
+	/**
 	 * When users press the "Upload Theme" button, show the upload form in place.
 	 */
 	uploader: function() {
@@ -1842,7 +1885,7 @@
 		});
 	},
 
-	// Toggle the full filters navigation
+	// Toggle the full filters navigation.
 	moreFilters: function( event ) {
 		var $body = $( 'body' ),
 			$toggleButton = $( '.drawer-toggle' );
@@ -1862,8 +1905,11 @@
 		$toggleButton.attr( 'aria-expanded', $body.hasClass( 'show-filters' ) );
 	},
 
-	// Clears all the checked filters
-	// @uses filtersChecked()
+	/**
+	 * Clears all the checked filters.
+	 *
+	 * @uses filtersChecked()
+	 */
 	clearFilters: function( event ) {
 		var items = $( '.filter-group' ).find( ':checkbox' ),
 			self = this;
@@ -1916,14 +1962,14 @@
 themes.RunInstaller = {
 
 	init: function() {
-		// Set up the view
-		// Passes the default 'section' as an option
+		// Set up the view.
+		// Passes the default 'section' as an option.
 		this.view = new themes.view.Installer({
 			section: 'featured',
 			SearchView: themes.view.InstallerSearch
 		});
 
-		// Render results
+		// Render results.
 		this.render();
 
 		// Start debouncing user searches after Backbone.history.start().
@@ -1932,7 +1978,7 @@
 
 	render: function() {
 
-		// Render results
+		// Render results.
 		this.view.render();
 		this.routes();
 
@@ -1951,11 +1997,11 @@
 			request = {};
 
 		// Bind to our global `wp.themes` object
-		// so that the router is available to sub-views
+		// so that the router is available to sub-views.
 		themes.router = new themes.InstallerRouter();
 
-		// Handles `theme` route event
-		// Queries the API for the passed theme slug
+		// Handles `theme` route event.
+		// Queries the API for the passed theme slug.
 		themes.router.on( 'route:preview', function( slug ) {
 
 			// Remove existing handlers.
@@ -1983,9 +2029,11 @@
 			}
 		});
 
-		// Handles sorting / browsing routes
-		// Also handles the root URL triggering a sort request
-		// for `featured`, the default view
+		/*
+		 * Handles sorting / browsing routes.
+		 * Also handles the root URL triggering a sort request
+		 * for `featured`, the default view.
+		 */
 		themes.router.on( 'route:sort', function( sort ) {
 			if ( ! sort ) {
 				sort = 'featured';
@@ -2040,7 +2088,7 @@
 
 })( jQuery );
 
-// Align theme browser thickbox
+// Align theme browser thickbox.
 jQuery(document).ready( function($) {
 	window.tb_position = function() {
 		var tbWindow = $('#TB_window'),