Merge "media: Mark ImageHandler::getScriptParams() as protected"
[lhc/web/wiklou.git] / resources / src / mediawiki.api / options.js
1 /**
2 * @class mw.Api.plugin.options
3 */
4 ( function () {
5
6 var saveOptionsRequests = {};
7
8 $.extend( mw.Api.prototype, {
9
10 /**
11 * Asynchronously save the value of a single user option using the API. See #saveOptions.
12 *
13 * @param {string} name
14 * @param {string|null} value
15 * @return {jQuery.Promise}
16 */
17 saveOption: function ( name, value ) {
18 var param = {};
19 param[ name ] = value;
20 return this.saveOptions( param );
21 },
22
23 /**
24 * Asynchronously save the values of user options using the API.
25 *
26 * If a value of `null` is provided, the given option will be reset to the default value.
27 *
28 * Any warnings returned by the API, including warnings about invalid option names or values,
29 * are ignored. However, do not rely on this behavior.
30 *
31 * If necessary, the options will be saved using several sequential API requests. Only one promise
32 * is always returned that will be resolved when all requests complete.
33 *
34 * If a request from a previous #saveOptions call is still pending, this will wait for it to be
35 * completed, otherwise MediaWiki gets sad. No requests are sent for anonymous users, as they
36 * would fail anyway. See T214963.
37 *
38 * @param {Object} options Options as a `{ name: value, … }` object
39 * @return {jQuery.Promise}
40 */
41 saveOptions: function ( options ) {
42 var name, value, bundleable,
43 grouped = [],
44 promise;
45
46 // Logged-out users can't have user options; we can't depend on mw.user, that'd be circular
47 if ( mw.config.get( 'wgUserName' ) === null ) {
48 return $.Deferred().reject( 'notloggedin' ).promise();
49 }
50
51 // If another options request to this API is pending, wait for it first
52 if (
53 saveOptionsRequests[ this.defaults.ajax.url ] &&
54 // Avoid long chains of promises, they may cause memory leaks
55 saveOptionsRequests[ this.defaults.ajax.url ].state() === 'pending'
56 ) {
57 promise = saveOptionsRequests[ this.defaults.ajax.url ].then( function () {
58 // Don't expose the old promise's result, it would be confusing
59 return $.Deferred().resolve();
60 }, function () {
61 return $.Deferred().resolve();
62 } );
63 } else {
64 promise = $.Deferred().resolve();
65 }
66
67 for ( name in options ) {
68 value = options[ name ] === null ? null : String( options[ name ] );
69
70 // Can we bundle this option, or does it need a separate request?
71 if ( this.defaults.useUS ) {
72 bundleable = name.indexOf( '=' ) === -1;
73 } else {
74 bundleable =
75 ( value === null || value.indexOf( '|' ) === -1 ) &&
76 ( name.indexOf( '|' ) === -1 && name.indexOf( '=' ) === -1 );
77 }
78
79 if ( bundleable ) {
80 if ( value !== null ) {
81 grouped.push( name + '=' + value );
82 } else {
83 // Omitting value resets the option
84 grouped.push( name );
85 }
86 } else {
87 if ( value !== null ) {
88 promise = promise.then( function ( name, value ) {
89 return this.postWithToken( 'csrf', {
90 formatversion: 2,
91 action: 'options',
92 optionname: name,
93 optionvalue: value
94 } );
95 }.bind( this, name, value ) );
96 } else {
97 // Omitting value resets the option
98 promise = promise.then( function ( name ) {
99 return this.postWithToken( 'csrf', {
100 formatversion: 2,
101 action: 'options',
102 optionname: name
103 } );
104 }.bind( this, name ) );
105 }
106 }
107 }
108
109 if ( grouped.length ) {
110 promise = promise.then( function () {
111 return this.postWithToken( 'csrf', {
112 formatversion: 2,
113 action: 'options',
114 change: grouped
115 } );
116 }.bind( this ) );
117 }
118
119 saveOptionsRequests[ this.defaults.ajax.url ] = promise;
120
121 return promise;
122 }
123
124 } );
125
126 /**
127 * @class mw.Api
128 * @mixins mw.Api.plugin.options
129 */
130
131 }() );