summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'MLEB/Translate/resources/js')
-rw-r--r--MLEB/Translate/resources/js/LanguagesMultiselectWidget.js123
-rw-r--r--MLEB/Translate/resources/js/ext.translate.base.js7
-rw-r--r--MLEB/Translate/resources/js/ext.translate.editor.helpers.js10
-rw-r--r--MLEB/Translate/resources/js/ext.translate.editor.js18
-rw-r--r--MLEB/Translate/resources/js/ext.translate.groupselector.js20
-rw-r--r--MLEB/Translate/resources/js/ext.translate.messagetable.js16
-rw-r--r--MLEB/Translate/resources/js/ext.translate.multiselectautocomplete.js96
-rw-r--r--MLEB/Translate/resources/js/ext.translate.pagetranslation.uls.js9
-rw-r--r--MLEB/Translate/resources/js/ext.translate.parsers.js4
-rw-r--r--MLEB/Translate/resources/js/ext.translate.proofread.js4
-rw-r--r--MLEB/Translate/resources/js/ext.translate.special.aggregategroups.js21
-rw-r--r--MLEB/Translate/resources/js/ext.translate.special.languagestats.js163
-rw-r--r--MLEB/Translate/resources/js/ext.translate.special.managegroups.js139
-rw-r--r--MLEB/Translate/resources/js/ext.translate.special.managetranslatorsandbox.js8
-rw-r--r--MLEB/Translate/resources/js/ext.translate.special.pagemigration.js38
-rw-r--r--MLEB/Translate/resources/js/ext.translate.special.pagepreparation.js36
-rw-r--r--MLEB/Translate/resources/js/ext.translate.special.pagetranslation.js69
-rw-r--r--MLEB/Translate/resources/js/ext.translate.special.searchtranslations.js36
-rw-r--r--MLEB/Translate/resources/js/ext.translate.special.translate.js73
-rw-r--r--MLEB/Translate/resources/js/ext.translate.special.translationstash.js9
-rw-r--r--MLEB/Translate/resources/js/ext.translate.special.translationstats.js137
-rw-r--r--MLEB/Translate/resources/js/ext.translate.statsbar.js2
-rw-r--r--MLEB/Translate/resources/js/ext.translate.translationstats.embedded.js86
-rw-r--r--MLEB/Translate/resources/js/ext.translate.translationstats.graphbuilder.js346
-rw-r--r--MLEB/Translate/resources/js/ext.translate.workflowselector.js12
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 );
} );