File: ve.ui.MWEditSummaryWidget.js

package info (click to toggle)
mediawiki 1%3A1.35.13-1%2Bdeb11u2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 274,932 kB
  • sloc: php: 677,563; javascript: 572,709; sql: 11,565; python: 4,447; xml: 3,145; sh: 892; perl: 788; ruby: 496; pascal: 365; makefile: 128
file content (177 lines) | stat: -rw-r--r-- 4,982 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*!
 * VisualEditor UserInterface MWEditSummaryWidget class.
 *
 * @copyright 2011-2018 VisualEditor Team and others; see http://ve.mit-license.org
 */

/**
 * Multi line text input for edit summary, with auto completion based on
 * the user's previous edit summaries.
 *
 * @class
 * @extends OO.ui.MultilineTextInputWidget
 * @mixins OO.ui.mixin.LookupElement
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {number} [limit=6] Number of suggestions to show
 */
ve.ui.MWEditSummaryWidget = function VeUiMWEditSummaryWidget( config ) {
	config = config || {};

	// Parent method
	ve.ui.MWEditSummaryWidget.super.apply( this, arguments );

	// Mixin method
	OO.ui.mixin.LookupElement.call( this, ve.extendObject( {
		showPendingRequest: false,
		showSuggestionsOnFocus: false,
		allowSuggestionsWhenEmpty: false,
		highlightFirst: false
	}, config ) );

	this.limit = config.limit || 6;
};

/* Inheritance */

OO.inheritClass( ve.ui.MWEditSummaryWidget, OO.ui.MultilineTextInputWidget );

OO.mixinClass( ve.ui.MWEditSummaryWidget, OO.ui.mixin.LookupElement );

/* Static properties */

ve.ui.MWEditSummaryWidget.static.summarySplitter = /^(\/\*.*?\*\/\s*)?(.*)$/;

/* Static methods */

/**
 * Split a summary into the section and the actual summary
 *
 * @param {string} summary Summary
 * @return {Object} Object with section and comment string properties
 */
ve.ui.MWEditSummaryWidget.static.splitSummary = function ( summary ) {
	var result = summary.match( this.summarySplitter );
	return {
		section: result[ 1 ] || '',
		comment: result[ 2 ]
	};
};

/**
 * Filter a list of edit summaries to a specific query stirng
 *
 * @param {string[]} summaries Edit summaries
 * @param {string} query User query
 * @return {string[]} Filtered edit summaries
 */
ve.ui.MWEditSummaryWidget.static.getMatchingSummaries = function ( summaries, query ) {
	var summaryPrefixMatches = [], wordPrefixMatches = [], otherMatches = [],
		lowerQuery = query.toLowerCase();

	if ( !query.trim() ) {
		// Show no results for empty query
		return [];
	}

	summaries.forEach( function ( summary ) {
		var lowerSummary = summary.toLowerCase(),
			index = lowerSummary.indexOf( lowerQuery );
		if ( index === 0 ) {
			// Exclude exact matches
			if ( lowerQuery !== summary ) {
				summaryPrefixMatches.push( summary );
			}
		} else if ( index !== -1 ) {
			if ( lowerSummary[ index - 1 ].match( /\s/ ) ) {
				// Character before match is whitespace
				wordPrefixMatches.push( summary );
			} else {
				otherMatches.push( summary );
			}
		}
	} );
	return summaryPrefixMatches.concat( wordPrefixMatches, otherMatches );
};

/* Methods */

/**
 * Get recent edit summaries for the logged in user
 *
 * @return {jQuery.Promise} Promise which resolves with a list of summaries
 */
ve.ui.MWEditSummaryWidget.prototype.getSummaries = function () {
	var splitSummary = this.constructor.static.splitSummary.bind( this.constructor.static );
	if ( !this.getSummariesPromise ) {
		if ( mw.user.isAnon() ) {
			this.getSummariesPromise = ve.createDeferred().resolve( [] ).promise();
		} else {
			this.getSummariesPromise = ve.init.target.getLocalApi().get( {
				action: 'query',
				list: 'usercontribs',
				ucuser: mw.user.getName(),
				ucprop: 'comment|title',
				uclimit: 500,
				format: 'json'
			} ).then( function ( response ) {
				var usedComments = {},
					changes = ve.getProp( response, 'query', 'usercontribs' ) || [];

				return changes
					// Remove section /* headings */
					.map( function ( change ) {
						return splitSummary( change.comment ).comment.trim();
					} )
					// Filter out duplicates and empty comments
					.filter( function ( comment ) {
						if ( !comment || Object.prototype.hasOwnProperty.call( usedComments, comment ) ) {
							return false;
						}
						usedComments[ comment ] = true;
						return true;
					} )
					.sort();
			} );
		}
	}
	return this.getSummariesPromise;
};

/**
 * @inheritdoc
 */
ve.ui.MWEditSummaryWidget.prototype.getLookupRequest = function () {
	var query = this.constructor.static.splitSummary( this.value ),
		limit = this.limit,
		widget = this;

	return this.getSummaries().then( function ( allSummaries ) {
		var matchingSummaries = widget.constructor.static.getMatchingSummaries( allSummaries, query.comment );
		if ( matchingSummaries.length > limit ) {
			// Quick in-place truncate
			matchingSummaries.length = limit;
		}
		return { summaries: matchingSummaries, section: query.section };
	} ).promise( { abort: function () {} } ); // don't abort, the actual request will be the same anyway
};

/**
 * @inheritdoc
 */
ve.ui.MWEditSummaryWidget.prototype.getLookupCacheDataFromResponse = function ( response ) {
	return response;
};

/**
 * @inheritdoc
 */
ve.ui.MWEditSummaryWidget.prototype.getLookupMenuOptionsFromData = function ( data ) {
	return data.summaries.map( function ( item ) {
		return new OO.ui.MenuOptionWidget( {
			label: item,
			data: data.section + item
		} );
	} );
};