MediaWiki:Gadget-rsw-util.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
1 (function ($, mw, rs) {
2
3 'use strict';
4
5 function createOOUIWindowManager() {
6 if (window.OOUIWindowManager == undefined) {
7 window.OOUIWindowManager = new OO.ui.WindowManager();
8 $( 'body' ).append( window.OOUIWindowManager.$element );
9 }
10 return window.OOUIWindowManager;
11 }
12
13 /**
14 * Reusable functions
15 *
16 * These are available under the `rswiki` global variable.
17 * @example `rswiki.addCommas`
18 * The alias `rs` is also available in place of `rswiki`.
19 */
20 var util = {
21 /**
22 * Formats a number string with commas.
23 *
24 * @todo fully replace this with Number.protoype.toLocaleString
25 * > 123456.78.toLocaleString('en')
26 *
27 * @example 123456.78 -> 123,456.78
28 *
29 * @param num {Number|String} The number to format.
30 * @return {String} The formated number.
31 */
32 addCommas: function (num) {
33 if (typeof num === 'number') {
34 return num.toLocaleString('en');
35 }
36
37 // @todo chuck this into parseFloat first and then to toLocaleString?
38 num += '';
39
40 var x = num.split('.'),
41 x1 = x[0],
42 x2 = x.length > 1 ?
43 '.' + x[1] :
44 '',
45 rgx = /(\d+)(\d{3})/;
46
47 while (rgx.test(x1)) {
48 x1 = x1.replace(rgx, '$1,$2');
49 }
50
51 return x1 + x2;
52 },
53
54 /**
55 * Extracts parameter-argument pairs from templates.
56 *
57 * @todo Fix for multiple templates
58 *
59 * @param tpl {String} Template to extract data from.
60 * @param text {String} Text to look for template in.
61 * @return {Object} Object containing parameter-argument pairs
62 */
63 parseTemplate: function (tpl, text) {
64 var rgx = new RegExp(
65 '\\{\\{(template:)?' + tpl.replace(/[ _]/g, '[ _]') + '\\s*(\\||\\}\\})',
66 'i'
67 ),
68 exec = rgx.exec(text),
69 // splits template into |arg=param or |param
70 paramRgx = /\|(.*?(\{\{.+?\}\})?)(?=\s*\||$)/g,
71 args = {},
72 params,
73 i,
74 j;
75
76 // happens if the template is not found in the text
77 if (exec === null) {
78 return false;
79 }
80
81 text = text.substring(exec.index + 2);
82
83 // used to account for nested templates
84 j = 0;
85
86 // this purposefully doesn't use regex
87 // as it became very difficult to make it work properly
88 for (i = 0; i < text.length; i += 1) {
89 if (text[i] === '{') {
90 j += 1;
91 } else if (text[i] === '}') {
92 if (j > 0) {
93 j -= 1;
94 } else {
95 break;
96 }
97 }
98 }
99
100 // cut off where the template ends
101 text = text.substring(0, i);
102 // remove template name as we're not interested in it past this point
103 text = text.substring(text.indexOf('|')).trim();
104 // separate params and args into an array
105 params = text.match(paramRgx);
106
107 // handle no params/args
108 if (params !== null) {
109 // used as an index for unnamed params
110 i = 1;
111
112 params.forEach(function (el) {
113 var str = el.trim().substring(1),
114 eq = str.indexOf('='),
115 tpl = str.indexOf('{{'),
116 param,
117 val;
118
119 // checks if the equals is after opening a template
120 // to catch unnamed args that have templates with named args as params
121 if (eq > -1 && (tpl === -1 || eq < tpl)) {
122 param = str.substring(0, eq).trim().toLowerCase();
123 val = str.substring(eq + 1).trim();
124 } else {
125 param = i;
126 val = str.trim();
127 i += 1;
128 }
129
130 args[param] = val;
131 });
132 }
133
134 return args;
135 },
136
137 /**
138 * Alternate version of `parseTemplate` for parsing exchange module data.
139 *
140 * @notes Only works for key-value pairs
141 *
142 * @param text {String} Text to parse.
143 * @return {Object} Object containing parameter-argument pairs.
144 */
145 parseExchangeModule: function (text) {
146
147 // strip down to just key-value pairs
148 var str = text
149 .replace(/return\s*\{/, '')
150 .replace(/\}\s*$/, '')
151 .trim(),
152 rgx = /\s*(.*?\s*=\s*(?:\{[\s\S]*?\}|.*?))(?=,?\n|$)/g,
153 args = {},
154 params = str.match(rgx);
155
156 if (params !== null) {
157 params.forEach(function (elem) {
158 var str = elem.trim(),
159 eq = str.indexOf('='),
160 param = str.substring(0, eq).trim().toLowerCase(),
161 val = str.substring(eq + 1).trim();
162
163 args[param] = val;
164 });
165 }
166
167 return args;
168 },
169
170 /**
171 * Helper for making cross domain requests to RuneScape's APIs.
172 * If the APIs ever enable CORS, we can ditch this and do the lookup directly.
173 *
174 * @param url {string} The URL to look up
175 * @param via {string} One of 'anyorigin', 'whateverorigin' or 'crossorigin'. Defaults to 'anyorigin'.
176 *
177 * @return {string} The URLto use to make the API request.
178 */
179 crossDomain: function (url, via) {
180 switch (via) {
181 case 'crossorigin':
182 url = 'http://crossorigin.me/' + url;
183 break;
184
185 case 'whateverorigin':
186 url = 'http://whateverorigin.org/get?url=' + encodeURIComponent( url ) + '&callback=?';
187 break;
188
189 case 'anyorigin':
190 default:
191 url = 'http://anyorigin.com/go/?url=' + encodeURIComponent( url ) + '&callback=?';
192 break;
193 }
194
195 return url;
196 },
197 /**
198 * Returns the OOUI window manager as a Promise. Will load OOUI (core and windows) and create the manager, if necessary.
199 *
200 * @return {jQuery.Deferred} A jQuery Promise where window.OOUIWindowManager is will be defined
201 * Chaining a .then will pass OOUIWindowManager to the function argument
202 */
203 withOOUIWindowManager: function() {
204 return mw.loader.using(['oojs-ui-core','oojs-ui-windows']).then(createOOUIWindowManager);
205 },
206
207 /**
208 * Helper for creating and initializing a new OOUI Dialog object
209 * After init, the window is added to the global Window Manager.
210 *
211 * Will automatically load OOUI (core and windows) and create the window manager, if necessary. window.OOUIWindowManager will be defined within this.
212 *
213 * @author JaydenKieran
214 *
215 * @param name {string} The symbolic name of the window
216 * @param title {string} The title of the window
217 * @param winconfig {object} Object containing params for the OO.ui.Dialog obj
218 * @param init {function} Function to be called to initialise the object
219 * @param openNow {boolean} Whether the window should be opened instantly
220 * @param autoClose {boolean} Autoclose when the user clicks outside of the modal
221 *
222 * @return {jquery.Deferred} The jQuery Promise returned by mw.loader.using
223 * Chaining a .then will pass the created {OO.ui.Dialog} object as the function argument
224 */
225 createOOUIWindow: function(name, title, winconfig, init, openNow, autoClose) {
226 return mw.loader.using(['oojs-ui-core','oojs-ui-windows']).then(function(){
227 createOOUIWindowManager();
228 winconfig = winconfig || {};
229
230 function myModal( config ) {
231 myModal.super.call( this, config );
232 }
233 OO.inheritClass( myModal, OO.ui.Dialog );
234
235 myModal.static.name = name;
236 myModal.static.title = title;
237
238 myModal.prototype.initialize = function () {
239 myModal.super.prototype.initialize.call( this );
240 init(this);
241 }
242
243 var modal = new myModal(winconfig);
244
245 console.debug('Adding ' + myModal.static.name + ' to WindowManager');
246 window.OOUIWindowManager.addWindows( [ modal ] );
247
248 if (openNow) {
249 window.OOUIWindowManager.openWindow(name);
250 }
251
252 if (autoClose) {
253 $(document).on('click', function (e) {
254 if (modal && modal.isVisible() && e.target.classList.contains('oo-ui-window-active')) {
255 modal.close();
256 };
257 });
258 }
259
260 return modal;
261 });
262 },
263
264 /**
265 * Helper for checking if the user's browser supports desktop notifications
266 * @author JaydenKieran
267 */
268 canSendBrowserNotifs: function () {
269 if (!("Notification" in window)) {
270 console.warn("This browser does not support desktop notifications");
271 return false;
272 } else {
273 return true;
274 }
275 },
276
277 /**
278 * Send a desktop/browser notification to a user, requires the page to be open
279 * @author JaydenKieran
280 *
281 * @param https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification
282 *
283 * @return Notification object or null
284 */
285 sendBrowserNotif: function (title, opts) {
286 if (rs.canSendBrowserNotifs == false) {
287 return null;
288 }
289 Notification.requestPermission().then(function(result) {
290 if (result === "granted") {
291 console.debug('Firing desktop notification');
292 var notif = new Notification(title, opts);
293 notif.onclick = function(e) {
294 window.focus();
295 }
296 return notif;
297 } else {
298 return null;
299 }
300 });
301 },
302
303 /**
304 * Check if the browser has support for localStorage
305 * @author JaydenKieran
306 *
307 * @return boolean
308 **/
309 hasLocalStorage: function() {
310 try {
311 localStorage.setItem('test', 'test')
312 localStorage.removeItem('test')
313 return true
314 } catch (e) {
315 return false
316 }
317 },
318
319 /**
320 * Check if user is using dark mode
321 * @author JaydenKieran
322 *
323 * @return boolean
324 **/
325 isUsingDarkmode: function() {
326 if (typeof $.cookie('darkmode') === 'undefined') {
327 return false
328 } else {
329 return $.cookie('darkmode') === 'true'
330 }
331 },
332
333 /**
334 * Gets a query string parameter from given URL or current href
335 * @author JaydenKieran
336 *
337 * @return string or null
338 **/
339 qsp: function(name, url) {
340 if (!url) url = window.location.href;
341 name = name.replace(/[\[\]]/g, '\\$&');
342 var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
343 results = regex.exec(url);
344 if (!results) return null;
345 if (!results[2]) return '';
346 return decodeURIComponent(results[2].replace(/\+/g, ' '));
347 },
348
349 /**
350 * Get the URL for a file on the wiki, aganst the endpoint that is actually cached and fast.
351 * Should probably not be used for images we expect to change frequently.
352 * @author cookmeplox
353 *
354 * @return string
355 **/
356 getFileURLCached: function(filename) {
357 var base = window.location.origin;
358 filename = filename.replace(/ /g,"_");
359 filename = filename.replace(/\(/g, '%28').replace(/\)/g, '%29');
360 var cb = '48781';
361 return base + '/images/' + filename + '?' + cb;
362 },
363
364 isUsingStickyHeader: function() {
365 return ($('body').hasClass('wgl-stickyheader'))
366 }
367 };
368
369 function init() {
370 $.extend(rs, util, {});
371 // add rs as a global alias
372 window.rs = rs;
373 }
374
375 init();
376
377 }(this.jQuery, this.mediaWiki, this.rswiki = this.rswiki || {}));