MediaWiki:Gadget-rsw-util.js

From Old School Near-Reality Wiki
Jump to navigation Jump to search

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 || {}));