diff options
Diffstat (limited to 'MLEB/Translate/resources/js')
25 files changed, 1157 insertions, 325 deletions
diff --git a/MLEB/Translate/resources/js/LanguagesMultiselectWidget.js b/MLEB/Translate/resources/js/LanguagesMultiselectWidget.js new file mode 100644 index 00000000..2ec3b0e7 --- /dev/null +++ b/MLEB/Translate/resources/js/LanguagesMultiselectWidget.js @@ -0,0 +1,123 @@ +'use strict'; +/* eslint-disable no-implicit-globals */ + +/** + * @class + * @extends OO.ui.MenuTagMultiselectWidget + * @mixins OO.ui.mixin.RequestManager + * @mixins OO.ui.mixin.PendingElement + * + * @constructor + * @param {Object} [config] Configuration options + * + * @author Niklas Laxström + * @license GPL-2.0-or-later + * @since 2020.07 + */ +var LanguagesMultiselectWidget = function ( config ) { + this.api = config.api; + this.allowedValues = Object.keys( config.languages ); + this.languages = config.languages; + + LanguagesMultiselectWidget.parent.call( this, $.extend( true, + { + allowEditTags: false, + allowArbitary: false, + menu: { + filterFromInput: false + } + }, + config + ) ); + + OO.ui.mixin.RequestManager.call( this, config ); + OO.ui.mixin.PendingElement.call( this, $.extend( true, {}, config, { + $pending: this.$handle + } ) ); + + // No need for default values for the menu + this.menu.clearItems(); +}; + +/* Setup */ + +OO.inheritClass( LanguagesMultiselectWidget, OO.ui.MenuTagMultiselectWidget ); +OO.mixinClass( LanguagesMultiselectWidget, OO.ui.mixin.RequestManager ); +OO.mixinClass( LanguagesMultiselectWidget, OO.ui.mixin.PendingElement ); + +/* Methods */ + +/** @inheritdoc OO.ui.MenuTagMultiselectWidget */ +LanguagesMultiselectWidget.prototype.onInputChange = function () { + var widget = this; + + this.getRequestData() + .then( function ( data ) { + // Reset + widget.menu.clearItems(); + widget.menu.addItems( widget.getOptionsFromData( data ) ); + } ).always( function () { + // Parent method + LanguagesMultiselectWidget.parent.prototype.onInputChange.call( widget ); + } ); +}; + +/** + * Get option widgets from the server response + * + * @param {Object} data Query result + * @return {OO.ui.MenuOptionWidget[]} Menu items + */ +LanguagesMultiselectWidget.prototype.getOptionsFromData = function ( data ) { + var languageCode, options = []; + + for ( languageCode in data ) { + if ( this.languages[ languageCode ] !== undefined ) { + options.push( new OO.ui.MenuOptionWidget( { + data: languageCode, + label: this.languages[ languageCode ] + } ) ); + } + } + + return options; +}; + +/** @inheritdoc OO.ui.MenuTagMultiselectWidget */ +LanguagesMultiselectWidget.prototype.setValue = function ( valueObject ) { + valueObject = Array.isArray( valueObject ) ? valueObject : [ valueObject ]; + + this.clearItems(); + valueObject.forEach( function ( obj ) { + var data; + + if ( typeof obj === 'string' ) { + data = obj; + } else { + data = obj.data; + } + + this.addTag( data, this.languages[ data ] ); + }.bind( this ) ); +}; + +/** @inheritdoc OO.ui.mixin.RequestManager */ +LanguagesMultiselectWidget.prototype.getRequestQuery = function () { + return this.input.getValue(); +}; + +/** @inheritdoc OO.ui.mixin.RequestManager */ +LanguagesMultiselectWidget.prototype.getRequest = function () { + return this.api.get( { + action: 'languagesearch', + formatversion: '2', + search: this.getRequestQuery() + } ); +}; + +/** @inheritdoc OO.ui.mixin.RequestManager */ +LanguagesMultiselectWidget.prototype.getRequestCacheDataFromResponse = function ( response ) { + return response.languagesearch || {}; +}; + +module.exports = LanguagesMultiselectWidget; diff --git a/MLEB/Translate/resources/js/ext.translate.base.js b/MLEB/Translate/resources/js/ext.translate.base.js index e9930834..cb0e13a5 100644 --- a/MLEB/Translate/resources/js/ext.translate.base.js +++ b/MLEB/Translate/resources/js/ext.translate.base.js @@ -171,10 +171,9 @@ }, isDirty: function () { - return $( '.mw-ajax-dialog:visible' ).length || // For old Translate - // For new Translate, something being typed in the current editor. - mw.translate.dirty || - // For new translate, previous editors has some unsaved edits + // Something being typed in the current editor. + return mw.translate.dirty || + // Previous editors has some unsaved edits $( '.tux-status-unsaved' ).length; } } ); diff --git a/MLEB/Translate/resources/js/ext.translate.editor.helpers.js b/MLEB/Translate/resources/js/ext.translate.editor.helpers.js index 7a1729aa..f728381e 100644 --- a/MLEB/Translate/resources/js/ext.translate.editor.helpers.js +++ b/MLEB/Translate/resources/js/ext.translate.editor.helpers.js @@ -68,8 +68,10 @@ ).done( function ( parsedDocumentation ) { $messageDesc.html( parsedDocumentation ); } ).fail( function ( errorCode, results ) { + // Note: It is possible for results to be undefined. + var errorInfo = results && results.error ? results.error.info : 'No information'; $messageDesc.html( newDocumentation ); - mw.log( 'Error parsing documentation ' + errorCode + ' ' + results.error.info ); + mw.log( 'Error parsing documentation ' + errorCode + ' ' + errorInfo ); } ).always( function () { // A collapsible element etc. may have been added mw.hook( 'wikipage.content' ).fire( $messageDesc ); @@ -359,7 +361,7 @@ e.stopPropagation(); if ( suggestion.$sourcesElement ) { - suggestion.$sourcesElement.toggle(); + suggestion.$sourcesElement.toggleClass( 'hide' ); return; } @@ -568,10 +570,12 @@ ); }.bind( this ) ).fail( function ( errorCode, results ) { + // results.error may be undefined + var errorInfo = results && results.error && results.error.info || 'Unknown error'; this.$editor.find( '.infocolumn .loading' ).remove(); this.$editor.find( '.infocolumn' ).append( $( '<div>' ) - .text( mw.msg( 'tux-editor-loading-failed', results.error.info ) ) + .text( mw.msg( 'tux-editor-loading-failed', errorInfo ) ) .addClass( 'warningbox tux-translation-aid-error' ) ); mw.log.error( 'Error loading translation aids:', errorCode, results ); diff --git a/MLEB/Translate/resources/js/ext.translate.editor.js b/MLEB/Translate/resources/js/ext.translate.editor.js index 0074418c..e4e227c1 100644 --- a/MLEB/Translate/resources/js/ext.translate.editor.js +++ b/MLEB/Translate/resources/js/ext.translate.editor.js @@ -244,16 +244,6 @@ if ( editResp.result === 'Success' ) { translateEditor.message.translation = translation; translateEditor.onSaveSuccess(); - // Handle errors - // BC MW < 1.34 - } else if ( editResp.spamblacklist ) { - translateEditor.onSaveFail( mw.msg( 'spamprotectiontext' ) ); - // BC MW < 1.34 - } else if ( editResp.info && - editResp.info.indexOf( 'Hit AbuseFilter:' ) === 0 && - editResp.warning - ) { - translateEditor.onSaveFail( editResp.warning ); } else { translateEditor.onSaveFail( mw.msg( 'tux-save-unknown-error' ) ); mw.log( response, xhr ); @@ -505,7 +495,7 @@ $editSummary, $editSummaryBlock, $discardChangesButton = $( [] ), - $saveButton, + $saveButton = $( [] ), $requestRight, $skipButton, $cancelButton, @@ -689,9 +679,7 @@ } ); $textarea.on( 'textchange', function () { - var $textarea = $( this ), - $saveButton = translateEditor.$editor.find( '.tux-editor-save-button' ), - $pasteSourceButton = translateEditor.$editor.find( '.tux-editor-paste-original-button' ), + var $pasteSourceButton = translateEditor.$editor.find( '.tux-editor-paste-original-button' ), original = translateEditor.message.translation || '', current = $textarea.val() || ''; @@ -1391,7 +1379,7 @@ }, /** - * Makes the textare large enough for insertables and positions the insertables. + * Makes the textarea large enough for insertables and positions the insertables. * * @param {jQuery} $textarea Text area. */ diff --git a/MLEB/Translate/resources/js/ext.translate.groupselector.js b/MLEB/Translate/resources/js/ext.translate.groupselector.js index 624da2a2..c87f4acf 100644 --- a/MLEB/Translate/resources/js/ext.translate.groupselector.js +++ b/MLEB/Translate/resources/js/ext.translate.groupselector.js @@ -64,7 +64,7 @@ this.$menu = $( '<div>' ) .addClass( 'tux-groupselector' ) - .addClass( 'grid' ); + .addClass( 'grid hide' ); $searchIcon = $( '<div>' ) .addClass( 'two columns tux-groupselector__filter__search__icon' ); @@ -124,7 +124,7 @@ * Show the selector */ show: function () { - this.$menu.addClass( 'open' ).show(); + this.$menu.addClass( 'open' ).removeClass( 'hide' ); this.position(); // Place the focus in the message group search box. this.$search.trigger( 'focus' ); @@ -147,7 +147,7 @@ return; } - this.$menu.hide().removeClass( 'open' ); + this.$menu.addClass( 'hide' ).removeClass( 'open' ); }, /** @@ -301,7 +301,7 @@ showDefaultGroups: function () { var groupSelector = this; - this.$loader.show(); + this.$loader.removeClass( 'hide' ); this.loadGroups().done( function ( groups ) { var groupsToShow = mw.translate.findGroup( groupSelector.parentGroupId, groups ); @@ -311,7 +311,7 @@ groupsToShow = groupsToShow.groups; } - groupSelector.$loader.hide(); + groupSelector.$loader.addClass( 'hide' ); groupSelector.$list.empty(); groupSelector.addGroupRows( groupsToShow ); } ); @@ -333,11 +333,11 @@ */ showSelectedGroups: function ( groups ) { var groupSelector = this; - this.$loader.show(); + this.$loader.removeClass( 'hide' ); this.loadGroups() .then( function ( allGroups ) { var rows = []; - $.each( groups, function ( index, id ) { + groups.forEach( function ( id ) { var group = mw.translate.findGroup( id, allGroups ); if ( group ) { rows.push( groupSelector.prepareMessageGroupRow( group ) ); @@ -346,7 +346,7 @@ return rows; } ) .always( function () { - groupSelector.$loader.hide(); + groupSelector.$loader.addClass( 'hide' ); groupSelector.$list.empty(); } ) .done( function ( rows ) { @@ -415,7 +415,7 @@ } } - self.$loader.hide(); + self.$loader.addClass( 'hide' ); self.$list.empty(); self.addGroupRows( foundGroups ); } ); @@ -466,7 +466,7 @@ return; } - $.each( groups, function ( index, group ) { + groups.forEach( function ( group ) { /* Hide from the selector: * - discouraged groups (the only priority value currently supported). * - groups that are recommended for other languages. diff --git a/MLEB/Translate/resources/js/ext.translate.messagetable.js b/MLEB/Translate/resources/js/ext.translate.messagetable.js index ba139bed..9bea2c33 100644 --- a/MLEB/Translate/resources/js/ext.translate.messagetable.js +++ b/MLEB/Translate/resources/js/ext.translate.messagetable.js @@ -285,17 +285,17 @@ } ); setTimeout( function () { - var offset, $icon = $( '.autotooltip:visible' ); - if ( !$icon.length ) { + var offset, $visibleIcon = $( '.autotooltip:visible' ); + if ( !$visibleIcon.length ) { return; } - offset = $icon.offset(); + offset = $visibleIcon.offset(); tooltip.$element.appendTo( document.body ); tooltip.toggle( true ).toggleClipping( false ).togglePositioning( false ); tooltip.$element.css( { - top: offset.top + $icon.outerHeight() + 5, - left: offset.left + $icon.outerWidth() - tooltip.$element.width() / 2 - 15 + top: offset.top + $visibleIcon.outerHeight() + 5, + left: offset.left + $visibleIcon.outerWidth() - tooltip.$element.width() / 2 - 15 } ); setTimeout( function () { @@ -509,7 +509,7 @@ } } - $.each( messages, function ( index, message ) { + messages.forEach( function ( message, index ) { message.group = self.settings.group; self.add( message ); self.messages.push( message ); @@ -779,7 +779,7 @@ } if ( messageTable.messages.length ) { - $.each( messageTable.messages, function ( index, message ) { + messageTable.messages.forEach( function ( message ) { messageTable.add( message ); } ); } else if ( messageTable.initialized && !messageTable.loading ) { @@ -855,7 +855,7 @@ var $warningContainer = $( '.tux-editor-header .group-warning' ); if ( errors ) { - $.map( errors, function ( error ) { + errors.forEach( function ( error ) { $warningContainer.append( error.html ); } ); } else { diff --git a/MLEB/Translate/resources/js/ext.translate.multiselectautocomplete.js b/MLEB/Translate/resources/js/ext.translate.multiselectautocomplete.js deleted file mode 100644 index e8c88897..00000000 --- a/MLEB/Translate/resources/js/ext.translate.multiselectautocomplete.js +++ /dev/null @@ -1,96 +0,0 @@ -/*! - * @author Santhosh Thottingal - * jQuery autocomplete based multiple selector for input box. - * Autocompleted values will be available in input filed as comma separated values. - * The values for autocompletion is from the language selector in this case. - * The input field is created in PHP code. - * Credits: https://jqueryui.com/autocomplete/#multiple - */ -$( function () { - 'use strict'; - - /* eslint-disable no-underscore-dangle */ - - $.widget( 'ui.multiselectautocomplete', { - options: { - inputbox: null // a jQuery selector for the input box where selections are written. - // TODO can have more options. - }, - _create: function () { - var self, select, options, input; - - self = this; - select = this.element.hide(); - options = this.options; - - function split( val ) { - return val.split( /,\s*/ ); - } - - input = this.input = $( options.inputbox ).autocomplete( { - delay: 0, - minLength: 0, - source: function ( request, response ) { - var term, matcher; - - term = split( request.term ).pop(); - matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), 'i' ); - - response( select.children( 'option' ).map( function () { - var text = $( this ).html(), - value = $( this ).val(), - term = split( request.term ).pop(); - - if ( this.value && ( !request.term || matcher.test( text ) ) ) { - if ( term.trim() !== '' ) { - text = text.replace( - new RegExp( - '(?![^&;]+;)(?!<[^<>]*)(' + - $.ui.autocomplete.escapeRegex( term ) + - ')(?![^<>]*>)(?![^&;]+;)', 'gi' - ), '<strong>$1</strong>' ); - } - - return { - label: text, - value: value, - option: this - }; - } - return undefined; - } ) ); - }, - select: function ( event, ui ) { - var terms; - - ui.item.option.selected = true; - self._trigger( 'selected', event, { - item: ui.item.option - } ); - terms = split( $( this ).val() ); - // remove the current input - terms.pop(); - // add the selected item - terms.push( ui.item.value ); - // add placeholder to get the comma-and-space at the end - terms.push( '' ); - $( this ).val( terms.join( ', ' ) ); - return false; - } - } ); - - input.data( 'autocomplete' )._renderItem = function ( ul, item ) { - return $( '<li>' ) - .data( 'item.autocomplete', item ) - .append( '<a>' + item.label + '</a>' ) - .appendTo( ul ); - }; - }, // End of _create - - destroy: function () { - this.input.remove(); - this.element.show(); - $.Widget.prototype.destroy.call( this ); - } - } ); -} ); diff --git a/MLEB/Translate/resources/js/ext.translate.pagetranslation.uls.js b/MLEB/Translate/resources/js/ext.translate.pagetranslation.uls.js index 00c67e9e..a0bfc2d4 100644 --- a/MLEB/Translate/resources/js/ext.translate.pagetranslation.uls.js +++ b/MLEB/Translate/resources/js/ext.translate.pagetranslation.uls.js @@ -10,6 +10,13 @@ page = page.replace( /\/[^/]+$/, '' ); } - location.href = mw.util.getUrl( page, { setlang: language } ); + if ( mw.uls.setLanguage ) { + mw.uls.setLanguage( language ).then( function () { + location.href = mw.util.getUrl( page ); + } ); + } else { + // Fallback if ULS is older than Translate (2021.03) + location.href = mw.util.getUrl( page, { setlang: language } ); + } }; }() ); diff --git a/MLEB/Translate/resources/js/ext.translate.parsers.js b/MLEB/Translate/resources/js/ext.translate.parsers.js index 4f38df41..393a76bb 100644 --- a/MLEB/Translate/resources/js/ext.translate.parsers.js +++ b/MLEB/Translate/resources/js/ext.translate.parsers.js @@ -36,14 +36,14 @@ } ); text = text.replace( /(^\*.*(\n|$))+/gm, function ( match ) { - match = match.replace( /^\*(.*)/gm, function ( match, p1 ) { + match = match.replace( /^\*(.*)/gm, function ( fullMatch, p1 ) { return $( '<div>' ).append( $( '<li>' ).html( p1 ) ).html(); } ); return $( '<div>' ).append( $( '<ul>' ).html( match ) ).html(); } ); text = text.replace( /(^#.*(\n|$))+/gm, function ( match ) { - match = match.replace( /^#(.*)/gm, function ( match, p1 ) { + match = match.replace( /^#(.*)/gm, function ( fullMatch, p1 ) { return $( '<div>' ).append( $( '<li>' ).html( p1 ) ).html(); } ); return $( '<div>' ).append( $( '<ol>' ).html( match ) ).html(); diff --git a/MLEB/Translate/resources/js/ext.translate.proofread.js b/MLEB/Translate/resources/js/ext.translate.proofread.js index 22179f6a..ad3791af 100644 --- a/MLEB/Translate/resources/js/ext.translate.proofread.js +++ b/MLEB/Translate/resources/js/ext.translate.proofread.js @@ -268,9 +268,7 @@ data = $this.data( 'proofread' ); if ( !data ) { - $this.data( 'proofread', - ( data = new Proofread( this, options ) ) - ); + $this.data( 'proofread', new Proofread( this, options ) ); } } ); diff --git a/MLEB/Translate/resources/js/ext.translate.special.aggregategroups.js b/MLEB/Translate/resources/js/ext.translate.special.aggregategroups.js index 24b2079d..41d4ac0a 100644 --- a/MLEB/Translate/resources/js/ext.translate.special.aggregategroups.js +++ b/MLEB/Translate/resources/js/ext.translate.special.aggregategroups.js @@ -31,7 +31,7 @@ } function associate( event, resp ) { - var successFunction, params, subgroupId, + var successFunction, params, subgroupId, i, $target = $( event.target ), $parent = $target.parents( '.mw-tpa-group' ), parentId = $parent.data( 'id' ), @@ -63,11 +63,12 @@ // Get the label for the value and make API request if valid subgroupId = ''; - $.each( resp, function ( key, value ) { - if ( subgroupName === value.label ) { - subgroupId = value.id; + for ( i = 0; i < resp.length; ++i ) { + if ( subgroupName === resp[ i ].label ) { + subgroupId = resp[ i ].id; + break; } - } ); + } if ( subgroupId ) { params = $.extend( getApiParams( $target ), { @@ -201,11 +202,15 @@ resp = []; - $.each( groups, function ( key, value ) { - if ( value.label.match( inp ) && exclude.indexOf( value.label ) === -1 ) { - resp.push( value ); + Object.keys( groups ).forEach( function ( key ) { + if ( + groups[ key ].label.match( inp ) && + exclude.indexOf( groups[ key ].label ) === -1 + ) { + resp.push( groups[ key ] ); } } ); + response( resp ); }; diff --git a/MLEB/Translate/resources/js/ext.translate.special.languagestats.js b/MLEB/Translate/resources/js/ext.translate.special.languagestats.js index 8f65646e..00daca44 100644 --- a/MLEB/Translate/resources/js/ext.translate.special.languagestats.js +++ b/MLEB/Translate/resources/js/ext.translate.special.languagestats.js @@ -8,19 +8,20 @@ ( function () { 'use strict'; + var $columns; + /** * Add css class to every other visible row. * It's not possible to do zebra colors with CSS only if there are hidden rows. */ - function doZebra() { - $( '.statstable tr:visible:odd' ).toggleClass( 'tux-statstable-even', false ); - $( '.statstable tr:visible:even' ).toggleClass( 'tux-statstable-even', true ); + function doZebra( $table ) { + $table.find( 'tr:visible:odd' ).toggleClass( 'tux-statstable-even', false ); + $table.find( 'tr:visible:even' ).toggleClass( 'tux-statstable-even', true ); } - $( function () { + function addExpanders( $table ) { var $allChildRows, $allTogglesCache, $toggleAllButton, - $translateTable = $( '.statstable' ), - $metaRows = $( 'tr.AggregateMessageGroup', $translateTable ); + $metaRows = $( 'tr.AggregateMessageGroup', $table ); // Quick return if ( !$metaRows.length ) { @@ -31,7 +32,7 @@ var $toggler, $parent = $( this ), thisGroupId = $parent.attr( 'data-groupid' ), - $children = $( 'tr[data-parentgroup="' + thisGroupId + '"]', $translateTable ); + $children = $( 'tr[data-parentgroup="' + thisGroupId + '"]', $table ); // Only do the collapse stuff if this Meta-group actually has children on this page if ( !$children.length ) { @@ -51,11 +52,13 @@ var $el = $( this ); // Switch the state and toggle the rows if ( $el.hasClass( 'collapsed' ) ) { - $children.fadeIn( { start: doZebra } ).trigger( 'show' ); + $children.removeClass( 'statstable-hide' ).trigger( 'show' ); + doZebra( $table ); $el.removeClass( 'collapsed' ).addClass( 'expanded' ); $el.find( '> a' ).text( mw.msg( 'translate-langstats-collapse' ) ); } else { - $children.fadeOut( { done: doZebra } ).trigger( 'hide' ); + $children.addClass( 'statstable-hide' ).trigger( 'hide' ); + doZebra( $table ); $el.addClass( 'collapsed' ).removeClass( 'expanded' ); $el.find( '> a' ).text( mw.msg( 'translate-langstats-expand' ) ); } @@ -77,7 +80,7 @@ } ); // Create, bind and append the toggle-all button - $allChildRows = $( 'tr[data-parentgroup]', $translateTable ); + $allChildRows = $( 'tr[data-parentgroup]', $table ); $allTogglesCache = null; $toggleAllButton = $( '<span>' ).addClass( 'collapsed' ) .append( @@ -89,56 +92,162 @@ ) .on( 'click', function ( e ) { var $el = $( this ), - $allToggles = $allTogglesCache || $( '.groupexpander', $translateTable ); + $allToggles = $allTogglesCache || $( '.groupexpander', $table ); // Switch the state and toggle the rows // and update the local toggles too if ( $el.hasClass( 'collapsed' ) ) { - $allChildRows.show(); + $allChildRows.removeClass( 'statstable-hide' ); $el.add( $allToggles ).removeClass( 'collapsed' ).addClass( 'expanded' ); $el.find( '> a' ).text( mw.msg( 'translate-langstats-collapseall' ) ); $allToggles.find( '> a' ).text( mw.msg( 'translate-langstats-collapse' ) ); } else { - $allChildRows.hide(); + $allChildRows.addClass( 'statstable-hide' ); $el.add( $allToggles ).addClass( 'collapsed' ).removeClass( 'expanded' ); $el.find( '> a' ).text( mw.msg( 'translate-langstats-expandall' ) ); $allToggles.find( '> a' ).text( mw.msg( 'translate-langstats-expand' ) ); } - doZebra(); + doZebra( $table ); e.preventDefault(); } ); // Initially hide them - $allChildRows.hide(); - doZebra(); + $allChildRows.addClass( 'statstable-hide' ); + doZebra( $table ); // Add the toggle-all button above the table - $( '<p>' ).addClass( 'groupexpander-all' ).append( $toggleAllButton ).insertBefore( $translateTable ); - } ); + $( '<p>' ).addClass( 'groupexpander-all' ).append( $toggleAllButton ).insertBefore( $table ); + } - $( function () { + function applySorting( $table ) { var index, sort = {}, re = /#sortable:(\d+)=(asc|desc)/, - match = re.exec( window.location.hash ), - $tables = $( '.statstable' ); + match = re.exec( location.hash ); if ( match ) { index = parseInt( match[ 1 ], 10 ); sort[ index ] = match[ 2 ]; } - $tables.tablesorter( { sortList: [ sort ] } ); + $table.tablesorter( { sortList: [ sort ] } ); - $tables.on( 'sortEnd.tablesorter', function () { - var $table = $( this ); + $table.on( 'sortEnd.tablesorter', function () { $table.find( '.headerSortDown, .headerSortUp' ).each( function () { - var index = $table.find( 'th' ).index( $( this ) ), + var headerIndex = $table.find( 'th' ).index( $( this ) ), dir = $( this ).hasClass( 'headerSortUp' ) ? 'asc' : 'desc'; - window.location.hash = 'sortable:' + index + '=' + dir; + location.hash = 'sortable:' + headerIndex + '=' + dir; + + doZebra( $table ); + } ); + } ); + } - doZebra(); + function narrowTable( $table, enable ) { + var $select, + labelColumnCount = 1, + // 0-indexed + defaultValueColumn = 2; + + if ( $columns === undefined ) { + $columns = $table.find( 'thead > tr > th ' ).map( function ( index, value ) { + return value.textContent; } ); + } + + $select = makeValueColumnSelector( $columns, labelColumnCount, defaultValueColumn ); + // Prevent table sorter from making the select inaccessible + $select.on( 'mousedown click', function ( e ) { + e.stopPropagation(); + } ).on( 'change', function () { + showValueColumn( $table, $select, labelColumnCount ); + } ); + + if ( enable ) { + showValueColumn( $table, $select, labelColumnCount ); + } else { + // Restore original headings + $table.find( 'thead > tr > th' ).map( function ( index ) { + return $( this ).text( $columns[ index ] ); + } ); + $table.find( 'tr > *' ).removeClass( 'statstable-hide' ); + } + + } + + function makeValueColumnSelector( headings, skip, def ) { + var i, $select = $( '<select>' ); + + for ( i = skip; i < headings.length; i++ ) { + $( '<option>' ) + .text( headings[ i ] ) + .val( i ) + .prop( 'selected', i === def ) + .appendTo( $select ); + } + + return $select; + } + + function showValueColumn( $table, $select, skip ) { + var i, index, cssQuery; + + index = parseInt( $select.val(), 10 ); + cssQuery = 'th:nth-child(_)'.replace( '_', index + 1 ); + $table.find( cssQuery ).html( $select ); + + for ( i = 0; i < $select.children().length; i++ ) { + cssQuery = 'tr > *:nth-child(_)'.replace( '_', i + skip + 1 ); + $table.find( cssQuery ).toggleClass( 'statstable-hide', i + skip !== index ); + } + } + + $( function () { + var isNarrowMode, minimumTableWidth, + $table = $( '.statstable' ); + + // Sometimes the table is not present on the page + if ( !$table.length ) { + return; + } + + // Calculate absolute minimum table width + if ( window.ResizeObserver ) { + $table.css( 'max-width', '1px' ); + } + + applySorting( $table ); + addExpanders( $table ); + + if ( !window.ResizeObserver ) { + return; + } + + // Hopefully previous stuff have time to render by now to have accurate picture of the width + ( window.requestAnimationFrame || setTimeout )( function () { + minimumTableWidth = $table.outerWidth(); + $table.css( 'max-width', '' ); } ); + + new ResizeObserver( function ( entries ) { + var newMode, shouldCollapse, shouldExpand; + + shouldCollapse = entries[ 0 ].contentRect.width < minimumTableWidth; + // Some fudge to avoid flapping + shouldExpand = entries[ 0 ].contentRect.width - 20 > minimumTableWidth; + + if ( isNarrowMode && shouldExpand ) { + newMode = false; + } else if ( !isNarrowMode && shouldCollapse ) { + newMode = true; + } else { + newMode = isNarrowMode; + } + + if ( newMode !== isNarrowMode ) { + isNarrowMode = newMode; + narrowTable( $table, isNarrowMode ); + } + } ).observe( $table.parent().get( 0 ) ); } ); }() ); diff --git a/MLEB/Translate/resources/js/ext.translate.special.managegroups.js b/MLEB/Translate/resources/js/ext.translate.special.managegroups.js index 9d727adf..9337ed5f 100644 --- a/MLEB/Translate/resources/js/ext.translate.special.managegroups.js +++ b/MLEB/Translate/resources/js/ext.translate.special.managegroups.js @@ -1,10 +1,13 @@ ( function () { - var RenameDropdown; + var RenameDropdown, + GroupSynchronization; $( function () { var windowManager, renameDialog; RenameDropdown.init(); + GroupSynchronization.init(); + // Create and append a window manager. windowManager = new OO.ui.WindowManager(); windowManager.$element.appendTo( document.body ); @@ -45,6 +48,7 @@ } ).show(); if ( $parentContainer.hasClass( 'smg-change-addition' ) ) { + // For a new message, the "add as new" option is hidden. RenameDropdown.hideOption( '.smg-rename-new-action' ); } event.preventDefault(); @@ -121,10 +125,11 @@ function toggleLoading( $element, isLoading ) { if ( isLoading ) { - $( '.smg-rename-actions' ).hide(); - $element.show().addClass( 'loading' ); + // hide all the rename buttons, but show the current one with loading animation + $( '.smg-rename-actions' ).addClass( 'mw-translate-hide' ); + $element.removeClass( 'mw-translate-hide' ).addClass( 'loading' ); } else { - $( '.smg-rename-actions' ).show(); + $( '.smg-rename-actions' ).removeClass( 'mw-translate-hide' ); $element.removeClass( 'loading' ); } } @@ -264,13 +269,13 @@ var $addAsRename = $( '<li>' ).append( $( '<button>' ) .attr( 'type', 'button' ) - .addClass( 'smg-rename-new-action' ) + .addClass( 'smg-rename-new-action mw-translate-hide' ) .text( mw.msg( 'translate-smg-rename-new' ) ) ), $addAsNew = $( '<li>' ).append( $( '<button>' ) .attr( 'type', 'button' ) - .addClass( 'smg-rename-rename-action' ) + .addClass( 'smg-rename-rename-action mw-translate-hide' ) .text( mw.msg( 'translate-smg-rename-rename' ) ) ); @@ -316,8 +321,9 @@ left: $currentTarget.position().left - $renameMenu.width() + $currentTarget.width() } ).data( 'custom-data', customData ); - // show all the li's - $renameMenu.find( 'li' ).show(); + // When appending, show all the li's by default since based on the + // message type (RENAME / NEW) some li's may be hidden previously + $renameMenu.find( 'li' ).removeClass( 'mw-translate-hide' ); return this; } @@ -337,7 +343,7 @@ * @chainable */ function hideOption( optSelector ) { - $renameMenu.find( optSelector ).parent().hide(); + $renameMenu.find( optSelector ).parent().addClass( 'mw-translate-hide' ); return this; } @@ -350,4 +356,119 @@ hideOption: hideOption }; }() ); + + GroupSynchronization = ( function () { + function init() { + $( '.js-group-sync-message-resolve' ).on( 'click', markMessageAsResolved ); + $( '.js-group-sync-group-resolve' ).on( 'click', markGroupAsResolved ); + } + + function markMessageAsResolved() { + var $target = $( this ), + groupId = $target.data( 'groupId' ), + messageTitle = $target.data( 'msgTitle' ); + + showLoading( $target ); + + markAsResolved( 'resolveMessage', groupId, messageTitle ).done( function ( response ) { + var responseData = response.managegroupsynchronizationcache || null; + if ( responseData && responseData.success ) { + if ( responseData.data.groupRemainingMessageCount === 0 ) { + removeParentGroupBlock( $target ); + } else { + // Remove the message from the DOM + $target.parents( '.js-group-sync-message-error' ).remove(); + } + } + } ).fail( function ( code, result ) { + handleResolutionFailure( code, result, groupId, messageTitle ); + } ).always( function () { + hideLoading( $target ); + } ); + } + + function markGroupAsResolved() { + var $target = $( this ), + groupId = $target.data( 'groupId' ); + + showLoading( $target ); + + markAsResolved( 'resolveGroup', groupId ).done( function ( response ) { + var responseData = response.managegroupsynchronizationcache || null; + if ( responseData && responseData.success ) { + removeParentGroupBlock( $target ); + } + } ).fail( function ( code, result ) { + handleResolutionFailure( code, result, groupId ); + } ).always( function () { + hideLoading( $target ); + } ); + } + + function markAsResolved( operation, groupId, messageTitle ) { + var params, api = new mw.Api(); + + params = { + action: 'managegroupsynchronizationcache', + group: groupId, + operation: operation, + assert: 'user', + formatversion: 2 + }; + + if ( messageTitle ) { + params.title = messageTitle; + } + + return api.postWithToken( 'csrf', params ); + } + + function removeParentGroupBlock( $child ) { + // Remove the entire group block from DOM + $child.parents( '.js-group-sync-group-errors' ).remove(); + // If all groups are resolved, remove the group sync error block + if ( !$( '.js-group-sync-group-errors' ).length ) { + $( '.js-group-sync-groups-with-error' ).remove(); + } + } + + function handleResolutionFailure( code, result, groupId, messageTitle ) { + var errorInfo = result && result.error ? result.error.info : null; + if ( errorInfo ) { + mw.notify( result.error.info, { + type: 'error', + tag: 'new-error' + } ); + } else { + // Unknown error + mw.notify( mw.msg( 'translate-smg-unknown-error' ), { + type: 'error', + tag: 'new-error' + } ); + } + + mw.log.error( 'Error while resolving group or message. Param: ' + JSON.stringify( { + groupId: groupId, + messageTitle: messageTitle, + errorCode: code, + errorInfo: errorInfo + } ) ); + } + + function showLoading( $target ) { + $target.addClass( 'loading' ) + .text( mw.msg( 'translate-smg-loading' ) ) + .removeAttr( 'href' ); + } + + function hideLoading( $target ) { + $target.removeClass( 'loading' ) + .text( mw.msg( 'translate-smg-group-action-resolve' ) ) + .prop( 'href', '#' ); + } + + return { + init: init + }; + }() ); }() ); diff --git a/MLEB/Translate/resources/js/ext.translate.special.managetranslatorsandbox.js b/MLEB/Translate/resources/js/ext.translate.special.managetranslatorsandbox.js index bb451592..e5986e1a 100644 --- a/MLEB/Translate/resources/js/ext.translate.special.managetranslatorsandbox.js +++ b/MLEB/Translate/resources/js/ext.translate.special.managetranslatorsandbox.js @@ -86,7 +86,9 @@ $( '<div>' ) .addClass( 'reminder-email row' ) .append( - $( '<span>' ).text( request.email ), + $( '<span>' ) + .attr( { dir: 'ltr' } ) + .text( request.email ), $( '<a>' ) .prop( 'href', '#' ) .addClass( 'send-reminder link' ) @@ -156,7 +158,7 @@ if ( request.languagepreferences ) { if ( request.languagepreferences.languages ) { - $.each( request.languagepreferences.languages, function ( index, language ) { + request.languagepreferences.languages.forEach( function ( language ) { $detailsPane.find( '.languages' ).append( $( '<span>' ) .prop( { @@ -220,7 +222,7 @@ ); translations.translationstash.translations.sort( sortTranslationsByLanguage ); - $.each( translations.translationstash.translations, function ( index, translation ) { + translations.translationstash.translations.forEach( function ( translation ) { showTranslation( translation ); } ); } diff --git a/MLEB/Translate/resources/js/ext.translate.special.pagemigration.js b/MLEB/Translate/resources/js/ext.translate.special.pagemigration.js index 19b5651d..06578db1 100644 --- a/MLEB/Translate/resources/js/ext.translate.special.pagemigration.js +++ b/MLEB/Translate/resources/js/ext.translate.special.pagemigration.js @@ -60,12 +60,12 @@ } if ( obj === undefined ) { // obj was not initialized - $errorBox.text( mw.msg( 'pm-page-does-not-exist', pageTitle ) ).show( 'fast' ); + $errorBox.text( mw.msg( 'pm-page-does-not-exist', pageTitle ) ).removeClass( 'hide' ); return $.Deferred().reject(); } if ( obj.revisions === undefined ) { // the case of /en subpage where first edit is by FuzzyBot - $errorBox.text( mw.msg( 'pm-old-translations-missing', pageTitle ) ).show( 'fast' ); + $errorBox.text( mw.msg( 'pm-old-translations-missing', pageTitle ) ).removeClass( 'hide' ); return $.Deferred().reject(); } pageContent = obj.revisions[ 0 ][ '*' ]; @@ -102,12 +102,12 @@ } // Page does not exist if missing field is present if ( obj === undefined || obj.missing === '' ) { - $errorBox.text( mw.msg( 'pm-page-does-not-exist', pageTitle ) ).show( 'fast' ); + $errorBox.text( mw.msg( 'pm-page-does-not-exist', pageTitle ) ).removeClass( 'hide' ); return $.Deferred().reject(); } // Page exists, but no edit by FuzzyBot if ( obj.revisions === undefined ) { - $errorBox.text( mw.msg( 'pm-old-translations-missing', pageTitle ) ).show( 'fast' ); + $errorBox.text( mw.msg( 'pm-old-translations-missing', pageTitle ) ).removeClass( 'hide' ); return $.Deferred().reject(); } else { // FB over here refers to FuzzyBot @@ -342,14 +342,14 @@ function saveHandler() { var i, content, list = []; - $( '.mw-tpm-sp-error__message' ).hide( 'fast' ); + $( '.mw-tpm-sp-error__message' ).addClass( 'hide' ); if ( noOfSourceUnits < noOfTranslationUnits ) { $( '.mw-tpm-sp-error__message' ).text( mw.msg( 'pm-extra-units-warning' ) ) - .show( 'fast' ); + .removeClass( 'hide' ); return; } else { $( 'input' ).prop( 'disabled', true ); - $( '.mw-tpm-sp-instructions' ).hide( 'fast' ); + $( '.mw-tpm-sp-instructions' ).addClass( 'hide' ); for ( i = 0; i < noOfSourceUnits; i++ ) { content = $( '.mw-tpm-sp-unit__target' ).eq( i ).val(); content = content.trim(); @@ -361,11 +361,13 @@ $.ajaxDispatcher( list, 1 ).done( function () { $( '#action-import' ).removeClass( 'hide' ); $( 'input' ).prop( 'disabled', false ); - $( '.mw-tpm-sp-instructions' ).text( mw.msg( 'pm-on-save-message-text' ) ).show( 'fast' ); + $( '.mw-tpm-sp-instructions' ) + .text( mw.msg( 'pm-on-save-message-text' ) ) + .removeClass( 'hide' ); } ).fail( function ( errmsg ) { $( 'input' ).prop( 'disabled', false ); // eslint-disable-next-line mediawiki/msg-doc - $( '.mw-tpm-sp-error__message' ).text( mw.msg( errmsg ) ).show( 'fast' ); + $( '.mw-tpm-sp-error__message' ).text( mw.msg( errmsg ) ).removeClass( 'hide' ); } ); } } @@ -374,8 +376,8 @@ * Handler for 'Cancel' button click event. */ function cancelHandler() { - $( '.mw-tpm-sp-error__message' ).hide( 'fast' ); - $( '.mw-tpm-sp-instructions' ).hide( 'fast' ); + $( '.mw-tpm-sp-error__message' ).addClass( 'hide' ); + $( '.mw-tpm-sp-instructions' ).addClass( 'hide' ); $( '#action-save, #action-cancel' ).addClass( 'hide' ); $( '#action-import' ).removeClass( 'hide' ); $( '.mw-tpm-sp-unit-listing' ).html( '' ); @@ -452,14 +454,14 @@ pageTitle = $( '#title' ).val().trim(); if ( pageTitle === '' ) { - $errorBox.text( mw.msg( 'pm-pagetitle-missing' ) ).show( 'fast' ); + $errorBox.text( mw.msg( 'pm-pagetitle-missing' ) ).removeClass( 'hide' ); return; } titleObj = mw.Title.newFromText( pageTitle ); - $messageBox.hide( 'fast' ); + $messageBox.addClass( 'hide' ); if ( titleObj === null ) { - $errorBox.text( mw.msg( 'pm-pagetitle-invalid' ) ).show( 'fast' ); + $errorBox.text( mw.msg( 'pm-pagetitle-invalid' ) ).removeClass( 'hide' ); return; } @@ -467,7 +469,7 @@ slashPos = pageTitle.lastIndexOf( '/' ); if ( slashPos === -1 ) { - $errorBox.text( mw.msg( 'pm-langcode-missing' ) ).show( 'fast' ); + $errorBox.text( mw.msg( 'pm-langcode-missing' ) ).removeClass( 'hide' ); return; } @@ -475,11 +477,11 @@ langCode = pageTitle.substring( slashPos + 1 ); if ( pageName === '' ) { - $errorBox.text( mw.msg( 'pm-pagetitle-invalid' ) ).show( 'fast' ); + $errorBox.text( mw.msg( 'pm-pagetitle-invalid' ) ).removeClass( 'hide' ); return; } - $errorBox.hide( 'fast' ); + $errorBox.addClass( 'hide' ); $.when( getSourceUnits( pageName ), getFuzzyTimestamp( pageTitle ) ) .then( function ( sourceUnits, fuzzyTimestamp ) { @@ -491,7 +493,7 @@ displayUnits( sourceUnits, translationUnits ); $( '#action-save, #action-cancel' ).removeClass( 'hide' ); $( '#action-import' ).addClass( 'hide' ); - $messageBox.text( mw.msg( 'pm-on-import-message-text' ) ).show( 'fast' ); + $messageBox.text( mw.msg( 'pm-on-import-message-text' ) ).removeClass( 'hide' ); } ); } ); } diff --git a/MLEB/Translate/resources/js/ext.translate.special.pagepreparation.js b/MLEB/Translate/resources/js/ext.translate.special.pagepreparation.js index b32008fe..cdf1aa52 100644 --- a/MLEB/Translate/resources/js/ext.translate.special.pagepreparation.js +++ b/MLEB/Translate/resources/js/ext.translate.special.pagepreparation.js @@ -73,7 +73,7 @@ * @return {string} */ function addLanguageBar( pageContent ) { - if ( !pageContent.match( /<languages\/>/gi ) ) { + if ( !/<languages\/>/gi.test( pageContent ) ) { pageContent = '<languages/>\n' + pageContent; } pageContent = pageContent.replace( /\{\{languages.*?\}\}/gi, '' ); @@ -376,12 +376,12 @@ $( '.messageDiv' ) .empty() .append( mw.message( 'pp-save-message', pageUrl ).parseDom() ) - .show(); - $( '.divDiff' ).hide( 'fast' ); - $( '#action-prepare' ).show(); + .removeClass( 'hide' ); + $( '.divDiff' ).addClass( 'hide' ); + $( '#action-prepare' ).removeClass( 'hide' ); $input.val( '' ); - $( '#action-save' ).hide(); - $( '#action-cancel' ).hide(); + $( '#action-save' ).addClass( 'hide' ); + $( '#action-cancel' ).addClass( 'hide' ); } ); } ); @@ -389,7 +389,7 @@ var pageName, $messageDiv = $( '.messageDiv' ); pageName = $input.val().trim(); - $messageDiv.hide(); + $messageDiv.addClass( 'hide' ); if ( pageName === '' ) { // eslint-disable-next-line no-alert alert( mw.msg( 'pp-pagename-missing' ) ); @@ -405,24 +405,24 @@ pageContent = addNewLines( pageContent ); pageContent = fixInternalLinks( pageContent ); pageContent = doTemplates( pageContent ); - doFiles( pageContent ).then( doCategories ).done( function ( pageContent ) { - pageContent = postPreparationCleanup( pageContent ); - pageContent = pageContent.trim(); - getDiff( pageName, pageContent ).done( function ( diff ) { + doFiles( pageContent ).then( doCategories ).done( function ( preppedContent ) { + preppedContent = postPreparationCleanup( preppedContent ); + preppedContent = preppedContent.trim(); + getDiff( pageName, preppedContent ).done( function ( diff ) { if ( diff === undefined ) { - $messageDiv.text( mw.msg( 'pp-diff-error' ) ).show(); + $messageDiv.text( mw.msg( 'pp-diff-error' ) ).removeClass( 'hide' ); return; } $( '.diff tbody' ).append( diff ); - $( '.divDiff' ).show( 'fast' ); + $( '.divDiff' ).removeClass( 'hide' ); if ( diff !== '' ) { - $messageDiv.text( mw.msg( 'pp-prepare-message' ) ).show(); - $( '#action-prepare' ).hide(); - $( '#action-save' ).show(); - $( '#action-cancel' ).show(); + $messageDiv.text( mw.msg( 'pp-prepare-message' ) ).removeClass( 'hide' ); + $( '#action-prepare' ).addClass( 'hide' ); + $( '#action-save' ).removeClass( 'hide' ); + $( '#action-cancel' ).removeClass( 'hide' ); } else { - $messageDiv.text( mw.msg( 'pp-already-prepared-message' ) ).show(); + $messageDiv.text( mw.msg( 'pp-already-prepared-message' ) ).removeClass( 'hide' ); } } ); } ); diff --git a/MLEB/Translate/resources/js/ext.translate.special.pagetranslation.js b/MLEB/Translate/resources/js/ext.translate.special.pagetranslation.js index 67610f52..fc27242a 100644 --- a/MLEB/Translate/resources/js/ext.translate.special.pagetranslation.js +++ b/MLEB/Translate/resources/js/ext.translate.special.pagetranslation.js @@ -1,26 +1,67 @@ +'use strict'; +/* eslint-disable no-implicit-globals */ + /*! * @author Santhosh Thottingal * @author Niklas Laxström * @license GPL-2.0-or-later */ -( function () { - 'use strict'; +var LanguagesMultiselectWidget = require( './LanguagesMultiselectWidget.js' ); + +// Needed for OOUI :( +window.LanguagesMultiselectWidget = LanguagesMultiselectWidget; + +function configureLanguageInput( $form, $widget ) { + var widget, $input; - $( function () { - $( '#wpUserLanguage' ).multiselectautocomplete( { inputbox: '#tpt-prioritylangs' } ); + /** @type {LanguagesMultiselectWidget} */ + widget = OO.ui.infuse( $widget, { api: new mw.Api() } ); - $( '#mw-content-text' ).on( 'click', '.mw-translate-jspost', function ( e ) { - var params, - uri = new mw.Uri( e.target.href ); + $input = $( '<input>' ).prop( { + type: 'hidden', + name: 'prioritylangs', + value: widget.getValue() + } ); + + $form.prepend( $input ); + widget.on( 'change', function () { + $input.val( widget.getValue() ); + } ); +} - params = uri.query; - params.token = mw.user.tokens.get( 'csrfToken' ); - $.post( uri.path, params ).done( function () { - location.reload(); - } ); +function configurePostLinks( $container ) { + $container.on( 'click', '.mw-translate-jspost', function ( e ) { + var params, + uri = new mw.Uri( e.target.href ); - e.preventDefault(); + params = uri.query; + params.token = mw.user.tokens.get( 'csrfToken' ); + $.post( uri.path, params ).done( function () { + location.reload(); } ); + + e.preventDefault(); } ); -}() ); +} + +// Init +$( function () { + var mediaWikiVersion = mw.config.get( 'wgVersion' ), + $widgets = $( '#mw-translate-SpecialPageTranslation-prioritylangs' ); + + configurePostLinks( $( '#mw-content-text' ) ); + + if ( $widgets.length ) { + // On MW 1.34, pre-selected priority languages are not being displayed when using + // LanguagesMultiselectWidget, which in turn uses MenuTagMultiselectWidget. + // This could be due to an older version of OOUI. + // Use a normal textarea and remove the loading input. + if ( ( /^1\.34\./ ).test( mediaWikiVersion ) ) { + $widgets.find( '.oo-ui-textInputWidget' ).last().remove(); + return; + } + + configureLanguageInput( $( '.mw-tpt-sp-markform' ), $widgets ); + } +} ); diff --git a/MLEB/Translate/resources/js/ext.translate.special.searchtranslations.js b/MLEB/Translate/resources/js/ext.translate.special.searchtranslations.js index 546e2612..e3f5ecb8 100644 --- a/MLEB/Translate/resources/js/ext.translate.special.searchtranslations.js +++ b/MLEB/Translate/resources/js/ext.translate.special.searchtranslations.js @@ -6,21 +6,21 @@ $( function () { resultGroups = $( '.facet.groups' ).data( 'facets' ); - $( '.tux-searchpage .button' ).on( 'click', function () { + $( '.tux-searchpage .mw-ui-button' ).on( 'click', function () { var query = $( '.tux-searchpage .searchinputbox' ).val(), result = lexOperators( query ), $form = $( '.tux-searchpage form[name=searchform]' ); - $.each( result, function ( index, value ) { + Object.keys( result ).forEach( function ( index ) { var $input = $( '<input>' ).prop( 'type', 'hidden' ), $elem = $form.find( 'input[name=' + index + ']' ); if ( $elem.length ) { - $elem.val( value ); + $elem.val( result[ index ] ); } else { $form.append( $input .prop( { - value: value, + value: result[ index ], name: index } ) ); @@ -135,7 +135,7 @@ ); } - $.each( Object.keys( languages ), function ( index, languageCode ) { + Object.keys( languages ).forEach( function ( languageCode ) { ulslanguages[ languageCode ] = mw.config.get( 'wgTranslateLanguages' )[ languageCode ]; } ); @@ -191,7 +191,6 @@ currentGroup = $( '.facet.groups' ).data( 'group' ), resultCount = groupList.length, position, - groups, options, grouppath; @@ -282,32 +281,30 @@ options = { language: mw.config.get( 'wgUserLanguage' ), position: position, - onSelect: function ( group ) { - var uri = new mw.Uri( location.href ); - uri.extend( { group: group.id, grouppath: group.id } ); - location.href = uri.toString(); + onSelect: function ( selectedGroup ) { + var currentUri = new mw.Uri( location.href ); + currentUri.extend( { group: selectedGroup.id, grouppath: selectedGroup.id } ); + location.href = currentUri.toString(); }, preventSelector: true }; - groups = $.map( resultGroups, function ( value, index ) { - return index; - } ); + $grouSelectorTrigger.msggroupselector( options, - groups + Object.keys( resultGroups ) ); } } function lexOperators( str ) { - var string = str.split( ' ' ), + var splitValues = str.split( ' ' ), result = {}, query = ''; - $.each( string, function ( index, value ) { - matchOperators( value, function ( obj ) { + splitValues.forEach( function ( string ) { + matchOperators( string, function ( obj ) { if ( obj === false ) { - query = query + ' ' + value; + query = query + ' ' + string; } else { result[ obj.operator ] = obj.value; } @@ -324,7 +321,7 @@ // Add operators for different filters operatorRegex = [ 'language', 'group', 'filter' ]; - $.each( operatorRegex, function ( index, value ) { + operatorRegex.forEach( function ( value ) { var regex = new RegExp( value + ':(\\S+)', 'i' ); if ( ( matches = regex.exec( str ) ) !== null ) { counter = true; @@ -354,6 +351,7 @@ /** * Finds a specific group from a groups object containing nested groups. + * * @param {string} targetGroupId * @param {Object} groups * @return {Object} Message group object, or null if group is not found diff --git a/MLEB/Translate/resources/js/ext.translate.special.translate.js b/MLEB/Translate/resources/js/ext.translate.special.translate.js index 63cac603..1b80061a 100644 --- a/MLEB/Translate/resources/js/ext.translate.special.translate.js +++ b/MLEB/Translate/resources/js/ext.translate.special.translate.js @@ -70,8 +70,8 @@ uri.extend( params ); // Support removing keys from the query - $.each( params, function ( key, val ) { - if ( val === null ) { + Object.keys( params ).forEach( function ( key ) { + if ( params[ key ] === null ) { delete uri.query[ key ]; } } ); @@ -143,18 +143,18 @@ /** * Updates all group specific stuff on the page. * - * @param {Object} state Information about current group and language. - * @param {string} state.group Message group id. - * @param {string} state.language Language. + * @param {Object} stateInfo Information about current group and language. + * @param {string} stateInfo.group Message group id. + * @param {string} stateInfo.language Language. */ - function updateGroupInformation( state ) { + function updateGroupInformation( stateInfo ) { var props = 'id|priority|prioritylangs|priorityforce|description'; - mw.translate.recentGroups.append( state.group ); + mw.translate.recentGroups.append( stateInfo.group ); - mw.translate.getMessageGroup( state.group, props ).done( function ( group ) { + mw.translate.getMessageGroup( stateInfo.group, props ).done( function ( group ) { updateDescription( group ); - updateGroupWarning( group, state.language ); + updateGroupWarning( group, stateInfo.language ); } ); } @@ -179,37 +179,50 @@ } function updateGroupWarning( group, language ) { - var preferredLanguages, headerMessage, languagesMessage, + var $preferredLanguages, headerMessage, languagesMessage, $groupWarning = $( '.tux-editor-header .group-warning' ); - if ( isPriorityLanguage( language, group.prioritylangs ) ) { + if ( !group.prioritylangs || isPriorityLanguage( language, group.prioritylangs ) ) { return; } // Make a comma-separated list of preferred languages - preferredLanguages = $.map( group.prioritylangs, function ( lang ) { + $preferredLanguages = $( '<span>' ); + group.prioritylangs.forEach( function ( languageCode, index ) { // bidi isolation for language names - return '<bdi>' + $.uls.data.getAutonym( lang ) + '</bdi>'; - } ).join( ', ' ); + $preferredLanguages.append( + $( '<bdi>' ).text( $.uls.data.getAutonym( languageCode ) ) + ); - headerMessage = mw.message( - group.priorityforce ? - 'tpt-discouraged-language-force-header' : - 'tpt-discouraged-language-header', - $.uls.data.getAutonym( language ) - ).parse(); + // Add comma between languages + if ( index + 1 !== group.prioritylangs.length ) { + $preferredLanguages.append( ', ' ); + } + } ); - languagesMessage = mw.message( - group.priorityforce ? - 'tpt-discouraged-language-force-content' : + if ( group.priorityforce ) { + headerMessage = mw.message( + 'tpt-discouraged-language-force-header', + $.uls.data.getAutonym( language ) + ); + languagesMessage = mw.message( + 'tpt-discouraged-language-force-content', + $preferredLanguages + ); + } else { + headerMessage = mw.message( + 'tpt-discouraged-language-header', + $.uls.data.getAutonym( language ) + ); + languagesMessage = mw.message( 'tpt-discouraged-language-content', - preferredLanguages - ).parse(); + $preferredLanguages + ); + } $groupWarning.append( - $( '<p>' ).append( $( '<strong>' ).text( headerMessage ) ), - // html because of the <bdi> and because it's parsed - $( '<p>' ).html( languagesMessage ) + $( '<p>' ).append( $( '<strong>' ).text( headerMessage.text() ) ), + $( '<p>' ).append( languagesMessage.parseDom() ) ); } @@ -388,11 +401,11 @@ } ); $( '#tux-option-optional' ).on( 'change', function () { - var uri = new mw.Uri( window.location.href ), + var currentUri = new mw.Uri( window.location.href ), checked = $( this ).prop( 'checked' ); mw.translate.changeUrl( { optional: checked ? 1 : 0 } ); - mw.translate.changeFilter( uri.query.filter ); + mw.translate.changeFilter( currentUri.query.filter ); } ); } ); diff --git a/MLEB/Translate/resources/js/ext.translate.special.translationstash.js b/MLEB/Translate/resources/js/ext.translate.special.translationstash.js index 6a9b8587..d4356923 100644 --- a/MLEB/Translate/resources/js/ext.translate.special.translationstash.js +++ b/MLEB/Translate/resources/js/ext.translate.special.translationstash.js @@ -177,7 +177,7 @@ var $untranslated, messages = result.query.messagecollection; $messageTable.empty(); - $.each( messages, function ( index, message ) { + messages.forEach( function ( message ) { message.properties = {}; message.properties.status = 'untranslated'; @@ -246,10 +246,9 @@ translationStashStorage.getUserTranslations() .done( function ( translations ) { if ( translations.translationstash.translations ) { - $.each( translations.translationstash.translations, - function ( index, translation ) { - userTranslations[ translation.title ] = translation; - } ); + translations.translationstash.translations.forEach( function ( translation ) { + userTranslations[ translation.title ] = translation; + } ); } loadMessages(); } ); diff --git a/MLEB/Translate/resources/js/ext.translate.special.translationstats.js b/MLEB/Translate/resources/js/ext.translate.special.translationstats.js index bf6408a5..093be515 100644 --- a/MLEB/Translate/resources/js/ext.translate.special.translationstats.js +++ b/MLEB/Translate/resources/js/ext.translate.special.translationstats.js @@ -1,41 +1,128 @@ +'use strict'; +/* eslint-disable no-implicit-globals */ + /*! - * JavaScript functions for embedding jQuery controls - * into translation notification form. - * + * Display translation stats via a form. * @author Amir E. Aharoni * @author Siebrand Mazeland * @author Niklas Laxström * @copyright Copyright © 2012-2013 Amir E. Aharoni, Siebrand Mazeland * @license GPL-2.0-or-later */ +var FormHandler = function () { + var $form = $( '#translationStatsConfig' ), + onFilter = null; -( function () { - 'use strict'; + function getHeight() { + return parseInt( $form.find( 'input[name="height"]' ).val(), 10 ); + } - $( function () { - var widget, defaultValue, defaultDate, - $input = $( '#start' ); + function getWidth() { + return parseInt( $form.find( 'input[name="width"]' ).val(), 10 ); + } - defaultDate = new Date(); - defaultDate.setDate( 1 ); + function getLanguages() { + var languages = $form.find( 'input[name="language"]' ).val().trim(); - if ( $input.val() ) { - defaultValue = new Date( $input.val() ); + if ( languages.length > 0 ) { + return getSplitValues( languages ); } - widget = new mw.widgets.datetime.DateTimeInputWidget( { - formatter: { - format: '${year|0}-${month|0}-${day|0}', - defaultDate: defaultDate - }, - type: 'date', - value: defaultValue, - max: new Date() - } ); + return []; + } + + function getGroups() { + var groups = $form.find( 'input[name="group"]' ).val().trim(); + + if ( groups.length > 0 ) { + return getSplitValues( groups ); + } + + return []; + } + + function getGranularity() { + return $form.find( 'input[name="scale"]:checked' ).val().trim(); + } + + function getMeasure() { + return $form.find( 'input[name="count"]:checked' ).val().trim(); + } + + function getDays() { + return parseInt( $form.find( 'input[name="days"]' ).val(), 10 ); + } + + function getStart() { + return $form.find( '#start' ).val(); + } + + function getAllOptions() { + return { + measure: getMeasure(), + days: getDays(), + start: getStart(), + granularity: getGranularity(), + group: getGroups(), + language: getLanguages(), + height: getHeight(), + width: getWidth() + }; + } - $input.after( widget.$element ).hide(); - widget.on( 'change', function ( data ) { - $input.val( data + 'T00:00:00' ); + function getSplitValues( values ) { + return values.split( ',' ).map( function ( value ) { + return value.trim(); } ); + } + + function filter() { + if ( !this.onFilter ) { + return; + } + + this.onFilter( this.getAllOptions() ); + } + + return { + onFilter: onFilter, + filter: filter, + getAllOptions: getAllOptions + }; +}; + +$( function () { + var widget, defaultValue, defaultDate, + $input = $( '#start' ), + formHandler = new FormHandler(), + $graphContainer = $( '#translationStatsGraphContainer' ), + graphBuilder; + + defaultDate = new Date(); + defaultDate.setDate( 1 ); + + if ( $input.val() ) { + defaultValue = new Date( $input.val() ); + } + + widget = new mw.widgets.datetime.DateTimeInputWidget( { + formatter: { + format: '${year|0}-${month|0}-${day|0}', + defaultDate: defaultDate + }, + type: 'date', + value: defaultValue, + max: new Date() } ); -}() ); + + $input.after( widget.$element ).addClass( 'mw-translate-translationstats-hide' ); + widget.on( 'change', function ( data ) { + $input.val( data + 'T00:00:00' ); + } ); + + // Check if the graph container has been loaded + if ( $graphContainer.length !== 0 ) { + graphBuilder = new mw.translate.TranslationStatsGraphBuilder( $graphContainer ); + graphBuilder.display( formHandler.getAllOptions() ); + } +} ); diff --git a/MLEB/Translate/resources/js/ext.translate.statsbar.js b/MLEB/Translate/resources/js/ext.translate.statsbar.js index aeec9314..43ba29da 100644 --- a/MLEB/Translate/resources/js/ext.translate.statsbar.js +++ b/MLEB/Translate/resources/js/ext.translate.statsbar.js @@ -175,7 +175,7 @@ data = $this.data( 'languagestatsbar' ); if ( !data ) { - $this.data( 'languagestatsbar', ( data = new LanguageStatsBar( this, options ) ) ); + $this.data( 'languagestatsbar', new LanguageStatsBar( this, options ) ); } } ); }; diff --git a/MLEB/Translate/resources/js/ext.translate.translationstats.embedded.js b/MLEB/Translate/resources/js/ext.translate.translationstats.embedded.js new file mode 100644 index 00000000..b0ec95fd --- /dev/null +++ b/MLEB/Translate/resources/js/ext.translate.translationstats.embedded.js @@ -0,0 +1,86 @@ +'use strict'; +/* eslint-disable no-implicit-globals */ + +/*! + * Used to embed translation stats graph on other pages. + * @license GPL-2.0-or-later + */ +var EmbeddedHandler = function ( $graphContainer ) { + var graphOptions = JSON.parse( + $graphContainer.find( '[name="translationStatsGraphOptions"]' ).val() + ); + + function getHeight() { + return parseInt( graphOptions.height, 10 ); + } + + function getWidth() { + return parseInt( graphOptions.width, 10 ); + } + + function getAllOptions() { + return { + measure: graphOptions.count, + days: graphOptions.days, + start: graphOptions.start, + granularity: graphOptions.scale, + group: graphOptions.group, + language: graphOptions.language, + height: getHeight(), + width: getWidth() + }; + } + + return { + getAllOptions: getAllOptions + }; +}; + +$( function () { + var $graphContainers = $( '.mw-translate-translationstats-container' ), + currentGraph = 0, + graphInstances = []; + + function loadGraph() { + var currentGraphBuilder = graphInstances[ currentGraph ].graphBuilder, + currentOptions = graphInstances[ currentGraph ].options.getAllOptions(); + + currentGraphBuilder + .display( currentOptions ) + .always( function () { + ++currentGraph; + if ( currentGraph < graphInstances.length ) { + loadGraph(); + } + } ); + } + + // Create graph and options instances, then display loader + function initGraph( $graphContainer ) { + var graphBuilder, + graphOptions; + + graphOptions = new EmbeddedHandler( $graphContainer ); + graphBuilder = new mw.translate.TranslationStatsGraphBuilder( + $graphContainer, graphOptions.getAllOptions() + ); + graphBuilder.showLoading(); + + return { + graphBuilder: graphBuilder, + options: graphOptions + }; + } + + for ( ;currentGraph < $graphContainers.length; ++currentGraph ) { + graphInstances.push( + initGraph( $graphContainers.eq( currentGraph ) ) + ); + } + + currentGraph = 0; + setTimeout( function () { + // Give time to display the loaders. + loadGraph(); + } ); +} ); diff --git a/MLEB/Translate/resources/js/ext.translate.translationstats.graphbuilder.js b/MLEB/Translate/resources/js/ext.translate.translationstats.graphbuilder.js new file mode 100644 index 00000000..f5280041 --- /dev/null +++ b/MLEB/Translate/resources/js/ext.translate.translationstats.graphbuilder.js @@ -0,0 +1,346 @@ +/*! + * Graph component to display translation stats using ChartJS + * @license GPL-2.0-or-later + */ + +( function () { + 'use strict'; + var graphInfo = { + edits: mw.msg( 'translate-statsf-count-edits' ), + users: mw.msg( 'translate-statsf-count-users' ), + registrations: mw.msg( 'translate-statsf-count-registrations' ), + reviews: mw.msg( 'translate-statsf-count-reviews' ), + reviewers: mw.msg( 'translate-statsf-count-reviewers' ) + }, granularityInfo = { + months: mw.msg( 'translate-statsf-scale-months' ), + weeks: mw.msg( 'translate-statsf-scale-weeks' ), + days: mw.msg( 'translate-statsf-scale-days' ), + hours: mw.msg( 'translate-statsf-scale-hours' ) + }, graphColors = [ + 'skyblue', 'green', 'orange', 'blue', 'red', 'darkgreen', 'purple', 'peru', + 'cyan', 'salmon', 'slateblue', 'yellowgreen', 'magenta', 'aquamarine', 'gold', 'violet' + ], + GraphBuilder; + + /** + * Used to display translation stats graph. Each instance of this class manages one + * instance of the graph. + * + * @param {Object} $graphContainer The title of the page including language code + * to store the translation. + * @param {Object} graphOptions Graph options, current only processes the width and height. + * @return {Object} Instance of the graph builder + */ + GraphBuilder = function ( $graphContainer, graphOptions ) { + var $graphElement = $( '<canvas>' ) + .attr( 'class', 'mw-translate-translationstats-graph' ) + .attr( 'role', 'img' ) + .attr( 'tabindex', 0 ) + .text( mw.msg( 'translate-statsf-graph-alt-text-info' ) ), + $graphWrapper = $( '<div>' ) + .attr( 'class', 'mw-translationstats-graph-container' ), + $loadingElement = $( '<div>' ) + .attr( 'class', 'mw-translate-loading-spinner' ), + $errorElement = $( '<div>' ) + .attr( 'class', 'mw-translate-error-container' ), + $tableElement = $( '<table>' ) + .addClass( 'wikitable mw-translate-translationstats-table' ) + .attr( 'tabindex', 0 ) + .attr( + 'summary', mw.msg( 'translate-statsf-alt-text' ) + ), + lineChart; + + $graphWrapper + .width( graphOptions && graphOptions.width ) + .height( graphOptions && graphOptions.height ); + + // Set the container height and width if passed. + $graphContainer.append( [ + $graphWrapper, + $loadingElement, + $errorElement + ] ); + + $graphWrapper.append( $graphElement ); + + function display( options ) { + if ( lineChart ) { + cleanup(); + } + + // Set the appropriate height and width and display the loader. + $graphWrapper + .width( options.width ) + .height( options.height ); + + showLoading(); + + return getData( options ).then( function ( graphData ) { + // Hide the loader before displaying the data. + showData( graphData, options ); + } ).fail( function ( errorCode, results ) { + var errorInfo = results && results.error ? results.error.info : + mw.msg( 'translate-statsf-unknown-error' ); + displayError( mw.msg( 'translate-statsf-error-message', errorInfo ) ); + } ).always( function () { + hideLoading(); + } ); + } + + function showData( apiResponse, options ) { + var graphData = getAxesLabelsAndData( apiResponse.data ), + graphDatasets = [], + datasetLabels = apiResponse.labels; + + if ( graphData.data.length ) { + graphData.data.forEach( function ( dataset, datasetIndex ) { + var graphDataset = { + data: dataset, + fill: false, + borderColor: getLineColor( datasetIndex ) + }; + + if ( datasetLabels[ datasetIndex ] ) { + graphDataset.label = datasetLabels[ datasetIndex ]; + } + + graphDatasets.push( graphDataset ); + } ); + } + + lineChart = new Chart( $graphElement, { + type: 'line', + data: { + labels: graphData.axesLabels, + datasets: graphDatasets + }, + options: { + maintainAspectRatio: false, + legend: { + display: datasetLabels.length !== 0 + }, + scales: { + yAxes: [ { + scaleLabel: { + display: true, + labelString: getXAxesLabel( options.measure ) + }, + ticks: { + beginAtZero: true, + precision: 0, + callback: function ( value ) { + return mw.language.convertNumber( Number( value ) ); + } + } + } ], + xAxes: [ { + scaleLabel: { + display: true, + labelString: getYAxesLabel( options.granularity ) + }, + ticks: { + maxTicksLimit: 15 + }, + gridLines: { + display: false + } + } ] + }, + tooltips: { + callbacks: { + label: function ( tooltipItem, data ) { + var convertedValue = mw.language.convertNumber( Number( tooltipItem.yLabel ) ), + label = data.datasets[ tooltipItem.datasetIndex ].label; + + if ( label ) { + return label + ': ' + convertedValue; + } + + return convertedValue; + } + } + } + } + } ); + + // Generate table inside the canvas element to improve accessibility. + showTable( graphData, datasetLabels, options ); + } + + function getAxesLabelsAndData( jsonGraphData ) { + var labelProp, labels = [], graphData = [], + labelData, i, labelIndex = 0, + currentValue, maxValue = 0, minValue = 0; + + for ( labelProp in jsonGraphData ) { + if ( labels.indexOf( labelProp ) === -1 ) { + labels.push( labelProp ); + } + + labelData = jsonGraphData[ labelProp ]; + + for ( i = 0; i < labelData.length; ++i ) { + if ( !graphData[ i ] ) { + graphData[ i ] = []; + } + + currentValue = labelData[ i ]; + graphData[ i ][ labelIndex ] = currentValue; + if ( currentValue < minValue ) { + minValue = currentValue; + } + + if ( currentValue > maxValue ) { + maxValue = currentValue; + } + } + + ++labelIndex; + } + + return { + axesLabels: labels, + data: graphData, + max: maxValue, + min: minValue + }; + } + + function getXAxesLabel( measure ) { + return graphInfo[ measure ]; + } + + function getYAxesLabel( granularity ) { + return granularityInfo[ granularity ]; + } + + function getData( filterOptions ) { + var api = new mw.Api(), + apiParams = { + action: 'translationstats', + count: filterOptions.measure, + days: filterOptions.days, + start: filterOptions.start || null, + scale: filterOptions.granularity, + group: filterOptions.group, + language: filterOptions.language, + formatversion: 2 + }; + + // Remove null or empty array from request object + Object.keys( apiParams ).forEach( function ( apiParamKey ) { + var apiParamValue = apiParams[ apiParamKey ]; + if ( + apiParamValue === null || + ( Array.isArray( apiParamValue ) && apiParamValue.length === 0 ) + ) { + delete apiParams[ apiParamKey ]; + } + } ); + + return api.get( apiParams ).then( function ( result ) { + return result.translationstats; + } ); + } + + function getLineColor( index ) { + var colorIndex = index % graphColors.length, + colorName = graphColors[ colorIndex ]; + return colorName; + } + + function displayError( errorMessage ) { + $errorElement.text( errorMessage ); + $graphContainer.addClass( 'mw-translate-has-error' ) + .height( 'auto' ); + } + + function showLoading() { + // show loading, and hide error messages. + $graphContainer.addClass( 'mw-translate-loading' ) + .removeClass( 'mw-translate-has-error' ); + } + + function hideLoading() { + $graphContainer.removeClass( 'mw-translate-loading' ); + } + + function showTable( graphData, datasetLabels, options ) { + $tableElement + .append( + $( '<caption>' ).text( getGraphSummary( options ) ) + ) + .append( getTableHead( datasetLabels, options ) ) + .append( getTableBody( graphData ) ); + + $graphContainer.append( $tableElement ); + } + + function getTableHead( datasetLabels, options ) { + var $tableHead = $( '<thead>' ), + $tableHeadRow = $( '<tr>' ), + i = 0; + + $tableHeadRow.append( $( '<th>' ).text( getYAxesLabel( options.granularity ) ) ); + + if ( datasetLabels && datasetLabels.length ) { + for ( ; i < datasetLabels.length; ++i ) { + $tableHeadRow.append( $( '<th>' ).text( datasetLabels[ i ] ) ); + } + } else { + $tableHeadRow.append( $( '<th>' ).text( getXAxesLabel( options.measure ) ) ); + } + + return $tableHead.append( $tableHeadRow ); + } + + function getTableBody( graphData ) { + var $tbody = $( '<tbody>' ), + scaleIndex, datasetIndex, $tBodyRow, + columnValue; + + for ( scaleIndex = 0; scaleIndex < graphData.axesLabels.length; scaleIndex++ ) { + $tBodyRow = $( '<tr>' ) + .append( $( '<td>' ).text( graphData.axesLabels[ scaleIndex ] ) ); + + for ( datasetIndex = 0; datasetIndex < graphData.data.length; datasetIndex++ ) { + columnValue = ''; + if ( + graphData.data[ datasetIndex ] && + graphData.data[ datasetIndex ][ scaleIndex ] !== undefined + ) { + columnValue = + mw.language.convertNumber( + Number( graphData.data[ datasetIndex ][ scaleIndex ] ) + ); + } + $tBodyRow.append( $( '<td>' ).text( columnValue ) ); + } + + $tbody.append( $tBodyRow ); + } + + return $tbody; + } + + function cleanup() { + lineChart.destroy(); + $tableElement.remove(); + } + + function getGraphSummary( options ) { + return getXAxesLabel( options.measure ) + ' / ' + + getYAxesLabel( options.granularity ); + } + + return { + display: display, + showLoading: showLoading, + hideLoading: hideLoading + }; + }; + + mw.translate = mw.translate || {}; + mw.translate.TranslationStatsGraphBuilder = mw.translate.TranslationStatsGraphBuilder || GraphBuilder; +}() ); diff --git a/MLEB/Translate/resources/js/ext.translate.workflowselector.js b/MLEB/Translate/resources/js/ext.translate.workflowselector.js index d5e64766..b9d86f67 100644 --- a/MLEB/Translate/resources/js/ext.translate.workflowselector.js +++ b/MLEB/Translate/resources/js/ext.translate.workflowselector.js @@ -104,23 +104,23 @@ e.stopPropagation(); } ); - $.each( this.states, function ( id, data ) { - var $state; + Object.keys( instance.states ).forEach( function ( key ) { + var data = instance.states[ key ], $state; // Store the id also - data.id = id; + data.id = key; $state = $( '<li>' ) .data( 'state', data ) .text( data.name ); - if ( data.canchange && id !== instance.currentState ) { + if ( data.canchange && data.id !== instance.currentState ) { $state.addClass( 'changeable' ); } else { $state.addClass( 'unchangeable' ); } - if ( id === instance.currentState ) { + if ( data.id === instance.currentState ) { $display.text( instance.getStateDisplay( data.name ) ); $display.append( $( '<span>' ).addClass( 'tux-workflow-status-triangle' ) ); $state.addClass( 'selected' ); @@ -157,7 +157,7 @@ data = $this.data( 'workflowselector' ); if ( !data ) { - $this.data( 'workflowselector', ( data = new WorkflowSelector( this ) ) ); + $this.data( 'workflowselector', new WorkflowSelector( this ) ); } $this.data( 'workflowselector' ).receiveState( groupId, language, state ); } ); |