9 // Extend the skeleton mw.user from mediawiki.js
10 // This is kind of ugly but we're stuck with this for b/c reasons
11 options
= mw
.user
.options
|| new mw
.Map(),
12 tokens
= mw
.user
.tokens
|| new mw
.Map();
14 // Maps for number -> hex string conversion (with padding)
15 // idea from: https://github.com/broofa/node-uuid/blob/master/uuid.js
16 for ( i
= 0; i
< 256; i
++ ) {
17 byteToHex
[i
] = (i
+ 0x100).toString(16).substr(1);
21 * Get the current user's groups or rights
24 * @param {string} info One of 'groups' or 'rights'
25 * @return {jQuery.Promise}
27 function getUserInfo( info
) {
29 if ( !deferreds
[info
] ) {
31 deferreds
.rights
= $.Deferred();
32 deferreds
.groups
= $.Deferred();
38 uiprop
: 'rights|groups'
39 } ).always( function ( data
) {
41 if ( data
.query
&& data
.query
.userinfo
) {
42 rights
= data
.query
.userinfo
.rights
;
43 groups
= data
.query
.userinfo
.groups
;
45 deferreds
.rights
.resolve( rights
|| [] );
46 deferreds
.groups
.resolve( groups
|| [] );
51 return deferreds
[info
].promise();
59 * Generate a random user session ID
60 * This information would potentially be stored in a cookie to identify a user during a
61 * session or series of sessions. Its uniqueness should
62 * not be depended on unless the browser supports the crypto API.
64 * Known problems with Math.random():
65 * Using the Math.random function we have seen sets
66 * with 1% of non uniques among 200.000 values with Safari providing most of these.
67 * Given the prevalence of Safari in mobile the percentage of duplicates in
68 * mobile usages of this code is probably higher.
71 * We need about 64 bits to make sure that probability of collision
72 * on 500 million (5*10^8) is <= 1%
73 * See: https://en.wikipedia.org/wiki/Birthday_problem#Probability_table
75 * @return {string} 64 bit integer in hex format, padded
77 generateRandomSessionId: function () {
78 /*jshint bitwise:false */
79 var rnds
, i
, r
, cryptoObj
, hexRnds
= new Array( 8 );
80 cryptoObj
= window
.crypto
|| window
.msCrypto
; // for IE 11
83 // We fill an array with 8 random values, each of which is 8 bits.
84 // note that rnds is an array-like object not a true array
85 rnds
= new Uint8Array( 8 );
86 cryptoObj
.getRandomValues( rnds
);
88 rnds
= new Array( 8 );
89 // From: https://github.com/broofa/node-uuid/blob/master/uuid.js
90 for ( i
= 0, r
; i
< 8; i
++ ) {
91 if ( ( i
& 0x03 ) === 0 ) {
92 r
= Math
.random() * 0x100000000;
94 rnds
[i
] = r
>>> ( ( i
& 0x03 ) << 3 ) & 0xff;
97 // convert to hex using byteToHex that already contains padding
98 for ( i
= 0; i
< rnds
.length
; i
++ ) {
99 hexRnds
[i
] = byteToHex
[rnds
[i
]];
102 // concatenation of two random integers with entrophy n and m
103 // returns a string with entrophy n+m if those strings are independent
104 return hexRnds
.join( '' );
108 * Get the current user's database id
110 * Not to be confused with #id.
112 * @return {number} Current user's id, or 0 if user is anonymous
115 return mw
.config
.get( 'wgUserId', 0 );
119 * Get the current user's name
121 * @return {string|null} User name string or null if user is anonymous
123 getName: function () {
124 return mw
.config
.get( 'wgUserName' );
128 * Get date user registered, if available
130 * @return {Date|boolean|null} Date user registered, or false for anonymous users, or
131 * null when data is not available
133 getRegistration: function () {
134 var registration
= mw
.config
.get( 'wgUserRegistration' );
135 if ( user
.isAnon() ) {
138 if ( registration
=== null ) {
139 // Information may not be available if they signed up before
140 // MW began storing this.
143 return new Date( registration
);
147 * Whether the current user is anonymous
151 isAnon: function () {
152 return user
.getName() === null;
156 * Get an automatically generated random ID (stored in a session cookie)
158 * This ID is ephemeral for everyone, staying in their browser only until they close
161 * @return {string} Random session ID
163 sessionId: function () {
164 var sessionId
= $.cookie( 'mediaWiki.user.sessionId' );
165 if ( sessionId
=== undefined || sessionId
=== null ) {
166 sessionId
= user
.generateRandomSessionId();
167 $.cookie( 'mediaWiki.user.sessionId', sessionId
, { expires
: null, path
: '/' } );
173 * Get the current user's name or the session ID
175 * Not to be confused with #getId.
177 * @return {string} User name or random session ID
180 return user
.getName() || user
.sessionId();
184 * Get the user's bucket (place them in one if not done already)
186 * mw.user.bucket( 'test', {
187 * buckets: { ignored: 50, control: 25, test: 25 },
192 * @deprecated since 1.23
193 * @param {string} key Name of bucket
194 * @param {Object} options Bucket configuration options
195 * @param {Object} options.buckets List of bucket-name/relative-probability pairs (required,
196 * must have at least one pair)
197 * @param {number} [options.version=0] Version of bucket test, changing this forces
199 * @param {number} [options.expires=30] Length of time (in days) until the user gets
201 * @return {string} Bucket name - the randomly chosen key of the `options.buckets` object
203 bucket: function ( key
, options
) {
204 var cookie
, parts
, version
, bucket
,
205 range
, k
, rand
, total
;
207 options
= $.extend( {
213 cookie
= $.cookie( 'mediaWiki.user.bucket:' + key
);
215 // Bucket information is stored as 2 integers, together as version:bucket like: "1:2"
216 if ( typeof cookie
=== 'string' && cookie
.length
> 2 && cookie
.indexOf( ':' ) !== -1 ) {
217 parts
= cookie
.split( ':' );
218 if ( parts
.length
> 1 && Number( parts
[0] ) === options
.version
) {
219 version
= Number( parts
[0] );
220 bucket
= String( parts
[1] );
224 if ( bucket
=== undefined ) {
225 if ( !$.isPlainObject( options
.buckets
) ) {
226 throw new Error( 'Invalid bucket. Object expected for options.buckets.' );
229 version
= Number( options
.version
);
233 for ( k
in options
.buckets
) {
234 range
+= options
.buckets
[k
];
237 // Select random value within range
238 rand
= Math
.random() * range
;
240 // Determine which bucket the value landed in
242 for ( k
in options
.buckets
) {
244 total
+= options
.buckets
[k
];
245 if ( total
>= rand
) {
251 'mediaWiki.user.bucket:' + key
,
252 version
+ ':' + bucket
,
253 { path
: '/', expires
: Number( options
.expires
) }
261 * Get the current user's groups
263 * @param {Function} [callback]
264 * @return {jQuery.Promise}
266 getGroups: function ( callback
) {
267 return getUserInfo( 'groups' ).done( callback
);
271 * Get the current user's rights
273 * @param {Function} [callback]
274 * @return {jQuery.Promise}
276 getRights: function ( callback
) {
277 return getUserInfo( 'rights' ).done( callback
);
281 }( mediaWiki
, jQuery
) );