Merge "LoadBalancer object injection cleanups"
[lhc/web/wiklou.git] / resources / src / mediawiki.action / mediawiki.action.edit.stash.js
1 /*!
2 * Scripts for pre-emptive edit preparing on action=edit
3 */
4 ( function ( mw, $ ) {
5 if ( !mw.config.get( 'wgAjaxEditStash' ) ) {
6 return;
7 }
8
9 $( function () {
10 var idleTimeout = 3000,
11 api = new mw.Api(),
12 pending = null,
13 $form = $( '#editform' ),
14 $text = $form.find( '#wpTextbox1' ),
15 $summary = $form.find( '#wpSummary' ),
16 section = $form.find( '[name=wpSection]' ).val(),
17 model = $form.find( '[name=model]' ).val(),
18 format = $form.find( '[name=format]' ).val(),
19 revId = $form.find( '[name=parentRevId]' ).val(),
20 lastText = $text.textSelection( 'getContents' ),
21 lastSummary = $summary.textSelection( 'getContents' ),
22 lastTextHash = null,
23 lastPriority = 0,
24 origSummary = lastSummary,
25 timer = null,
26 PRIORITY_LOW = 1,
27 PRIORITY_HIGH = 2;
28
29 // Send a request to stash the edit to the API.
30 // If a request is in progress, abort it since its payload is stale and the API
31 // may limit concurrent stash parses.
32 function stashEdit( priority, hashForReuse ) {
33 if ( pending ) {
34 pending.abort();
35 pending = null;
36 }
37
38 api.getToken( 'csrf' ).then( function ( token ) {
39 // If applicable, just send the hash key to reuse the last text server-side
40 var req, params;
41
42 // Update tracking of the last text/summary sent out
43 lastText = $text.textSelection( 'getContents' );
44 lastSummary = $summary.textSelection( 'getContents' );
45 lastPriority = priority;
46 lastTextHash = null; // "failed" until proven successful
47
48 params = {
49 action: 'stashedit',
50 token: token,
51 title: mw.config.get( 'wgPageName' ),
52 section: section,
53 sectiontitle: '',
54 summary: lastSummary,
55 contentmodel: model,
56 contentformat: format,
57 baserevid: revId
58 };
59 if ( hashForReuse ) {
60 params.stashedtexthash = hashForReuse;
61 } else {
62 params.text = lastText;
63 }
64
65 req = api.post( params );
66 pending = req;
67 req.then( function ( data ) {
68 if ( req === pending ) {
69 pending = null;
70 }
71 if ( data.stashedit && data.stashedit.texthash ) {
72 lastTextHash = data.stashedit.texthash;
73 }
74 } );
75 } );
76 }
77
78 // Check if edit body text changed since the last stashEdit() call or if no edit
79 // stash calls have yet been made
80 function isTextChanged() {
81 var newText = $text.textSelection( 'getContents' );
82 return newText !== lastText;
83 }
84
85 // Check if summary changed since the last stashEdit() call or if no edit
86 // stash calls have yet been made
87 function isSummaryChanged() {
88 var newSummary = $summary.textSelection( 'getContents' );
89 return newSummary !== lastSummary;
90 }
91
92 function onEditorIdle() {
93 var textChanged = isTextChanged(),
94 summaryChanged = isSummaryChanged(),
95 priority = textChanged ? PRIORITY_HIGH : PRIORITY_LOW;
96
97 if ( !textChanged && !summaryChanged ) {
98 return; // nothing to do
99 }
100
101 if ( pending && lastPriority > priority ) {
102 // Stash requests for summary changes should wait on pending text change stashes
103 pending.then( onEditorIdle );
104 return;
105 }
106
107 stashEdit( priority, textChanged ? null : lastTextHash );
108 }
109
110 function onKeyUp( e ) {
111 // Ignore keystrokes that don't modify text, like cursor movements.
112 // See <http://www.javascripter.net/faq/keycodes.htm> and
113 // <http://www.quirksmode.org/js/keys.html>. We don't have to be exhaustive,
114 // because the cost of misfiring is low.
115 // * Key code 33-40: Page Up/Down, End, Home, arrow keys.
116 // * Key code 16-18: Shift, Ctrl, Alt.
117 if ( ( e.which >= 33 && e.which <= 40 ) || ( e.which >= 16 && e.which <= 18 ) ) {
118 return;
119 }
120
121 clearTimeout( timer );
122 timer = setTimeout( onEditorIdle, idleTimeout );
123 }
124
125 function onSummaryFocus() {
126 // Summary typing is usually near the end of the workflow and involves less pausing.
127 // Re-stash frequently in hopes of capturing the final summary before submission.
128 idleTimeout = 1000;
129 // Stash now since the text is likely the final version. The re-stashes based on the
130 // summary are targeted at caching edit checks that need the final summary.
131 onEditorIdle();
132 }
133
134 function onTextFocus() {
135 // User returned to the text field...
136 if ( $summary.textSelection( 'getContents' ) === origSummary ) {
137 idleTimeout = 3000; // no summary yet; reset stash rate to default
138 }
139 }
140
141 function onFormLoaded() {
142 if (
143 // Reverts may involve use (undo) links; stash as they review the diff.
144 // Since the form has a pre-filled summary, stash the edit immediately.
145 mw.util.getParamValue( 'undo' ) !== null
146 // Pressing "show changes" and "preview" also signify that the user will
147 // probably save the page soon
148 || $.inArray( $form.find( '#mw-edit-mode' ).val(), [ 'preview', 'diff' ] ) > -1
149 ) {
150 stashEdit( PRIORITY_HIGH, null );
151 }
152 }
153
154 // We don't attempt to stash new section edits because in such cases the parser output
155 // varies on the edit summary (since it determines the new section's name).
156 if ( $form.find( 'input[name=wpSection]' ).val() === 'new' ) {
157 return;
158 }
159
160 $text.on( {
161 change: onEditorIdle,
162 keyup: onKeyUp,
163 focus: onTextFocus
164 } );
165 $summary.on( {
166 focus: onSummaryFocus,
167 focusout: onEditorIdle,
168 keyup: onKeyUp
169 } );
170 onFormLoaded();
171 } );
172 }( mediaWiki, jQuery ) );