From e6c4315d93f5f70dc5334d529a957eb42dfdf47a Mon Sep 17 00:00:00 2001 From: Alex Monk Date: Tue, 9 Sep 2014 20:49:27 +0100 Subject: [PATCH] Refactor a bunch of CategoryInputWidget code, tear out dead/broken redirect handling that should be rewritten Category redirect stuff is currently broken because of I5c536697, but no one seems to have noticed. Fix bug 69886 while we're here Bug: 69886 Change-Id: I62cf23d0e86bc43d739bb4c4610a5a852146fa3f --- .../ui/widgets/ve.ui.MWCategoryInputWidget.js | 250 +++++------------- 1 file changed, 73 insertions(+), 177 deletions(-) diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryInputWidget.js b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryInputWidget.js index 4649695550..14af21b65a 100644 --- a/modules/ve-mw/ui/widgets/ve.ui.MWCategoryInputWidget.js +++ b/modules/ve-mw/ui/widgets/ve.ui.MWCategoryInputWidget.js @@ -36,7 +36,6 @@ ve.ui.MWCategoryInputWidget = function VeUiMWCategoryInputWidget( categoryWidget // Initialization this.$element.addClass( 've-ui-mwCategoryInputWidget' ); this.lookupMenu.$element.addClass( 've-ui-mwCategoryInputWidget-menu' ); - this.categoryRedirects = []; }; /* Inheritance */ @@ -59,8 +58,7 @@ ve.ui.MWCategoryInputWidget.prototype.getLookupRequest = function () { list: 'allcategories', acmin: 1, acprefix: this.value, - acprop: 'hidden', - redirects: '' + acprop: 'hidden' } ); }; @@ -71,31 +69,14 @@ ve.ui.MWCategoryInputWidget.prototype.getLookupRequest = function () { * @param {Mixed} data Response from server */ ve.ui.MWCategoryInputWidget.prototype.getLookupCacheItemFromData = function ( data ) { - var categoryInputWidget = this, result = {}; - if ( data.query && data.query.allcategories ) { - $.each( data.query.allcategories, function () { - var title = mw.Title.newFromText( this['*'] ); - if ( title ) { - result[title.getMainText()] = this.hasOwnProperty( 'hidden' ); - categoryInputWidget.categoryWidget.categoryHiddenStatus[this['*']] = result[title.getMainText()]; - } else { - mw.log.warning( '"' + this['*'] + '" is an invalid title!' ); - } - } ); - } - if ( data.query && data.query.redirects ) { - $.each( data.query.redirects, function ( index, redirectInfo ) { - var foundIdentical = false; - $.each( categoryInputWidget.categoryRedirects, function ( index, existingRedirectInfo ) { - if ( existingRedirectInfo.from === redirectInfo.from && existingRedirectInfo.to === redirectInfo.to ) { - foundIdentical = true; - } - } ); - if ( !foundIdentical ) { - categoryInputWidget.categoryRedirects.push( redirectInfo ); - } - } ); - } + var result = []; + data.query = data.query || {}; + + $.each( data.query.allcategories || [], function ( index, category ) { + result.push( category['*'] ); + this.categoryWidget.categoryHiddenStatus[category['*']] = category.hasOwnProperty( 'hidden' ); + }.bind( this ) ); + return result; }; @@ -106,171 +87,86 @@ ve.ui.MWCategoryInputWidget.prototype.getLookupCacheItemFromData = function ( da * @returns {OO.ui.MenuItemWidget[]} Menu items */ ve.ui.MWCategoryInputWidget.prototype.getLookupMenuItemsFromData = function ( data ) { - var i, len, item, - exactMatch = false, - newCategoryItems = [], - existingCategoryItems = [], - matchingCategoryItems = [], - hiddenCategoryItems = [], - items = [], - menu$ = this.lookupMenu.$, + var exactMatch = false, + itemWidgets = [], + existingCategoryItems = [], matchingCategoryItems = [], + hiddenCategoryItems = [], newCategoryItems = [], category = this.getCategoryItemFromValue( this.value ), existingCategories = this.categoryWidget.getCategories(), - matchingCategories = [], - hiddenCategories = [], - redirectStorage = {}, - itemTitle, - searchForQueryWithinRedirectInfo = function ( element ) { - return element.lastIndexOf( new mw.Title( 'Category:' + category.value ).getPrefixedText(), 0 ) === 0; - }; + linkCacheUpdate = {}, + canonicalQueryValue = mw.Title.newFromText( this.value ); - $.each( this.categoryRedirects, function () { - if ( redirectStorage.hasOwnProperty( this.to ) && redirectStorage[this.to].indexOf( this.from ) === -1 ) { - redirectStorage[this.to].push( this.from ); - } else { - redirectStorage[this.to] = [this.from]; - } - } ); + if ( canonicalQueryValue ) { + canonicalQueryValue = canonicalQueryValue.getMainText(); + } // Invalid titles just end up with canonicalQueryValue being null. - $.each( data, function ( title, hiddenStatus ) { - if ( hiddenStatus ) { - hiddenCategories.push( title ); - } else { - matchingCategories.push( title ); + $.each( data, function ( index, suggestedCategory ) { + if ( canonicalQueryValue === suggestedCategory ) { + exactMatch = true; } - } ); + linkCacheUpdate['Category:' + suggestedCategory] = { missing: false }; + if ( ve.indexOf( suggestedCategory, existingCategories ) === -1 && suggestedCategory.lastIndexOf( canonicalQueryValue, 0 ) === 0 ) { + if ( this.categoryWidget.categoryHiddenStatus[suggestedCategory] ) { + hiddenCategoryItems.push( suggestedCategory ); + } else { + matchingCategoryItems.push( suggestedCategory ); + } + } + }.bind( this ) ); // Existing categories - // This is deliberately not checking the last existingCategories entry so we don't show it under - // "Move this category here" etc. That is done below. - for ( i = 0, len = existingCategories.length - 1; i < len; i++ ) { - item = existingCategories[i]; - // Verify that item starts with category.value - if ( item.lastIndexOf( category.value, 0 ) === 0 ) { - if ( item === category.value ) { - exactMatch = true; - } - existingCategoryItems.push( item ); + $.each( existingCategories, function ( index, existingCategory ) { + if ( existingCategory === canonicalQueryValue ) { + exactMatch = true; } - } - // Now check the last one. Don't add to existingCategoryItems but do make it a match - if ( existingCategories[existingCategories.length - 1] === category.value ) { - exactMatch = true; - } - - // Matching categories - for ( i = 0, len = matchingCategories.length; i < len; i++ ) { - item = matchingCategories[i]; - itemTitle = new mw.Title( 'Category:' + item ).getPrefixedText(); - if ( - ve.indexOf( item, existingCategories ) === -1 && - item.lastIndexOf( category.value, 0 ) === 0 || ( - redirectStorage[itemTitle] !== undefined && - $.grep( redirectStorage[itemTitle], searchForQueryWithinRedirectInfo ).length - ) - ) { - if ( ( item === category.value ) || ( - redirectStorage[itemTitle] !== undefined && - redirectStorage[itemTitle].indexOf( new mw.Title( 'Category:' + category.value ).getPrefixedText() ) !== -1 - ) ) { - exactMatch = true; - } - matchingCategoryItems.push( item ); + if ( index < existingCategories.length - 1 && existingCategory.lastIndexOf( canonicalQueryValue, 0 ) === 0 ) { + // Verify that item starts with category.value + existingCategoryItems.push( existingCategory ); } - } - // Hidden categories - for ( i = 0, len = hiddenCategories.length; i < len; i++ ) { - item = hiddenCategories[i]; - itemTitle = new mw.Title( 'Category:' + item ).getPrefixedText(); - if ( - ve.indexOf( item, existingCategories ) === -1 && - item.lastIndexOf( category.value, 0 ) === 0 || ( - redirectStorage[itemTitle] !== undefined && - $.grep( redirectStorage[itemTitle], searchForQueryWithinRedirectInfo ).length - ) - ) { - if ( ( item === category.value ) || ( - redirectStorage[itemTitle] !== undefined && - redirectStorage[itemTitle].indexOf( new mw.Title( 'Category:' + category.value ).getPrefixedText() ) !== -1 - ) ) { - exactMatch = true; - } - hiddenCategoryItems.push( item ); - } - } + } ); // New category if ( !exactMatch ) { - newCategoryItems.push( category.value ); + newCategoryItems.push( canonicalQueryValue ); + linkCacheUpdate['Category:' + category.value] = { missing: true }; } - // Add sections for non-empty groups - if ( newCategoryItems.length ) { - items.push( new OO.ui.MenuSectionItemWidget( - 'newCategory', { $: menu$, label: ve.msg( 'visualeditor-dialog-meta-categories-input-newcategorylabel' ) } - ) ); - for ( i = 0, len = newCategoryItems.length; i < len; i++ ) { - item = newCategoryItems[i]; - items.push( new OO.ui.MenuItemWidget( item, { $: menu$, label: item } ) ); - } - } - if ( existingCategoryItems.length ) { - items.push( new OO.ui.MenuSectionItemWidget( - 'inArticle', { $: menu$, label: ve.msg( 'visualeditor-dialog-meta-categories-input-movecategorylabel' ) } - ) ); - for ( i = 0, len = existingCategoryItems.length; i < len; i++ ) { - item = existingCategoryItems[i]; - items.push( new OO.ui.MenuItemWidget( item, { $: menu$, label: item } ) ); - } - } - if ( matchingCategoryItems.length ) { - items.push( new OO.ui.MenuSectionItemWidget( - 'matchingCategories', { $: menu$, label: ve.msg( 'visualeditor-dialog-meta-categories-input-matchingcategorieslabel' ) } - ) ); - for ( i = 0, len = matchingCategoryItems.length; i < len; i++ ) { - item = matchingCategoryItems[i]; - items.push( this.getMenuItemWidgetFromCategoryName( item, menu$ ) ); - } - } - if ( hiddenCategoryItems.length ) { - items.push( new OO.ui.MenuSectionItemWidget( - 'hiddenCategories', { $: menu$, label: ve.msg( 'visualeditor-dialog-meta-categories-input-hiddencategorieslabel' ) } - ) ); - for ( i = 0, len = hiddenCategoryItems.length; i < len; i++ ) { - item = hiddenCategoryItems[i]; - items.push( this.getMenuItemWidgetFromCategoryName( item, menu$ ) ); - } - } + ve.init.platform.linkCache.set( linkCacheUpdate ); - return items; -}; + // Add sections for non-empty groups. Each section consists of an id, a label and items + $.each( [ + { + id: 'newCategory', + label: ve.msg( 'visualeditor-dialog-meta-categories-input-newcategorylabel' ), + items: newCategoryItems + }, + { + id: 'inArticle', + label: ve.msg( 'visualeditor-dialog-meta-categories-input-movecategorylabel' ), + items: existingCategoryItems + }, + { + id: 'matchingCategories', + label: ve.msg( 'visualeditor-dialog-meta-categories-input-matchingcategorieslabel' ), + items: matchingCategoryItems + }, + { + id: 'hiddenCategories', + label: ve.msg( 'visualeditor-dialog-meta-categories-input-hiddencategorieslabel' ), + items: hiddenCategoryItems + } + ], function ( index, sectionData ) { + if ( sectionData.items.length ) { + itemWidgets.push( new OO.ui.MenuSectionItemWidget( + sectionData.id, { $: this.lookupMenu.$, label: sectionData.label } + ) ); + $.each( sectionData.items, function ( index, categoryItem ) { + itemWidgets.push( new OO.ui.MenuItemWidget( categoryItem, { $: this.lookupMenu.$, label: categoryItem } ) ); + }.bind( this ) ); + } + }.bind( this ) ); -/** - * Get a OO.ui.MenuSectionItemWidget object for a given category name. - * Deals with redirects. - * - * @method - * @param {string} item Category name - * @param {jQuery} menu$ Lookup menu jQuery - * @returns {OO.ui.MenuSectionItemWidget} Menu item - */ -ve.ui.MWCategoryInputWidget.prototype.getMenuItemWidgetFromCategoryName = function ( item, menu$ ) { - var itemTitle = new mw.Title( 'Category:' + item ).getPrefixedText(), - redirectInfo = $.grep( this.categoryRedirects, function ( redirectInfo ) { - return redirectInfo.to === itemTitle; - } ); - if ( redirectInfo.length ) { - return new OO.ui.MenuItemWidget( item, { - $: menu$, - autoFitLabel: false, - label: this.$( '' ) - .text( new mw.Title( redirectInfo[0].from ).getMainText() ) - .append( '
↳ ' ) - .append( $( '' ).text( new mw.Title( item ).getMainText() ) ) - } ); - } else { - return new OO.ui.MenuItemWidget( item, { $: menu$, label: item } ); - } + return itemWidgets; }; /**