diff -r 3d4e9c994f10 -r a86126ab1dd4 wp/wp-admin/js/theme.js --- 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( '' ) ) @@ -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( '

' + l10n.addNew + '

' ); } @@ -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'),