jenkins-bot has submitted this change and it was merged. Change subject: TUX refactoring for performance ......................................................................
TUX refactoring for performance ApiQueryMessageGroups: root parameter can now be used together with format=flat and then it only returns that group. JavaScript changes: mw.translate.loadMessageGroups has been removed. mw.translate.getMessageGroup now takes a second parameter for props to load. mw.translate.getGroup has been renamed to mw.translate.findGroup and the second parameter is no longer optional. It is also now in the base module ext.translate.base. Module ext.translate.groupselector was refactored to load group data itself to improve performance. It's still loading the priority* properties which are among the slowest properties to load currently. Module ext.translate.special.translate was refactored to load data only for the currently selected group to improve performance. https://mingle.corp.wikimedia.org/projects/internationalization/cards/3854 Bug: 53748 Change-Id: I2f83cd0666386745820b0fd665c2cba703bbd560 --- M Resources.php M api/ApiQueryMessageGroups.php M resources/js/ext.translate.base.js M resources/js/ext.translate.groupselector.js M resources/js/ext.translate.special.searchtranslations.js M resources/js/ext.translate.special.translate.js M resources/js/ext.translate.workflowselector.js M specials/SpecialSearchTranslations.php 8 files changed, 211 insertions(+), 210 deletions(-) Approvals: Santhosh: Looks good to me, approved jenkins-bot: Verified diff --git a/Resources.php b/Resources.php index 6fc6650..fff380a 100644 --- a/Resources.php +++ b/Resources.php @@ -315,6 +315,7 @@ 'ext.uls.mediawiki', 'ext.uls.geoclient', 'ext.translate.groupselector', + 'mediawiki.Uri', ), 'messages' => array( 'translate-documentation-language', diff --git a/api/ApiQueryMessageGroups.php b/api/ApiQueryMessageGroups.php index 4fe23ac..8003cad 100644 --- a/api/ApiQueryMessageGroups.php +++ b/api/ApiQueryMessageGroups.php @@ -27,9 +27,16 @@ $groups = array(); if ( $params['format'] === 'flat' ) { - $groups = MessageGroups::getAllGroups(); - foreach ( MessageGroups::getDynamicGroups() as $id => $unused ) { - $groups[$id] = MessageGroups::getGroup( $id ); + if ( $params['root'] !== '' ) { + $group = MessageGroups::getGroup( $params['root'] ); + if ( $group ) { + $groups[$params['root']] = $group; + } + } else { + $groups = MessageGroups::getAllGroups(); + foreach ( MessageGroups::getDynamicGroups() as $id => $unused ) { + $groups[$id] = MessageGroups::getGroup( $id ); + } } // Not sorted by default, so do it now @@ -276,7 +283,8 @@ TEXT; $root = <<<TEXT When using the tree format, instead of starting from top level start from the -given message group, which must be an aggregate message group. +given message group, which must be an aggregate message group. When using flat +format only the specified group is returned. TEXT; $filter = <<<TEXT Only return messages with IDs that match one or more of the input(s) given diff --git a/resources/js/ext.translate.base.js b/resources/js/ext.translate.base.js index 71cd409..e1a3dc9 100644 --- a/resources/js/ext.translate.base.js +++ b/resources/js/ext.translate.base.js @@ -22,9 +22,7 @@ // Storage for language stats loader functions from API, // indexed by language code languageStatsLoader: {}, - messageGroupsLoader: null, - messageGroups: {}, /** * Get language stats for a language from the API. * @param {string} language Language code. @@ -48,45 +46,73 @@ }, /** - * Loads information about all message groups. Use getMessageGroup - * instead. + * Load message group information asynchronously. * - * @return {jQuery.Deferred} + * @param {string} id Message group id + * @param {string|array} [props] List of properties to load + * @return {jQuery.Promise} Object containing the requested properties on success. */ - loadMessageGroups: function () { - if ( mw.translate.messageGroupsLoader ) { - return mw.translate.messageGroupsLoader; + getMessageGroup: function ( id, props ) { + var params, + deferred = new $.Deferred(); + + if ( $.isArray( props ) ) { + props = props.join( '|' ); + } else if ( props === undefined ) { + props = 'id|label|description|icon|priority|prioritylangs|priorityforce|workflowstates'; } - var loader, - queryParams = { - action: 'query', - format: 'json', - meta: 'messagegroups', - mgformat: 'tree', - mgprop: 'id|label|description|icon|priority|prioritylangs|priorityforce|workflowstates', - mgiconsize: '32' - }; - loader = new mw.Api().get( queryParams ); - loader.done( function ( result ) { - mw.translate.messageGroups = result.query.messagegroups; - } ); + params = { + action: 'query', + format: 'json', + meta: 'messagegroups', + mgformat: 'flat', + mgprop: props, + mgroot: id, + }; - mw.translate.messageGroupsLoader = loader; - return loader; + new mw.Api() + .get( params ) + .done( function ( result ) { + deferred.resolve( result.query.messagegroups[0] ); + } ) + .fail( deferred.reject ); + + return deferred.promise(); }, /** - * Load message group information asynchronously. - * @param {string} id Message group id - * @return {jQuery.Deferred} + * Find a group from an array of message groups as returned by web api + * and recurse it through sub groups. + * + * @param {string} id Group id to search for. + * @param {Array} groups Array of message grous + * @return {Object} Message group object */ - getMessageGroup: function ( id ) { - var deferred = new $.Deferred(); - mw.translate.loadMessageGroups().done( function () { - deferred.resolve( mw.translate.getGroup( id, mw.translate.messageGroups ) ); + findGroup: function ( id, groups ) { + var result = null; + + if ( !id ) { + return groups; + } + + $.each( groups, function ( index, group ) { + if ( group.id === id ) { + result = group; + return; + } + + if ( group.groups ) { + group = mw.translate.findGroup( id, group.groups ); + + if ( group ) { + result = group; + return; + } + } } ); - return deferred; + + return result; }, /** diff --git a/resources/js/ext.translate.groupselector.js b/resources/js/ext.translate.groupselector.js index 22567a4..cd7d83c 100644 --- a/resources/js/ext.translate.groupselector.js +++ b/resources/js/ext.translate.groupselector.js @@ -1,6 +1,8 @@ ( function ( $, mw ) { 'use strict'; + var groupLoader, delay; + /** * options * - position: accepts same values as jquery.ui.position @@ -26,14 +28,8 @@ */ init: function () { this.parentGroupId = this.$trigger.data( 'msggroupid' ); - if ( this.hasChildGroups( this.parentGroupId ) ) { - this.prepareSelectorMenu(); - this.listen(); - if ( mw.translate.messageGroups !== {} ) { - // If data is ready, render now. - this.$trigger.trigger( 'dataready.translate' ); - } - } + this.prepareSelectorMenu(); + this.listen(); }, /** @@ -113,6 +109,23 @@ this.position(); // Keep the focus in the message group search box. this.$menu.find( '.ext-translate-msggroup-search-input' ).focus(); + this.showDefaultGroups(); + }, + + showDefaultGroups: function () { + var self = this; + + this.$menu.find( '.ext-translate-msggroup-list' ).empty(); + this.loadGroups().done( function( groups ) { + var groupsToShow = mw.translate.findGroup( self.parentGroupId, groups ); + + // We do not want to display the group itself, only its subgroups + if ( self.parentGroupId ) { + groupsToShow = groupsToShow.groups; + } + + self.addGroupRows( groupsToShow ); + } ); }, /** @@ -144,12 +157,6 @@ // Hide the selector panel when clicking outside of it $( 'html' ).on( 'click', function () { groupSelector.hide(); - } ); - - groupSelector.$trigger.on( 'dataready.translate', function () { - if ( groupSelector.hasChildGroups( groupSelector.parentGroupId ) ) { - groupSelector.loadGroups( groupSelector.parentGroupId ); - } } ); groupSelector.$trigger.on( 'click', function ( e ) { @@ -216,8 +223,7 @@ if ( $this.hasClass( 'recent' ) ) { groupSelector.getRecentGroups(); } else { - groupSelector.$menu.find( '.ext-translate-msggroup-list' ).empty(); - groupSelector.loadGroups( groupSelector.$trigger.data( 'msggroupid' ) ); + groupSelector.showDefaultGroups(); } } ); @@ -237,11 +243,10 @@ keyup: function () { var query, groupSelector = this, - $search; + $search = this.$menu.find( '.ext-translate-msggroup-search-input' ); // Respond to the keypress events after a small timeout to avoid freeze when typed fast. delay( function () { - $search = groupSelector.$menu.find( '.ext-translate-msggroup-search-input' ); query = $.trim( $search.val() ).toLowerCase(); groupSelector.filter( query ); }, 300 ); @@ -263,25 +268,26 @@ getRecentGroups: function () { var api = new mw.Api(), groupSelector = this, - messageGroups = this.$menu.data( 'msggroups' ), - $msgGroupList = this.$menu.find( '.ext-translate-msggroup-list' ), + $list = this.$menu.find( '.ext-translate-msggroup-list' ), recentMessageGroups = $( '.ext-translate-msggroup-selector' ) .data( 'recentmsggroups' ); - $msgGroupList.empty(); + $list.empty(); function addRecentMessageGroups( recentgroups ) { - var msgGroupRows = []; + var rows = []; - $.each( recentgroups, function ( index, messageGroupId ) { - var messagegroup = mw.translate.getGroup( messageGroupId, messageGroups ); + groupSelector.loadGroups().done( function( groups ) { + $.each( recentgroups, function ( index, id ) { + var messagegroup = mw.translate.findGroup( id, groups ); - if ( messagegroup ) { - msgGroupRows.push( groupSelector.prepareMessageGroupRow( messagegroup ) ); - } + if ( messagegroup ) { + rows.push( groupSelector.prepareMessageGroupRow( messagegroup ) ); + } + } ); + + $list.append( rows ); } ); - - $msgGroupList.append( msgGroupRows ); } if ( recentMessageGroups ) { @@ -332,116 +338,109 @@ * @param query */ filter: function ( query ) { - var index, - matcher, - parentGroupId, - messageGroups, - currentGroup, - foundGroups = []; - - this.$menu.find( '.ext-translate-msggroup-list' ).empty(); + var self = this; // Show the initial list if the query is empty/undefined/null if ( !query ) { - this.addGroupRows( this.parentGroupId, null ); - + this.showDefaultGroups(); return; } - if ( !this.flatGroupList ) { - this.flatGroupList = []; - parentGroupId = this.$trigger.data( 'msggroupid' ); - messageGroups = this.$menu.data( 'msggroups' ); + this.$menu.find( '.ext-translate-msggroup-list' ).empty(); - if ( parentGroupId ) { - currentGroup = mw.translate.getGroup( parentGroupId, messageGroups ).groups; - } else { - currentGroup = messageGroups; + this.loadGroups().done( function( groups ) { + var currentGroup, index, matcher, foundGroups = []; + + if ( !self.flatGroupList ) { + self.flatGroupList = []; + currentGroup = mw.translate.findGroup( self.parentGroupId, groups ); + if ( self.parentGroupId ) { + currentGroup = currentGroup.groups; + } + self.flattenGroupList( currentGroup, {} ); } - this.flattenGroupList( currentGroup, {} ); - } + // Optimization, assuming that people search the beginning + // of the group name. + matcher = new RegExp( '\\b' + escapeRegex( query ), 'i' ); - // Optimization, assuming that people search the beginning - // of the group name. - matcher = new RegExp( '\\b' + escapeRegex( query ), 'i' ); - - for ( index = 0; index < this.flatGroupList.length; index++ ) { - if ( matcher.test( this.flatGroupList[index].label ) || - query === this.flatGroupList[index].id ) { - foundGroups.push( this.flatGroupList[index] ); + for ( index = 0; index < self.flatGroupList.length; index++ ) { + if ( matcher.test( self.flatGroupList[index].label ) || + query === self.flatGroupList[index].id ) { + foundGroups.push( self.flatGroupList[index] ); + } } - } - this.addGroupRows( this.parentGroupId, foundGroups ); + self.addGroupRows( foundGroups ); + } ); }, /** - * Load message groups and relevant properties - * using the API and display the loaded groups - * in the group selector. + * Load message groups and relevant properties using the API. * - * @param parentGroupId */ - loadGroups: function ( parentGroupId ) { - this.$menu.data( 'msggroups', mw.translate.messageGroups ); - this.addGroupRows( parentGroupId, null ); + loadGroups: function () { + if ( groupLoader === undefined ) { + var params = { + action: 'query', + format: 'json', + meta: 'messagegroups', + mgformat: 'tree', + mgprop: 'id|label|icon|priority|prioritylangs|priorityforce', + mgiconsize: '32' + }; + + groupLoader = $.Deferred(); + new mw.Api() + .get( params ) + .done( function( result ) { + groupLoader.resolve( result.query.messagegroups ); + } ) + .fail( groupLoader.reject ); + } + + return groupLoader; }, - hasChildGroups: function ( groupId ) { - if ( !groupId ) { - return true; - } - var childGroups = mw.translate.getGroup( groupId, null ).groups; - return childGroups && childGroups.length; - }, /** * Add rows with message groups to the selector. * - * @param {string|null} parentGroupId. If it's null, all groups are loaded. Otherwise, groups under this id are loaded. - * @param {Array} msgGroups - array of message group objects to add. + * @param {Array} groups Array of message group objects to add. */ - addGroupRows: function ( parentGroupId, msgGroups ) { + addGroupRows: function ( groups ) { var groupSelector = this, $msgGroupRows, $parent, - messageGroups = this.$menu.data( 'msggroups' ), $msgGroupList = this.$menu.find( '.ext-translate-msggroup-list' ), targetLanguage = this.options.language; - if ( msgGroups ) { - messageGroups = msgGroups; - } else { - if ( parentGroupId ) { - messageGroups = mw.translate.getGroup( parentGroupId, messageGroups ).groups; - } - } + this.$menu.find( '.tux-loading-indicator' ).hide(); - if ( !messageGroups ) { + if ( !groups ) { return; } $msgGroupRows = []; - $.each( messageGroups, function ( index, messagegroup ) { + $.each( groups, function ( index, group ) { /* Hide from the selector: * - discouraged groups (the only priority value currently supported). * - groups that are recommended for other languages. */ - if ( messagegroup.priority === 'discouraged' || - ( messagegroup.priorityforce && - messagegroup.prioritylangs && - $.inArray( targetLanguage, messagegroup.prioritylangs ) === -1 ) + if ( group.priority === 'discouraged' || + ( group.priorityforce && + group.prioritylangs && + $.inArray( targetLanguage, group.prioritylangs ) === -1 ) ) { return; } - $msgGroupRows.push( groupSelector.prepareMessageGroupRow( messagegroup ) ); + $msgGroupRows.push( groupSelector.prepareMessageGroupRow( group ) ); } ); - if ( parentGroupId ) { + if ( groupSelector.parentGroupId ) { $parent = $msgGroupList.find( '.ext-translate-msggroup-item[data-msggroupid="' + - parentGroupId + '"]' ); + groupSelector.parentGroupId + '"]' ); if ( $parent.length ) { $parent.after( $msgGroupRows ); @@ -451,9 +450,7 @@ } else { $msgGroupList.append( $msgGroupRows ); } - if ( $msgGroupRows.length ) { - this.$menu.find( '.tux-loading-indicator' ).hide(); - } + }, /** @@ -575,42 +572,7 @@ return value.replace( /[\-\[\]{}()*+?.,\\\^$\|#\s]/g, '\\$&' ); } - mw.translate = mw.translate || {}; - - /** - * Find a group from an array of message groups - * recurse it through sub groups. - * - * @param {string} messageGroupId - * @param {Array|Object} [messageGroups] Array of messageGroups - * @return {Object|boolean} Messagegroup object - */ - mw.translate.getGroup = function ( messageGroupId, messageGroups ) { - var result = false; - - if ( !messageGroups ) { - messageGroups = mw.translate.messageGroups; - } - - $.each( messageGroups, function ( id, messageGroup ) { - if ( messageGroup.id === messageGroupId ) { - result = messageGroup; - return; - } - - if ( messageGroup.groups ) { - messageGroup = mw.translate.getGroup( messageGroupId, messageGroup.groups ); - - if ( messageGroup ) { - result = messageGroup; - } - } - } ); - - return result; - }; - - var delay = ( function () { + delay = ( function () { var timer = 0; return function ( callback, milliseconds ) { diff --git a/resources/js/ext.translate.special.searchtranslations.js b/resources/js/ext.translate.special.searchtranslations.js index ad53e80..7c37e60 100644 --- a/resources/js/ext.translate.special.searchtranslations.js +++ b/resources/js/ext.translate.special.searchtranslations.js @@ -1,8 +1,12 @@ ( function ( $, mw ) { 'use strict'; + var resultGroups; + $( document ).ready( function () { var $messages = $( '.tux-message' ); + + resultGroups = $( '.facet.groups' ).data( 'facets' ); // Make the whole rows clickable $( '.facet-item' ).click( function () { @@ -158,12 +162,13 @@ $groups = $( '.facet.groups' ); currentGroup = $groups.data( 'group' ); - mw.translate.messageGroups = $groups.data( 'facets' ); - if ( !mw.translate.messageGroups ) { - // at empty search screen + + if ( !resultGroups ) { + // No search results return; } - groupList = Object.keys( mw.translate.messageGroups ); + + groupList = Object.keys( resultGroups ); listGroups( groupList, currentGroup, $groups ); } @@ -174,13 +179,14 @@ group, groupId, $groupRow, + uri, maxListSize = 10, currentGroup = $( '.facet.groups' ).data( 'group' ), resultCount = groupList.length; level = level || 0; groupList = groupList.splice( 0, maxListSize ); - if ( currentGroup && mw.translate.messageGroups[currentGroup] && + if ( currentGroup && resultGroups[currentGroup] && $.inArray( currentGroup, groupList ) < 0 ) { // Make sure current selected group is displayed always. @@ -190,7 +196,7 @@ groupList.sort( sortGroups ); for ( i = 0; i <= groupList.length; i++ ) { groupId = groupList[i]; - group = mw.translate.getGroup( groupId ); + group = mw.translate.findGroup( groupId, resultGroups ); if ( !group ) { continue; } @@ -199,15 +205,16 @@ } else { selectedClass = ''; } + + uri = new mw.Uri( window.location.href ); + uri.extend( { 'group': groupId } ); + $groupRow = $( '<div>' ) .addClass( 'row facet-item ' + ' facet-level-' + level ) .append( $( '<span>' ) .addClass( 'facet-name ' + selectedClass) .append( $( '<a>' ) - .attr( { - href: group.url, - title: group.description - } ) + .attr( 'href', uri.toString() ) .text( group.label ) ), $( '<span>' ) @@ -240,15 +247,17 @@ at: 'left top' }, onSelect: function ( group ) { - window.location = group.url; + var uri = new mw.Uri( window.location.href ); + uri.extend( { 'group': group.id } ); + window.location.href = uri.toString(); } } ); } } - function sortGroups ( groupIdA, groupIdB ) { - var groupAName = mw.translate.getGroup( groupIdA ).label, - groupBName = mw.translate.getGroup( groupIdB ).label; + function sortGroups( groupIdA, groupIdB ) { + var groupAName = mw.translate.findGroup( groupIdA, resultGroups ).label, + groupBName = mw.translate.findGroup( groupIdB, resultGroups ).label; return groupAName.localeCompare( groupBName ); } diff --git a/resources/js/ext.translate.special.translate.js b/resources/js/ext.translate.special.translate.js index b11b318..e29d7d8 100644 --- a/resources/js/ext.translate.special.translate.js +++ b/resources/js/ext.translate.special.translate.js @@ -218,19 +218,28 @@ var preferredLanguages, $groupWarning = $( '.tux-editor-header .group-warning' ), targetLanguage = $( '.tux-messagelist' ).data( 'targetlangcode' ), - msgGroupData = mw.translate.getGroup( - $( '.tux-messagetable-loader' ).data( 'messagegroup' ) - ); + id = $( '.tux-messagetable-loader' ).data( 'messagegroup' ), + props = 'priority|prioritylangs|priorityforce'; - if ( msgGroupData.prioritylangs && - $.inArray( targetLanguage, msgGroupData.prioritylangs ) === -1 - ) { - preferredLanguages = $.map( msgGroupData.prioritylangs, function ( code ) { - return $.uls.data.getAutonym( code ); - } ); + $groupWarning.empty(); + + mw.translate.getMessageGroup( id, props ).done( function ( group ) { + + // Check whether group has priority languages + if ( !group.prioritylangs ) { + return; + } + + // And if current language is among them, we can return early + if ( $.inArray( targetLanguage, group.prioritylangs ) !== -1 ) { + return; + } + + // Format a nice warning for the user + preferredLanguages = $.map( group.prioritylangs, $.uls.data.getAutonym ); new mw.Api().parse( - mw.message( msgGroupData.priorityforce ? + mw.message( group.priorityforce ? 'tpt-discouraged-language-force' : 'tpt-discouraged-language', '', @@ -240,9 +249,7 @@ ).done( function ( parsedWarning ) { $groupWarning.html( parsedWarning ); } ); - } else { - $groupWarning.empty(); - } + } ); } $( document ).ready( function () { @@ -278,15 +285,7 @@ language: targetLanguage } ); - $.when( - // Get ready with language stats - mw.translate.loadLanguageStats( targetLanguage ), - // Get ready with message groups - mw.translate.loadMessageGroups() - ).then( function () { - $( '.ext-translate-msggroup-selector .grouplink' ).trigger( 'dataready.translate' ); - updateGroupWarning(); - } ); + updateGroupWarning(); $( '.tux-messagelist' ).messagetable(); // Use ULS for language selection if it's available diff --git a/resources/js/ext.translate.workflowselector.js b/resources/js/ext.translate.workflowselector.js index 04b08ab..cb6beb8 100644 --- a/resources/js/ext.translate.workflowselector.js +++ b/resources/js/ext.translate.workflowselector.js @@ -35,12 +35,11 @@ instance.groupId = groupId; instance.language = language; - mw.translate.getMessageGroup( groupId ).done( - function ( group ) { + mw.translate.getMessageGroup( groupId, 'workflowstates' ) + .done( function ( group ) { instance.states = group.workflowstates; instance.display(); - } - ); + } ); }, /** diff --git a/specials/SpecialSearchTranslations.php b/specials/SpecialSearchTranslations.php index d155eed..0aeffd7 100644 --- a/specials/SpecialSearchTranslations.php +++ b/specials/SpecialSearchTranslations.php @@ -319,10 +319,7 @@ $output[$id] = array( 'id' => $id, 'count' => $count, - 'url' => $url, 'label' => $group->getLabel(), - 'description' => $group->getDescription(), - 'icon' => TranslateUtils::getIcon( $group, 100 ), ); if ( isset( $path[$level] ) && $path[$level] === $id ) { -- To view, visit https://gerrit.wikimedia.org/r/97918 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I2f83cd0666386745820b0fd665c2cba703bbd560 Gerrit-PatchSet: 7 Gerrit-Project: mediawiki/extensions/Translate Gerrit-Branch: master Gerrit-Owner: Nikerabbit <niklas.laxst...@gmail.com> Gerrit-Reviewer: Ori.livneh <o...@wikimedia.org> Gerrit-Reviewer: Santhosh <santhosh.thottin...@gmail.com> Gerrit-Reviewer: Siebrand <siebr...@wikimedia.org> Gerrit-Reviewer: jenkins-bot _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits