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