summaryrefslogtreecommitdiff
path: root/www/wiki/resources/src/mediawiki.action/mediawiki.action.edit.stash.js
blob: 5ae91e8564bd47a13d3d88d1b55fc5406f4eeed0 (plain)
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
/*!
 * Scripts for pre-emptive edit preparing on action=edit
 */
/* eslint-disable no-use-before-define */
( function ( mw, $ ) {
	if ( !mw.config.get( 'wgAjaxEditStash' ) ) {
		return;
	}

	$( function () {
		var idleTimeout = 3000,
			api = new mw.Api(),
			timer,
			stashReq,
			lastText,
			lastSummary,
			lastTextHash,
			$form = $( '#editform' ),
			$text = $form.find( '#wpTextbox1' ),
			$summary = $form.find( '#wpSummary' ),
			section = $form.find( '[name=wpSection]' ).val(),
			model = $form.find( '[name=model]' ).val(),
			format = $form.find( '[name=format]' ).val(),
			revId = $form.find( '[name=parentRevId]' ).val(),
			lastPriority = 0,
			PRIORITY_LOW = 1,
			PRIORITY_HIGH = 2;

		// We don't attempt to stash new section edits because in such cases the parser output
		// varies on the edit summary (since it determines the new section's name).
		if ( !$form.length || section === 'new' ) {
			return;
		}

		// Send a request to stash the edit to the API.
		// If a request is in progress, abort it since its payload is stale and the API
		// may limit concurrent stash parses.
		function stashEdit() {
			var req, params,
				textChanged = isTextChanged(),
				priority = textChanged ? PRIORITY_HIGH : PRIORITY_LOW;

			if ( stashReq ) {
				if ( lastPriority > priority ) {
					// Stash request for summary change should wait on pending text change stash
					stashReq.then( checkStash );
					return;
				}
				stashReq.abort();
			}

			// Update the "last" tracking variables
			lastSummary = $summary.textSelection( 'getContents' );
			lastPriority = priority;
			if ( textChanged ) {
				lastText = $text.textSelection( 'getContents' );
				// Reset hash
				lastTextHash = null;
			}

			params = {
				formatversion: 2,
				action: 'stashedit',
				title: mw.config.get( 'wgPageName' ),
				section: section,
				sectiontitle: '',
				summary: lastSummary,
				contentmodel: model,
				contentformat: format,
				baserevid: revId
			};
			if ( lastTextHash ) {
				params.stashedtexthash = lastTextHash;
			} else {
				params.text = lastText;
			}

			req = api.postWithToken( 'csrf', params );
			stashReq = req;
			req.then( function ( data ) {
				if ( req === stashReq ) {
					stashReq = null;
				}
				if ( data.stashedit && data.stashedit.texthash ) {
					lastTextHash = data.stashedit.texthash;
				} else {
					// Request failed or text hash expired;
					// include the text in a future stash request.
					lastTextHash = null;
				}
			} );
		}

		// Whether the body text content changed since the last stashEdit()
		function isTextChanged() {
			return lastText !== $text.textSelection( 'getContents' );
		}

		// Whether the edit summary has changed since the last stashEdit()
		function isSummaryChanged() {
			return lastSummary !== $summary.textSelection( 'getContents' );
		}

		// Check whether text or summary have changed and call stashEdit()
		function checkStash() {
			if ( !isTextChanged() && !isSummaryChanged() ) {
				return;
			}

			stashEdit();
		}

		function onKeyUp( e ) {
			// Ignore keystrokes that don't modify text, like cursor movements.
			// See <http://www.javascripter.net/faq/keycodes.htm> and
			// <http://www.quirksmode.org/js/keys.html>. We don't have to be exhaustive,
			// because the cost of misfiring is low.
			// * Key code 33-40: Page Up/Down, End, Home, arrow keys.
			// * Key code 16-18: Shift, Ctrl, Alt.
			if ( ( e.which >= 33 && e.which <= 40 ) || ( e.which >= 16 && e.which <= 18 ) ) {
				return;
			}

			clearTimeout( timer );
			timer = setTimeout( checkStash, idleTimeout );
		}

		function onSummaryFocus() {
			// Summary typing is usually near the end of the workflow and involves less pausing.
			// Re-stash more frequently in hopes of capturing the final summary before submission.
			idleTimeout = 1000;
			// Stash now since the text is likely the final version. The re-stashes based on the
			// summary are targeted at caching edit checks that need the final summary.
			checkStash();
		}

		function onTextFocus() {
			// User returned to the text field... reset stash rate to default
			idleTimeout = 3000;
		}

		$text.on( {
			keyup: onKeyUp,
			focus: onTextFocus,
			change: checkStash
		} );
		$summary.on( {
			keyup: onKeyUp,
			focus: onSummaryFocus,
			focusout: checkStash
		} );

		if (
			// Reverts may involve use (undo) links; stash as they review the diff.
			// Since the form has a pre-filled summary, stash the edit immediately.
			mw.util.getParamValue( 'undo' ) !== null ||
			// Pressing "show changes" and "preview" also signify that the user will
			// probably save the page soon
			[ 'preview', 'diff' ].indexOf( $form.find( '#mw-edit-mode' ).val() ) > -1
		) {
			checkStash();
		}
	} );
}( mediaWiki, jQuery ) );