MediaWiki:Common.js
From AppleGamingWiki, the wiki about gaming on M1 Apple silicon Macs
Note: After saving, 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: Go to Menu → Settings (Opera → Preferences on a Mac) and then to Privacy & security → Clear browsing data → Cached images and files.
//<nowiki> /* ===== FIX: Suppress link click on user menu on tablet devices ========================================================================================================================= * * Fixes the user menu being inaccessible on tablets due to touch events going through to to the user page link. * * @source https://stackoverflow.com/a/15558423 * */ function fixTabletLinks() { if(document.createEvent("TouchEvent")) { // using document.createEvent is more reliable than navigator (Modernizr uses this practice) $("div#personal-bar-flyout > div > a").each(function() { // have to use an `each` here - either a jQuery `each` or a `for(...)` loop var onClick; // this will be a function var firstClick = function() { $("a").trigger("JWUnhover"); // triggering hoverfix reset if any link gets touched onClick = secondClick; return false; }; secondClick = function() { onClick = firstClick; return true; }; onClick = firstClick; $(this).click(function() { if (Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0) > 800) { return onClick(); } else { return true; } }); $(this).bind('JWUnhover', function(){ onClick = firstClick; }); }); } } try { fixTabletLinks(); } catch (e) { // Suppress console error if device does not support touch events. } /* ===== FUNCTIONALITY : Style pages based on categories ========================================================================================================================= * * Adds CSS classes to the body tag based on the categories this page belongs to * * @source https://www.mediawiki.org/wiki/Snippets/Style_pages_based_on_categories * @revision 2016-01-18 * */ (function($, mw) { var fn = function() { var cats = mw.config.get('wgCategories'), newClasses; if (cats) { newClasses = $.map(cats, function(el) { return 'cat-' + encodeURIComponent(el.replace(/[ .]/g, '_')).replace(/%/g, '_'); }).join(' '); $(document.body).addClass(newClasses); } }; if (document.body) { fn(); } else { $(fn); } })(jQuery, mw); /* ===== FUNCTIONALITY : System Requirements Tabs ========================================================================================================================= * */ function sysreqTabs() { // run tab code only if tabNav exists if ($('#tabNav')[0]) { // store the number of sysreq tables var sysreqcount = $('.sysreq').length // run tab code only if there are at least two sysreq tables if (sysreqcount > 1) { // Make tab nav visible $('#tabNav').css('display', 'inline-block'); // hide sysreq table headings $('.sysreq_heading').css('display', 'none'); // remove tabs for missing tables (if not already removed by the template) $('.sysreq_PC_booter')[0] || $('#sysreq_PC_booter').remove(); $('.sysreq_DOS')[0] || $('#sysreq_DOS').remove(); $('.sysreq_Windows_3x')[0] || $('#sysreq_Windows_3x').remove(); $('.sysreq_Windows')[0] || $('#sysreq_Windows').remove(); $('.sysreq_Mac_OS')[0] || $('#sysreq_Mac_OS').remove(); $('.sysreq_OS_X')[0] || $('#sysreq_OS_X').remove(); $('.sysreq_Linux')[0] || $('#sysreq_Linux').remove(); // find first tab and apply style var firstTab = document.querySelector('.sysreqTab:first-child'); $(firstTab).addClass('selectedTab'); // ensure table matching first tab is visible (in case page order does not match OS order expected by tab template) $('.sysreq').css('display', 'none'); $('.' + $(firstTab).attr('id')).css('display', 'block'); // apply table visibility and tab style changes when tabs are clicked $('.sysreqTab').click(function() { $('.sysreqTab').removeClass('selectedTab'); $(this).addClass('selectedTab'); $('.sysreq').css('display', 'none'); $('.' + $(this).attr('id')).css('display', 'block'); }) // remove tabs table if there are fewer than two sysreq tables } else { $('#tabNav').remove(); } } } $(sysreqTabs); /* ===== FUNCTIONALITY : Extend the WYSIWYG wiki editor with new functions ========================================================================================================================= * */ // Retrieves todays date according to UTC and slices out the YYYY-MM-DD part of it. var TodaysDateUTC = new Date().toJSON().slice(0,10); var customizeToolbar = function() { // Add a hook handler. mw.hook('wikiEditor.toolbarReady').add(function($textarea) { // Removes the 'file' tool (the Image-icon) as this makes use of [[File:]] and not the preferred {{Image|}} template. $textarea.wikiEditor('removeFromToolbar', { 'section': 'main', 'group': 'insert', 'tool': 'file' }); // Removes the 'reference' tool as using the templates are the preferred way of referencing stuff $textarea.wikiEditor('removeFromToolbar', { 'section': 'main', 'group': 'insert', 'tool': 'reference' }); // Removes the 'redirect' tool as it have been moved to the search group $textarea.wikiEditor('removeFromToolbar', { 'section': 'advanced', 'group': 'insert', 'tool': 'redirect' }); // Adds a custom Image icon to the insert group $textarea.wikiEditor('addToToolbar', { 'section': 'main', 'group': 'insert', 'tools': { 'smile': { label: 'Image', type: 'button', icon: '/w/load.php?modules=oojs-ui.styles.icons-media&image=image&format=rasterized&lang=en&skin=overclocked', action: { type: 'encapsulate', options: { pre: "{{Image|", peri: "File.png|Caption", post: "}}", ownline: true } } } } }); // Adds a custom Redirect icon to the search group $textarea.wikiEditor('addToToolbar', { 'section': 'advanced', 'group': 'search', 'tools': { 'redirect': { label: 'Redirect', type: 'button', icon: '/w/load.php?modules=oojs-ui.styles.icons-content&image=articleRedirect&format=rasterized&lang=en&skin=overclocked', action: { type: 'encapsulate', options: { pre: "#REDIRECT [[", peri: "Target page name", post: "]]", ownline: true } } } } }); // Adds custom byttons to the advanced > insert group $textarea.wikiEditor('addToToolbar', { 'section': 'advanced', 'group': 'insert', 'tools': { 'fixbox': { label: 'Fixbox', type: 'button', icon: '//images.pcgamingwiki.com/d/d8/Editor_Icon_-_Fixbox.svg', action: { type: 'encapsulate', options: { pre: "{{Fixbox|description=", peri: "Name", post: "|ref=<ref>Reference</ref>|fix=\n}}", ownline: true } } } } }); // Adds custom templates in a new group to the right of the advanced > insert group $textarea.wikiEditor('addToToolbar', { 'section': 'advanced', 'groups': { 'insert-sections': { 'label': '', 'tools': { 'availabilityrow': { label: 'Availability row', type: 'button', icon: '//images.pcgamingwiki.com/6/67/Editor_Icon_-_Availability_Table_Row.svg', action: { type: 'encapsulate', options: { pre: "{{Availability/row| ", peri: "store", post: " | id | DRM | notes | Key | OS }}", ownline: true } } }, 'dlcrow': { label: 'DLC row', type: 'button', icon: '//images.pcgamingwiki.com/9/9f/Editor_Icon_-_DLCrow.svg', action: { type: 'encapsulate', options: { pre: "{{DLC/row| ", peri: "Name", post: " | notes | OS }}", ownline: true } } }, 'gamedataconfigrow': { label: 'Game data row - config', type: 'button', icon: '//images.pcgamingwiki.com/0/0c/Editor_Icon_-_Game_Data_Row.svg', action: { type: 'encapsulate', options: { pre: "{{Game data/config|", peri: "System", post: "|Path}}", ownline: true } } }, 'gamedatasavesrow': { label: 'Game data row - saves', type: 'button', icon: '//images.pcgamingwiki.com/0/0c/Editor_Icon_-_Game_Data_Row.svg', action: { type: 'encapsulate', options: { pre: "{{Game data/saves|", peri: "System", post: "|Path}}", ownline: true } } }, 'langrow': { label: 'Language row', type: 'button', icon: '//images.pcgamingwiki.com/2/22/Editor_Icon_-_Localizations.svg', action: { type: 'encapsulate', options: { pre: "{{L10n/switch \n |language = ", post: "\n |interface = true\n |audio = \n |subtitles = \n |notes = \n |fan = \n |ref = \n}}", ownline: true } } } } } } }); // Adds custom templates in a new group to the right of the advanced > insert-sections group $textarea.wikiEditor('addToToolbar', { 'section': 'advanced', 'groups': { 'main-templates': { 'label': '', 'tools': { 'reftemplates': { label: 'Reference', type: 'select', list: { 'refuser': { label: 'Checked by user', action: { type: 'encapsulate', options: { pre: "<ref>{{Refcheck|user=" + (mw.config.get('wgUserName') !== null ? mw.config.get('wgUserName') : "User") + "|date=" + TodaysDateUTC + "|comment=Comment (optional)}}</ref>" } } }, 'refurl': { label: 'URL', action: { type: 'encapsulate', options: { pre: "<ref>{{Refurl|url=|title=|date=" + TodaysDateUTC + "}}</ref>" } } }, 'refsnippet': { label: 'Snippet from URL', action: { type: 'encapsulate', options: { pre: "<ref>{{Refurl|url=|title=|date=" + TodaysDateUTC + "|snippet=}}</ref>" } } }, 'cn': { label: 'Citation needed', action: { type: 'encapsulate', options: { pre: "{{cn|date=" + TodaysDateUTC + "|reason=}}" } } }, 'dubious': { label: 'Dubious', action: { type: 'encapsulate', options: { pre: "{{dubious|date=" + TodaysDateUTC + "|reason=}}" } } }, 'note': { label: 'Note', action: { type: 'encapsulate', options: { pre: "{{note|", peri: "The content of the note here.", post: "}}" } } } } }, 'termsection': { label: 'Term', type: 'select', list: { 'categorygeneral': { label: 'General:', }, 'drm': { label: '· DRM', action: { type: 'encapsulate', options: { pre: "{{Term|DRM}}" } } }, 'fmv': { label: '· FMV (full motion video)', action: { type: 'encapsulate', options: { pre: "{{Term|FMV}}" } } }, 'categoryvr': { label: 'VR:', }, 'hmd': { label: '· HMD (head-mounted display)', action: { type: 'encapsulate', options: { pre: "{{Term|HMD}}" } } }, 'categorygraphics': { label: 'Graphics:', }, 'sdr': { label: '· SDR', action: { type: 'encapsulate', options: { pre: "{{Term|SDR}}" } } }, 'hdr': { label: '· HDR (modern HDR)', action: { type: 'encapsulate', options: { pre: "{{Term|HDR}}" } } }, 'hdrr': { label: '· HDR Rendering ("old-school HDR")', action: { type: 'encapsulate', options: { pre: "{{Term|HDR Rendering}}" } } }, 'categoryscaling': { label: 'Scaling:', }, 'horplus': { label: '· Hor+', action: { type: 'encapsulate', options: { pre: "{{Term|Hor+}}" } } }, 'vertminus': { label: '· Vert-', action: { type: 'encapsulate', options: { pre: "{{Term|Vert-}}" } } }, 'letterbox': { label: '· Letterbox', action: { type: 'encapsulate', options: { pre: "{{Term|Letterbox}}" } } }, 'pillarbox': { label: '· Pillarbox', action: { type: 'encapsulate', options: { pre: "{{Term|Pillarbox}}" } } }, 'anamorphic': { label: '· Anamorphic', action: { type: 'encapsulate', options: { pre: "{{Term|Anamorphic}}" } } }, 'pixelbased': { label: '· Pixel-based', action: { type: 'encapsulate', options: { pre: "{{Term|Pixel-based}}" } } }, 'pixelperfect': { label: '· Pixel-perfect / integer scaling', action: { type: 'encapsulate', options: { pre: "{{Term|Pixel-perfect}}" } } }, 'stretch': { label: '· Stretch', action: { type: 'encapsulate', options: { pre: "{{Term|Stretch}}" } } }, 'noscaling': { label: '· No scaling', action: { type: 'encapsulate', options: { pre: "{{Term|No scaling}}" } } }, 'nearestneighbor': { label: '· Nearest-neighbor (algorithm)', action: { type: 'encapsulate', options: { pre: "{{Term|Nearest-neighbor}}" } } } } }, 'filepath': { label: 'Path', type: 'select', list: { 'categorygeneral': { label: 'General:', }, 'pathgame': { label: '· Path to game', action: { type: 'encapsulate', options: { pre: "{{P|game}}" } } }, 'pathlocalized': { label: '· Localized folder or file name', action: { type: 'encapsulate', options: { pre: "{{LocalizedPath|", peri: "Folder or file name", post: "}}" } } }, 'pathuid': { label: '· User ID', action: { type: 'encapsulate', options: { pre: "{{P|uid}}" } } }, 'pathsteam': { label: '· Steam install folder', action: { type: 'encapsulate', options: { pre: "{{P|steam}}" } } }, 'pathubisoftconnect': { label: '· Ubisoft Connect install folder', action: { type: 'encapsulate', options: { pre: "{{P|ubisoftconnect}}" } } }, 'categorywindowsdisk': { label: 'Windows (disk):', }, 'pathuserprofile': { label: '· %USERPROFILE%', action: { type: 'encapsulate', options: { pre: "{{P|userprofile}}" } } }, 'pathuserprofiledocuments': { label: '· %USERPROFILE%\\Documents', action: { type: 'encapsulate', options: { pre: "{{P|userprofile\\Documents}}" } } }, 'pathappdata': { label: '· AppData\\Roaming (%APPDATA%)', action: { type: 'encapsulate', options: { pre: "{{P|appdata}}" } } }, 'pathlocalappdata': { label: '· AppData\\Local (%LOCALAPPDATA%)', action: { type: 'encapsulate', options: { pre: "{{P|localappdata}}" } } }, 'pathlocallowappdata': { label: '· AppData\\LocalLow', action: { type: 'encapsulate', options: { pre: "{{P|userprofile\\appdata\\locallow}}" } } }, 'pathpublic': { label: '· %PUBLIC%', action: { type: 'encapsulate', options: { pre: "{{P|public}}" } } }, 'pathprogramdata': { label: '· %PROGRAMDATA%', action: { type: 'encapsulate', options: { pre: "{{P|programdata}}" } } }, 'pathwindir': { label: '· %WINDIR%', action: { type: 'encapsulate', options: { pre: "{{P|windir}}" } } }, 'categorywindowsregistry': { label: 'Windows (registry):', }, 'pathhkcu': { label: '· HKEY_CURRENT_USER', action: { type: 'encapsulate', options: { pre: "{{P|hkcu}}" } } }, 'pathhklm': { label: '· HKEY_LOCAL_MACHINE', action: { type: 'encapsulate', options: { pre: "{{P|hklm}}" } } }, 'pathwow64': { label: '· Wow6432Node', action: { type: 'encapsulate', options: { pre: "{{P|wow64}}" } } }, 'categoryosx': { label: 'OS X:', }, 'pathosxhome': { label: '· Home folder (osx)', action: { type: 'encapsulate', options: { pre: "{{P|osxhome}}" } } }, 'categorylinux': { label: 'Linux:', }, 'pathlinuxhome': { label: '· Home folder (linux)', action: { type: 'encapsulate', options: { pre: "{{P|linuxhome}}" } } }, 'pathlinuxxdgdatahome': { label: '· XDG data home', action: { type: 'encapsulate', options: { pre: "{{P|xdgdatahome}}" } } }, 'pathxdgconfighome': { label: '· XDG config home', action: { type: 'encapsulate', options: { pre: "{{P|xdgconfighome}}" } } } } }, 'seesection': { label: 'See...', type: 'select', list: { 'categoryvideo': { label: 'Video:', }, 'widescreen': { label: '· Widescreen resolution', action: { type: 'encapsulate', options: { pre: "See [[#Widescreen resolution|Widescreen resolution]]." } } }, 'multimonitor': { label: '· Multi-monitor', action: { type: 'encapsulate', options: { pre: "See [[#Multi-monitor|Multi-monitor]]." } } }, 'ultrawidescreen': { label: '· Ultra-widescreen', action: { type: 'encapsulate', options: { pre: "See [[#Ultra-widescreen|Ultra-widescreen]]." } } }, '4kultrahd': { label: '· 4K Ultra HD', action: { type: 'encapsulate', options: { pre: "See [[#4K Ultra HD|4K Ultra HD]]." } } }, 'fieldofview': { label: '· Field of view (FOV)', action: { type: 'encapsulate', options: { pre: "See [[#Field of view (FOV)|Field of view (FOV)]]." } } }, 'windowed': { label: '· Windowed', action: { type: 'encapsulate', options: { pre: "See [[#Windowed|Windowed]]." } } }, 'borderless': { label: '· Borderless fullscreen windowed', action: { type: 'encapsulate', options: { pre: "See [[#Borderless fullscreen windowed|Borderless fullscreen windowed]]." } } }, 'anisotropic': { label: '· Anisotropic filtering (AF)', action: { type: 'encapsulate', options: { pre: "See [[#Anisotropic filtering (AF)|Anisotropic filtering (AF)]]." } } }, 'antialiasing': { label: '· Anti-aliasing (AA)', action: { type: 'encapsulate', options: { pre: "See [[#Anti-aliasing (AA)|Anti-aliasing (AA)]]." } } }, 'vsync': { label: '· Vertical sync (Vsync)', action: { type: 'encapsulate', options: { pre: "See [[#Vertical sync (Vsync)|Vertical sync (Vsync)]]." } } }, 'highframe': { label: '· High frame rate', action: { type: 'encapsulate', options: { pre: "See [[#High frame rate|High frame rate]]." } } }, 'hdr': { label: '· High dynamic range display (HDR)', action: { type: 'encapsulate', options: { pre: "See [[#High dynamic range display (HDR)|High dynamic range display (HDR)]]." } } }, 'raytracing': { label: '· Ray tracing', action: { type: 'encapsulate', options: { pre: "See [[#Ray tracing|Ray tracing]]." } } }, 'colorblind': { label: '· Color blind mode', action: { type: 'encapsulate', options: { pre: "See [[#Color blind mode|Color blind mode]]." } } }, 'categoryinput': { label: 'Input:', }, 'remapping': { label: '· Remapping', action: { type: 'encapsulate', options: { pre: "See [[#Remapping|Remapping]]." } } }, 'mouseaccel': { label: '· Mouse acceleration', action: { type: 'encapsulate', options: { pre: "See [[#Mouse acceleration|Mouse acceleration]]." } } }, 'controllersupport': { label: '· Controller support', action: { type: 'encapsulate', options: { pre: "See [[#Controller support|Controller support]]." } } }, 'controllerremap': { label: '· Controller remapping', action: { type: 'encapsulate', options: { pre: "See [[#Controller remapping|Controller remapping]]." } } }, 'touchscreen': { label: '· Touchscreen optimised', action: { type: 'encapsulate', options: { pre: "See [[#Touchscreen|Touchscreen]]." } } }, 'categoryvr': { label: 'VR:', }, 'oculus': { label: '· Oculus Rift', action: { type: 'encapsulate', options: { pre: "See [[#Oculus Rift|Oculus Rift]]." } } } } } } } } }); // Adds custom controls to the toolbar $textarea.wikiEditor('addToToolbar', { sections: { 'section-templates': { type: 'toolbar', label: 'Sections', groups: { templates: { 'label': '', 'tools': { 'introduction': { label: 'Introduction', type: 'button', icon: '//images.pcgamingwiki.com/4/40/Editor_Icon_-_Introduction.svg', action: { type: 'encapsulate', options: { pre: "{{Introduction\n|introduction = ", post: "\n\n|release history = \n\n|current state = \n}}", ownline: true } } }, 'dlc': { label: 'DLC', type: 'button', icon: '//images.pcgamingwiki.com/3/38/Editor_Icon_-_DLCtable.svg', action: { type: 'encapsulate', options: { pre: "{{DLC|\n", peri: "{{DLC/row| Name | notes | OS }}", post: "\n}}", ownline: true } } }, 'cloud': { label: 'Cloud syncing', type: 'button', icon: '//images.pcgamingwiki.com/b/bc/Editor_Icon_-_Cloud_Sync.svg', action: { type: 'encapsulate', options: { pre: "===[[Glossary:Save game cloud syncing|Save game cloud syncing]]===\n{{Save game cloud syncing\n|epic games launcher = \n|epic games launcher notes = \n|gog galaxy = \n|gog galaxy notes = \n|origin = \n|origin notes = \n|steam cloud = \n|steam cloud notes = \n}}\n", ownline: true } } }, 'apitable': { label: 'macOS Compatibility', type: 'button', icon: '//images.pcgamingwiki.com/c/cb/Editor_Icon_-_API.svg', action: { type: 'encapsulate', options: { pre: "{{Compatibility/macOS\n|native = na\n|native notes = \n|rosetta 2 = na\n|rosetta 2 notes = \n|ios-ipados app = na\n|ios-ipados app notes = \n|crossover = na\n|crossover notes = \n|wine = na\n|wine notes = \n|parallels = na\n|parallels notes = \n|windows 10 arm = na\n|windows 10 arm notes = \n|linux arm = na\n|linux arm notes = \n}}", ownline: true } } }, 'middlewaretable': { label: 'Middleware', type: 'button', icon: '//images.pcgamingwiki.com/c/c2/Editor_Icon_-_Middleware.svg', action: { type: 'encapsulate', options: { pre: "===Middleware===\n{{Middleware\n|physics = \n|physics notes = \n|audio = \n|audio notes = \n|interface = \n|interface notes = \n|input = \n|input notes = \n|cutscenes = \n|cutscenes notes = \n|multiplayer = \n|multiplayer notes= \n|anticheat = \n|anticheat notes = \n}}", ownline: true } } }, 'sysreq': { label: 'System requirements', type: 'button', icon: '//images.pcgamingwiki.com/4/4b/Editor_Icon_-_System_Requirements.svg', action: { type: 'encapsulate', options: { pre: "{{System requirements\n|OSfamily = ", peri: "System", post: "\n|minOS = \n|minCPU = \n|minRAM = \n|minHD = \n|minGPU = \n\n|recOS = \n|recCPU = \n|recRAM = \n|recHD = \n|recGPU = \n}}", ownline: true } } }, 'infoboxdrop': { label: 'Infobox row', type: 'select', list: { 'infoboxdeveloper': { label: 'Developer', action: { type: 'encapsulate', options: { pre: "{{Infobox game/row/developer|", post: "}}", ownline: true } } }, 'infoboxporter': { label: 'Porter', action: { type: 'encapsulate', options: { pre: "{{Infobox game/row/porter|", post: "|OS}}", ownline: true } } }, 'infoboxpublisher': { label: 'Publisher', action: { type: 'encapsulate', options: { pre: "{{Infobox game/row/publisher|", post: "}}", ownline: true } } }, 'infoboxengine': { label: 'Engine', action: { type: 'encapsulate', options: { pre: "{{Infobox game/row/engine|", post: "}}", ownline: true } } }, 'infoboxdateosx': { label: 'OS X release date', action: { type: 'encapsulate', options: { pre: "{{Infobox game/row/date|OS X|", peri: "Month dd, yyyy", post: "}}", ownline: true } } }, 'infoboxreception': { label: 'Reception rows', action: { type: 'encapsulate', options: { post: "|reception = \n{{Infobox game/row/reception|Metacritic|link|rating}}\n{{Infobox game/row/reception|OpenCritic|link|rating}}\n{{Infobox game/row/reception|IGDB|link|rating}}", ownline: true } } }, 'infobox taxonomy': { label: 'Taxonomy rows', action: { type: 'encapsulate', options: { post: "|taxonomy = \n{{Infobox game/row/taxonomy/monetization | }}\n{{Infobox game/row/taxonomy/microtransactions | }}\n{{Infobox game/row/taxonomy/modes | }}\n{{Infobox game/row/taxonomy/pacing | }}\n{{Infobox game/row/taxonomy/perspectives | }}\n{{Infobox game/row/taxonomy/controls | }}\n{{Infobox game/row/taxonomy/genres | }}\n{{Infobox game/row/taxonomy/sports | }}\n{{Infobox game/row/taxonomy/vehicles | }}\n{{Infobox game/row/taxonomy/art styles | }}\n{{Infobox game/row/taxonomy/themes | }}\n{{Infobox game/row/taxonomy/series | }}", ownline: true } } } } } } } } } } }); /* Adds fade out on mouse out for the dropdown menus */ /* disabled 2022-09-17 due to seeminlgy causing issues with menus in certain scenarios $('div.tool-select > div.menu > div').mouseleave(function() { $(this).fadeOut(); }); */ }); }; // Check if we're editing a page, and if so customize the toolbar if (['edit', 'submit'].indexOf(mw.config.get('wgAction')) !== -1) { $.when( mw.loader.using( 'ext.wikiEditor' ), $.ready ).then( customizeToolbar ); } // Add achors in headers function addAnchorInHeaders() { $('span.mw-headline', '#mw-content-text').each(function() { var $this = $(this); if ($this.attr('id') !== undefined) { $('<span class="mw-linkhere"></span>').text(' • ').append( $('<a></a>').text('Link').attr({title: 'Link', href: '#'+$this.attr('id')}) ).appendTo(this); } }); } $(addAnchorInHeaders); /* ===== FUNCTIONALITY : Collapsible TOC levels ========================================================================================================================= * * Adds collapsible TOC levels copied directly over from the French Wiktionary. * * Source Code: * - Javascript : https://fr.wiktionary.org/wiki/MediaWiki:Gadget-SommaireDeveloppable.js * * 2019-07-21 - Added expand/collapse all options + storing the setting in a cookie. * 2019-07-20 - Localization, updated to automatically expand Essential Improvements, various changes. * 2019-07-19 - Initial commit, with a minor change to CollapsibleTOC_LabelNeither. * */ /* ----------------------------------------------------------------------------------------------------- Table of contents which shows by default only level 1 headers (so as not to clutter the screen) but whose higher depth levels are viewable (or expandable) by clicking a [+] button (or [-]), in a way similar to what classic file browsers do. -------------------------------------------------------------------------------------------------------- Authors: * Eiku April 25, 2012 for most of the code. * ArséniureDeGallium for details and various additions. * Inspired by WikiTravel (http://files.wikitravel.org/mw/skins/common/wikibits.js?0.1), but made differently --------------------------------------------------------------------------------------------------------*/ /*----------------- config variables ------------------------------------------*/ // Which pages should we expand the first level 1 section automatically on? var CollapsibleTOC_PreDevelop = (mw.config.get('wgNamespaceNumber') === 0); // only for the main namespace // Should we number the sections in the TOC? var CollapsibleTOC_NumberHeadings = mw.user.options.get('numberheadings'); // use the user's preferences, aka is "auto-number headings" enabled or not? // What characters to use to indicate expanded/collapsed status? var CollapsibleTOC_LabelExpand = '[+]'; var CollapsibleTOC_LabelCollapse = '[-]'; var CollapsibleTOC_LabelNeither = ' '; // Help tooltips var CollapsibleTOC_TooltipExpand = 'This section has subsections, click to expand them'; var CollapsibleTOC_TooltipCollapse = 'Click to collapse the subsections'; var CollapsibleTOC_TooltipNeither = 'This section has no subsections'; /*------------------- hooks the function ----------------------------------------------*/ // This is where the possible presence of the {{CollapsibleTOC}} template is managed in the page // * CollapsibleTOC_NO has priority // * CollapsibleTOC_YES afterwards // * for the rest of the majority of pages it's the namespace that decides var NoCollapsibleTOC = document.getElementById('CollapsibleTOC_NO'); var YesCollapsibleTOC = document.getElementById('CollapsibleTOC_YES') || (mw.config.get('wgNamespaceNumber') === 0); if ((!NoCollapsibleTOC) && (YesCollapsibleTOC)) { $(CollapsibleTOC_Main); $(CollapsibleTOC_Toggle); } /*----------------- support function ------------------------------------------*/ jQuery.fn.cleanWhitespace = function() { this.contents().filter( function() { return (this.nodeType == 3 && !/\S/.test(this.nodeValue)); }) .remove(); return this; }; /*----------------- main function ------------------------------------------*/ function CollapsibleTOC_Main() { alwaysExpand = $.cookie( 'pcgw_alwaysexpand' ) === '1'; // for each reasonable level of depth of table of contents for (var level = 1; level < 7; level++) { // get the list of <li> class elements "toclevel-n" var lis = $('.toclevel-' + level); if (lis.length === 0) break; // no need to continue, there is no deeper level // for each for (var _i = 0; _i < lis.length; _i++) { var li = lis[_i]; // check that it is a <li> if (li.tagName.toLowerCase() != 'li') continue; var plus; if (li.children.length >= 2) { var ul = li.children[1]; // check that the 2nd child is a <ul> if (ul.tagName.toLowerCase() != 'ul') continue; // add (later) a clickable element plus = document.createElement('a'); // add the 'collapsibletoc-section-toggle' CSS class to it plus.className = "collapsibletoc-section-toggle"; // if we click on it and that ul is visible, it hides it, and vice versa plus.onclick = (function(ul, plus) { return function() { if (ul.style.display == 'none') { ul.style.display = 'block'; plus.title = CollapsibleTOC_TooltipCollapse; plus.innerHTML = CollapsibleTOC_LabelCollapse; } else { ul.style.display = 'none'; plus.title = CollapsibleTOC_TooltipExpand; plus.innerHTML = CollapsibleTOC_LabelExpand; } }; })(ul, plus); if (alwaysExpand) { ul.style.display = 'block'; plus.title = CollapsibleTOC_TooltipCollapse; plus.innerHTML = CollapsibleTOC_LabelCollapse; } else { // hide by default, except for the first one section (to advertise) if ((level == 1) && (li.children[0].innerText == "Essential improvements") && CollapsibleTOC_PreDevelop) { ul.style.display = 'block'; plus.title = CollapsibleTOC_TooltipCollapse; plus.innerHTML = CollapsibleTOC_LabelCollapse; } else { ul.style.display = 'none'; plus.title = CollapsibleTOC_TooltipExpand; plus.innerHTML = CollapsibleTOC_LabelExpand; } } plus.style.cursor = 'pointer'; // cursor to indicate its a link } else { // add a visual element that takes as many spaces for same width as + and - for elements without child headers plus = document.createElement('span'); //plus.title = CollapsibleTOC_TooltipNeither; plus.innerHTML = CollapsibleTOC_LabelNeither; plus.style.cursor = 'default'; // force default pointer to hide the fact that users are currently hovering over a bunch of non-breaking spaces ( ) // add the 'collapsibletoc-section-neither' CSS class to it plus.className = "collapsibletoc-section-neither"; } // use a monospaced font for the expand/collapse links so that they have the same width regardless of character plus.style.fontFamily = 'courier, monospace, mono'; plus.style.color = '#000'; plus.style.fontWeight = 'bold'; plus.style.textDecoration = 'none'; // insert the link as the 1st node of the current <li> $(li).children().first().before(plus); } } //---- visibility of auto-number headings ---- var spans = $('.tocnumber'); if (CollapsibleTOC_NumberHeadings) { for (var _i = 0; _i < spans.length; _i++) { spans[_i].style.display = "inline"; } } /* else { for (var _i = 0; _i < spans.length; _i++) { spans[_i].style.display = "none"; } // in Chrome, if you leave the display: table-cell by default, // a line break inserts between the added [+] and the <a> that follows. $('.toctext').css('display', 'inline'); } */ // Unnecessary $('.toclevel-1 a').cleanWhitespace(); } function CollapsibleTOC_ToggleAll(s) { for (var level = 1; level < 7; level++) { // get the list of <li> class elements "toclevel-n" var lis = $('.toclevel-' + level); if (lis.length === 0) break; // no need to continue, there is no deeper level // for each for (var _i = 0; _i < lis.length; _i++) { var li = lis[_i]; // check that it is a <li> if (li.tagName.toLowerCase() != 'li') continue; if (li.children.length >= 3) { var toggle = li.children[0]; var ul = li.children[2]; // check that the 3rd child is a <ul> if (ul.tagName.toLowerCase() != 'ul') continue; if (s == 'block') { ul.style.display = 'block'; toggle.title = CollapsibleTOC_TooltipCollapse; toggle.innerHTML = CollapsibleTOC_LabelCollapse; } else { ul.style.display = 'none'; toggle.title = CollapsibleTOC_TooltipExpand; toggle.innerHTML = CollapsibleTOC_LabelExpand; } } } } } function CollapsibleTOC_Toggle() { // Table of contents toggle var $content = $('#mw-content-text'); var $toc, $tocTitle, $tocToggleLink, $tocList, alwaysExpand; $toc = $content.find( '#toc' ); $tocTitle = $content.find( '.toctitle' ); $tocToggleLink = $content.find( '#alwaysToggle' ); $tocList = $toc.find( 'ul' ).eq( 0 ); // Hide/show the table of contents element function toggleSections() { if ( $.cookie( 'pcgw_alwaysexpand' ) === '1' ) { CollapsibleTOC_ToggleAll('none'); $tocToggleLink.html( '<span class="tocExpandAll">e</span>' ); $.cookie( 'pcgw_alwaysexpand', null, { expires: 365, path: '/' } ); } else { CollapsibleTOC_ToggleAll('block'); $tocToggleLink.html( '<span class="tocCollapseAll">c</span>' ); $.cookie( 'pcgw_alwaysexpand', '1', { expires: 365, path: '/' } ); } } // Only add it if there is a complete TOC and it doesn't // have a toggle added already if ( $toc.length && $tocTitle.length && $tocList.length && !$tocToggleLink.length ) { alwaysExpand = $.cookie( 'pcgw_alwaysexpand' ) === '1'; $tocToggleLink = $( '<a href="#" id="togglelink"></a>' ) .html( alwaysExpand ? '<span class="tocCollapseAll">c</span>' : '<span class="tocExpandAll">e</span>' ) .click( function ( e ) { e.preventDefault(); toggleSections(); } ); $tocTitle.append( $tocToggleLink .wrap( '<span class="toctoggle collapsibletoc-toggle"></span>' ) .parent() .prepend( ' [' ) .append( '] ' ) ); if ( alwaysExpand ) { CollapsibleTOC_ToggleAll('block'); } } } /* ===== FUNCTIONALITY : Adds ReferenceTooltips ========================================================================================================================= * * Adds ReferenceTooltips copied directly over from the Russian Wikipedia. * * Information: https://www.mediawiki.org/w/index.php?title=Topic:Ueqlcc482l9yw8gv&topic_showPostId=ueqlcc6fkvg15qzj#flow-post-ueqlcc6fkvg15qzj * * Source Code: * - Javascript : https://ru.wikipedia.org/wiki/MediaWiki:Gadget-referenceTooltips.js * - CSS : https://ru.wikipedia.org/wiki/MediaWiki:Gadget-referenceTooltips.css * * 2019-02-22 - Merged with latest version from https://ru.wikipedia.org/wiki/MediaWiki:Gadget-referenceTooltips.js * 2018-09-17 - Updated + added english text (Google Translated) * */ // See [[mw:Reference Tooltips]] var referenceTooltips = function () { // ruwiki settings var REF_LINK_SELECTOR = '.reference, a[href^="#CITEREF"]', COMMENTED_TEXT_CLASS = 'ts-comment-commentedText', COMMENTED_TEXT_SELECTOR = '.' + COMMENTED_TEXT_CLASS + ', abbr[title]'; mw.messages.set( { 'rt-settings': 'Tooltip settings', // hover alt text on the cog wheel shown on the tooltips. 'rt-settings-title': 'Reference Tooltips settings', 'rt-save': 'Save settings', 'rt-cancel': 'Cancel', 'rt-enable': 'Enable Reference Tooltips', 'rt-disable': 'Disable Reference Tooltips', 'rt-activationMethod': 'Tooltip is activated by:', 'rt-hovering': 'hovering', 'rt-clicking': 'clicking', 'rt-delay': 'Delay before the tooltip appears (in milliseconds):', 'rt-tooltipsForComments': 'Show tooltips over <span title="Tooltip example" class="'+ COMMENTED_TEXT_CLASS +'" style="border-bottom: 1px dotted; cursor: help;">underlined text</span> in the style of Reference Tooltips (allows you to see tooltips on devices without mouse support).', 'rt-disabledNote': 'Once disabled, Reference Tooltips can be re-enabled using a link in the footer of the page.', 'rt-ready': 'Done', 'rt-enable-footer': 'Enable Reference Tooltips', // Text as shown in the footer among the category links 'rt-enabled': 'Reference Tooltips have been enabled.' // Shown on the popup in the top right corner when re-enabling Reference Tooltips from the footer. } ); // "Global" variables var SECONDS_IN_A_DAY = 60 * 60 * 24, CLASSES = { FADE_IN_DOWN: 'rt-fade-in-down', FADE_IN_UP: 'rt-fade-in-up', FADE_OUT_DOWN: 'rt-fade-out-down', FADE_OUT_UP: 'rt-fade-out-up' }, IS_TOUCHSCREEN = 'ontouchstart' in document.documentElement, // Quite a rough check for mobile browsers, a mix of what is advised at // https://stackoverflow.com/a/24600597 (sends to // https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent) // and https://stackoverflow.com/a/14301832 IS_MOBILE = /Mobi|Android/i.test( navigator.userAgent ) || typeof window.orientation !== 'undefined', CLIENT_NAME = $.client.profile().name, settingsString, settings, enabled, delay, activatedByClick, tooltipsForComments, cursorWaitCss, windowManager, $body = $( document.body ), $window = $( window ); function rt( $content ) { // Popups gadget if ( window.pg ) { return; } var teSelector, settingsDialogOpening = false; function setSettingsCookie() { mw.cookie.set( 'RTsettings', Number( enabled ) + '|' + delay + '|' + Number( activatedByClick ) + '|' + Number( tooltipsForComments ), { path: '/', expires: 90 * SECONDS_IN_A_DAY } ); } function enableRt() { enabled = true; setSettingsCookie(); $( '.rt-enableItem' ).remove(); rt( $content ); mw.notify( mw.msg( 'rt-enabled' ) ); } function disableRt() { $content.find( teSelector ).removeClass( 'rt-commentedText' ).off( '.rt' ); $body.off( '.rt' ); $window.off( '.rt' ); } function addEnableLink() { // #footer-places – Vector // #f-list – Timeless, Monobook, Modern // parent of #footer li – Cologne Blue // var $footer = $( '#footer-places, #f-list' ); var $footer = $( '#mw-normal-catlinks ul' ); if ( !$footer.length ) { $footer = $( '#footer li' ).parent(); } $footer.append( $( '<li>' ) .addClass( 'rt-enableItem' ) .append( $( '<a>' ) .text( mw.msg( 'rt-enable-footer' ) ) .attr( 'href', 'javascript:' ) .click( function ( e ) { e.preventDefault(); enableRt(); } ) ) ); } function TooltippedElement( $element ) { var tooltip, te = this; function onStartEvent( e ) { var showRefArgs; if ( activatedByClick ) { e.preventDefault(); } if ( !te.noRef ) { showRefArgs = [ $( this ) ]; if ( te.type !== 'supRef' ) { showRefArgs.push( e.pageX, e.pageY ); } te.showRef.apply( te, showRefArgs ); } } function onEndEvent() { if ( !te.noRef ) { te.hideRef(); } } if ( !$element ) { return; } // TooltippedElement.$element and TooltippedElement.$originalElement will be different when // the first is changed after its cloned version is hovered in a tooltip this.$element = $element; this.$originalElement = $element; if ( this.$element.is( REF_LINK_SELECTOR ) ) { if ( this.$element.prop( 'tagName' ) === 'SUP' ) { this.type = 'supRef'; } else { this.type = 'harvardRef'; } } else { this.type = 'commentedText'; this.comment = this.$element.attr( 'title' ); if ( !this.comment ) { return; } this.$element.addClass('rt-commentedText'); } this.$element.on( activatedByClick ? { 'click.rt': onStartEvent } : { 'mouseenter.rt': onStartEvent, 'mouseleave.rt': onEndEvent } ); this.hideRef = function ( immediately ) { clearTimeout( te.showTimer ); if ( this.type === 'commentedText' ) { this.$element.attr( 'title', this.comment ); } if ( this.tooltip && this.tooltip.isPresent ) { if ( activatedByClick || immediately ) { this.tooltip.hide(); } else { this.hideTimer = setTimeout( function () { te.tooltip.hide(); }, 200 ); } } else if ( this.$ref && this.$ref.hasClass( 'rt-target' ) ) { this.$ref.removeClass( 'rt-target' ); if ( activatedByClick ) { $body.off( 'click.rt touchstart.rt', this.onBodyClick ); } } }; this.showRef = function ( $element, ePageX, ePageY ) { // Popups gadget if ( window.pg ) { disableRt(); return; } var tooltipInitiallyPresent = this.tooltip && this.tooltip.isPresent; function reallyShow() { var viewportTop, refOffsetTop, teHref; if ( !te.$ref && !te.comment ) { teHref = te.type === 'supRef' ? te.$element.find( 'a' ).attr( 'href' ) : te.$element.attr( 'href' ); // harvardRef te.$ref = teHref && $( '#' + $.escapeSelector( teHref.slice( 1 ) ) ); if ( !te.$ref || !te.$ref.length || !te.$ref.text() ) { te.noRef = true; return; } } if ( !tooltipInitiallyPresent && !te.comment ) { viewportTop = $window.scrollTop(); refOffsetTop = te.$ref.offset().top; if ( !activatedByClick && viewportTop < refOffsetTop && viewportTop + $window.height() > refOffsetTop + te.$ref.height() && // There can be gadgets/scripts that make references horizontally scrollable. $window.width() > te.$ref.offset().left + te.$ref.width() ) { // Highlight the reference itself te.$ref.addClass( 'rt-target' ); return; } } if ( !te.tooltip ) { te.tooltip = new Tooltip( te ); if ( !te.tooltip.$content.length ) { return; } } // If this tooltip is called from inside another tooltip. We can't define it // in the constructor since a ref can be cloned but have the same Tooltip object; // so, Tooltip.parent is a floating value. te.tooltip.parent = te.$element.closest( '.rt-tooltip' ).data( 'tooltip' ); if ( te.tooltip.parent && te.tooltip.parent.disappearing ) { return; } te.tooltip.show(); if ( tooltipInitiallyPresent ) { if ( te.tooltip.$element.hasClass( 'rt-tooltip-above' ) ) { te.tooltip.$element.addClass( CLASSES.FADE_IN_DOWN ); } else { te.tooltip.$element.addClass( CLASSES.FADE_IN_UP ); } return; } te.tooltip.calculatePosition( ePageX, ePageY ); $window.on( 'resize.rt', te.onWindowResize ); } // We redefine this.$element here because e.target can be a reference link inside // a reference tooltip, not a link that was initially assigned to this.$element this.$element = $element; if ( this.type === 'commentedText' ) { this.$element.attr( 'title', '' ); } if ( activatedByClick ) { if ( tooltipInitiallyPresent || ( this.$ref && this.$ref.hasClass( 'rt-target' ) ) ) { return; } else { setTimeout( function () { $body.on( 'click.rt touchstart.rt', te.onBodyClick ); }, 0 ); } } if ( activatedByClick || tooltipInitiallyPresent ) { reallyShow(); } else { this.showTimer = setTimeout( reallyShow, delay ); } }; this.onBodyClick = function ( e ) { if ( !te.tooltip && !te.$ref.hasClass( 'rt-target' ) ) { return; } var $current = $( e.target ); function contextMatchesParameter( parameter ) { return this === parameter; } // The last condition is used to determine cases when a clicked tooltip is the current // element's tooltip or one of its descendants while ( $current.length && ( !$current.hasClass( 'rt-tooltip' ) || !$current.data( 'tooltip' ) || !$current.data( 'tooltip' ).upToTopParent( contextMatchesParameter, [ te.tooltip ], true ) ) ) { $current = $current.parent(); } if ( !$current.length ) { te.hideRef(); } }; this.onWindowResize = function () { te.tooltip.calculatePosition(); }; } function Tooltip( te ) { function openSettingsDialog() { var settingsDialog, settingsWindow; if ( cursorWaitCss ) { cursorWaitCss.disabled = true; } function SettingsDialog() { SettingsDialog.parent.call( this ); } OO.inheritClass( SettingsDialog, OO.ui.ProcessDialog ); SettingsDialog.static.name = 'settingsDialog'; //SettingsDialog.static.size = 'large'; SettingsDialog.static.title = mw.msg( 'rt-settings-title' ); SettingsDialog.static.actions = [ { modes: 'basic', action: 'save', label: mw.msg( 'rt-save' ), flags: [ 'primary', 'progressive' ] }, { modes: 'basic', label: mw.msg( 'rt-cancel' ), flags: 'safe' }, { modes: 'disabled', action: 'deactivated', label: mw.msg( 'rt-ready' ), flags: [ 'primary', 'progressive' ] } ]; SettingsDialog.prototype.initialize = function () { var dialog = this; SettingsDialog.parent.prototype.initialize.apply( this, arguments ); this.enableOption = new OO.ui.RadioOptionWidget( { label: mw.msg( 'rt-enable' ) } ); this.disableOption = new OO.ui.RadioOptionWidget( { label: mw.msg( 'rt-disable' ) } ); this.enableSelect = new OO.ui.RadioSelectWidget( { items: [ this.enableOption, this.disableOption ], classes: [ 'rt-enableSelect' ] } ); this.enableSelect.selectItem( this.enableOption ); this.enableSelect.on( 'choose', function ( item ) { if ( item === dialog.disableOption ) { dialog.activationMethodSelect.setDisabled( true ); dialog.delayInput.setDisabled( true ); dialog.tooltipsForCommentsCheckbox.setDisabled( true ); } else { dialog.activationMethodSelect.setDisabled( false ); dialog.delayInput.setDisabled( dialog.clickOption.isSelected() ); dialog.tooltipsForCommentsCheckbox.setDisabled( false ); } } ); this.hoverOption = new OO.ui.RadioOptionWidget( { label: mw.msg( 'rt-hovering' ) } ); this.clickOption = new OO.ui.RadioOptionWidget( { label: mw.msg( 'rt-clicking' ) } ); this.activationMethodSelect = new OO.ui.RadioSelectWidget( { items: [ this.hoverOption, this.clickOption ] } ); this.activationMethodSelect.selectItem( activatedByClick ? this.clickOption : this.hoverOption ); this.activationMethodSelect.on( 'choose', function ( item ) { if ( item === dialog.clickOption ) { dialog.delayInput.setDisabled( true ); } else { dialog.delayInput.setDisabled( dialog.clickOption.isSelected() ); } } ); this.activationMethodField = new OO.ui.FieldLayout( this.activationMethodSelect, { label: mw.msg( 'rt-activationMethod' ), align: 'top' } ); this.delayInput = new OO.ui.NumberInputWidget( { input: { value: delay }, step: 50, min: 0, max: 5000, disabled: activatedByClick, classes: [ 'rt-numberInput' ] } ); this.delayField = new OO.ui.FieldLayout( this.delayInput, { label: mw.msg( 'rt-delay' ), align: 'top' } ); this.tooltipsForCommentsCheckbox = new OO.ui.CheckboxInputWidget( { selected: tooltipsForComments } ); this.tooltipsForCommentsField = new OO.ui.FieldLayout( this.tooltipsForCommentsCheckbox, { label: new OO.ui.HtmlSnippet( mw.msg( 'rt-tooltipsForComments' ) ), align: 'inline', classes: [ 'rt-tooltipsForCommentsField' ] } ); new TooltippedElement( this.tooltipsForCommentsField.$element.find( '.' + COMMENTED_TEXT_CLASS ) ); this.fieldset = new OO.ui.FieldsetLayout(); this.fieldset.addItems( [ this.activationMethodField, this.delayField, this.tooltipsForCommentsField ] ); this.panelSettings = new OO.ui.PanelLayout( { padded: true, expanded: false } ); this.panelSettings.$element.append( this.enableSelect.$element, $( '<hr>' ).addClass( 'rt-settingsFormSeparator' ), this.fieldset.$element ); this.panelDisabled = new OO.ui.PanelLayout( { padded: true, expanded: false } ); this.panelDisabled.$element.append( $( '<table>' ) .addClass( 'rt-disabledHelp' ) .append( $( '<tr>' ).append( $( '<td>' ).append( $( '<img>' ).attr( 'src', '/images/b/b3/Reference_Tooltips_footer.png' ) ), $( '<td>' ) .addClass( 'rt-disabledNote' ) .text( mw.msg( 'rt-disabledNote' ) ) ) ) ); this.stackLayout = new OO.ui.StackLayout( { items: [ this.panelSettings, this.panelDisabled ] } ); this.$body.append( this.stackLayout.$element ); }; SettingsDialog.prototype.getSetupProcess = function ( data ) { return SettingsDialog.parent.prototype.getSetupProcess.call( this, data ) .next( function () { this.stackLayout.setItem( this.panelSettings ); this.actions.setMode( 'basic' ); }, this ); }; SettingsDialog.prototype.getActionProcess = function ( action ) { var dialog = this; if ( action === 'save' ) { return new OO.ui.Process( function () { var newDelay = Number( dialog.delayInput.getValue() ); enabled = dialog.enableOption.isSelected(); if ( newDelay >= 0 && newDelay <= 5000 ) { delay = newDelay; } activatedByClick = dialog.clickOption.isSelected(); tooltipsForComments = dialog.tooltipsForCommentsCheckbox.isSelected(); setSettingsCookie(); if ( enabled ) { dialog.close(); disableRt(); rt( $content ); } else { dialog.actions.setMode( 'disabled' ); dialog.stackLayout.setItem( dialog.panelDisabled ); disableRt(); addEnableLink(); } } ); } else if ( action === 'deactivated' ) { dialog.close(); } return SettingsDialog.parent.prototype.getActionProcess.call( this, action ); }; SettingsDialog.prototype.getBodyHeight = function () { return this.stackLayout.getCurrentItem().$element.outerHeight( true ); //return 400; // sets height of the dialog to ~430px }; tooltip.upToTopParent( function adjustRightAndHide() { if ( this.isPresent ) { if ( this.$element[ 0 ].style.right ) { this.$element.css( 'right', '+=' + ( window.innerWidth - $window.width() ) ); } this.te.hideRef( true ); } } ); if ( !windowManager ) { windowManager = new OO.ui.WindowManager(); $body.append( windowManager.$element ); } settingsDialog = new SettingsDialog(); windowManager.addWindows( [ settingsDialog ] ); settingsWindow = windowManager.openWindow( settingsDialog ); settingsWindow.opened.then( function () { settingsDialogOpening = false; } ); settingsWindow.closed.then( function () { windowManager.clearWindows(); } ); } var tooltip = this; // This variable can change: one tooltip can be called from a harvard-style reference link // that is put into different tooltips this.te = te; switch ( this.te.type ) { case 'supRef': this.id = 'rt-' + this.te.$originalElement.attr( 'id' ); this.$content = this.te.$ref .children() .not( '.mw-cite-backlink' ) .clone( true ); break; case 'harvardRef': this.id = 'rt-' + this.te.$originalElement.closest( 'li' ).attr( 'id' ); this.$content = this.te.$ref .clone( true ) .removeAttr( 'id' ); break; case 'commentedText': this.id = 'rt-' + String( Math.random() ).slice( 2 ); this.$content = $( document.createTextNode( this.te.comment ) ); break; } if ( !this.$content.length ) { return; } this.insideWindow = Boolean( this.te.$element.closest( '.oo-ui-window' ).length ); this.$element = $( '<div>' ) .addClass( 'rt-tooltip' ) .attr( 'id', this.id ) .attr( 'role', 'tooltip' ) .data( 'tooltip', this ); if ( this.insideWindow ) { this.$element.addClass( 'rt-tooltip-insideWindow' ); } // We need the $content interlayer here in order for the settings icon to have the correct // margins this.$content = this.$content .wrapAll( '<div>' ) .parent() .addClass( 'rt-tooltipContent' ) .appendTo( this.$element ); if ( !activatedByClick ) { this.$element .mouseenter( function () { if ( !tooltip.disappearing ) { tooltip.upToTopParent( function () { this.show(); } ); } } ) .mouseleave( function ( e ) { // https://stackoverflow.com/q/47649442 workaround. Relying on relatedTarget // alone has pitfalls: when alt-tabbing, relatedTarget is empty too if ( CLIENT_NAME !== 'chrome' || ( !e.originalEvent || e.originalEvent.relatedTarget !== null || !tooltip.clickedTime || $.now() - tooltip.clickedTime > 50 ) ) { tooltip.upToTopParent( function () { this.te.hideRef(); } ); } } ) .click( function () { tooltip.clickedTime = $.now(); } ); } if ( !this.insideWindow ) { $( '<div>' ) .addClass( 'rt-settingsLink' ) .attr( 'title', mw.msg( 'rt-settings' ) ) .click( function () { if ( settingsDialogOpening ) { return; } settingsDialogOpening = true; if ( mw.loader.getState( 'oojs-ui' ) !== 'ready' ) { if ( cursorWaitCss ) { cursorWaitCss.disabled = false; } else { cursorWaitCss = mw.util.addCSS( 'body { cursor: wait; }' ); } } mw.loader.using( [ 'oojs', 'oojs-ui' ], openSettingsDialog ); } ) .prependTo( this.$content ); } // Tooltip tail element is inside tooltip content element in order for the tooltip // not to disappear when the mouse is above the tail $( '<div>' ) .addClass( 'rt-tooltipTail' ) .prependTo( this.$element ); this.disappearing = false; this.show = function () { this.disappearing = false; clearTimeout( this.te.hideTimer ); clearTimeout( this.te.removeTimer ); this.$element .removeClass( CLASSES.FADE_OUT_DOWN ) .removeClass( CLASSES.FADE_OUT_UP ); if ( !this.isPresent ) { $body.append( this.$element ); } this.isPresent = true; }; this.hide = function () { var tooltip = this; tooltip.disappearing = true; if ( tooltip.$element.hasClass( 'rt-tooltip-above' ) ) { tooltip.$element .removeClass( CLASSES.FADE_IN_DOWN ) .addClass( CLASSES.FADE_OUT_UP ); } else { tooltip.$element .removeClass( CLASSES.FADE_IN_UP ) .addClass( CLASSES.FADE_OUT_DOWN ); } tooltip.te.removeTimer = setTimeout( function () { if ( tooltip.isPresent ) { tooltip.$element.detach(); if ( tooltip.tailCss ) { tooltip.tailCss.disabled = true; } if ( activatedByClick ) { $body.off( 'click.rt touchstart.rt', tooltip.te.onBodyClick ); } $window.off( 'resize.rt', tooltip.te.onWindowResize ); tooltip.isPresent = false; } }, 200 ); }; this.calculatePosition = function ( ePageX, ePageY ) { var teElement, teOffsets, teOffset, tooltipTailOffsetX, tooltipTailLeft, offsetYCorrection = 0; if ( this.tailCss ) { this.tailCss.disabled = true; } teElement = this.te.$element.get( 0 ); if ( ePageX !== undefined ) { tooltipTailOffsetX = ePageX; teOffsets = teElement.getClientRects && teElement.getClientRects() || teElement.getBoundingClientRect(); if ( teOffsets.length > 1 ) { for (var i = teOffsets.length - 1; i >= 0; i--) { if ( ePageY >= Math.round( $window.scrollTop() + teOffsets[i].top ) && ePageY <= Math.round( $window.scrollTop() + teOffsets[i].top + teOffsets[i].height ) ) { teOffset = teOffsets[i]; } } } } if ( !teOffset ) { teOffset = teElement.getClientRects && teElement.getClientRects()[0] || teElement.getBoundingClientRect(); } teOffset = { top: $window.scrollTop() + teOffset.top, left: $window.scrollLeft() + teOffset.left, width: teOffset.width, height: teOffset.height }; if ( !tooltipTailOffsetX ) { tooltipTailOffsetX = ( teOffset.left * 2 + teOffset.width ) / 2; } if ( CLIENT_NAME === 'msie' && this.te.type === 'supRef' ) { offsetYCorrection = -Number( this.te.$element.parent().css( 'font-size' ).replace( 'px', '' ) ) / 2; } this.$element.css( { top: teOffset.top - this.$element.outerHeight() - 7 + offsetYCorrection, left: tooltipTailOffsetX - 20, right: '' } ); // Is it squished against the right side of the page? if ( this.$element.offset().left + this.$element.outerWidth() > $window.width() - 1 ) { this.$element.css( { left: '', right: 0 } ); tooltipTailLeft = tooltipTailOffsetX - this.$element.offset().left - 5; } // Is a part of it above the top of the screen? if ( teOffset.top < this.$element.outerHeight() + $window.scrollTop() + 6 ) { this.$element .removeClass( 'rt-tooltip-above' ) .addClass( 'rt-tooltip-below' ) .addClass( CLASSES.FADE_IN_UP ) .css( { top: teOffset.top + teOffset.height + 9 + offsetYCorrection } ); if ( tooltipTailLeft ) { this.tailCss = mw.util.addCSS( '#' + $.escapeSelector( tooltip.id ) + ' .rt-tooltipTail { left: ' + ( tooltipTailLeft + 12 ) + 'px; }' ); } } else { this.$element .removeClass( 'rt-tooltip-below' ) .addClass( 'rt-tooltip-above' ) .addClass( CLASSES.FADE_IN_DOWN ) // A fix for cases when a tooltip shown once is then wrongly positioned when it // is shown again after a window resize. We just repeat what is above. .css( { top: teOffset.top - this.$element.outerHeight() - 7 + offsetYCorrection } ); if ( tooltipTailLeft ) { // 12 is the tail element width/height this.tailCss = mw.util.addCSS( '#' + $.escapeSelector( tooltip.id ) + ' .rt-tooltipTail { left: ' + tooltipTailLeft + 'px; }' ); } } }; // Run some function for all the tooltips up to the top one in a tree. Its context will be // the tooltip, while its parameters may be passed to Tooltip.upToTopParent as an array // in the second parameter. If the third parameter passed to ToolTip.upToTopParent is true, // the execution stops when the function in question returns true for the first time, // and ToolTip.upToTopParent returns true as well. this.upToTopParent = function ( func, parameters, stopAtTrue ) { var returnValue, currentTooltip = this; do { returnValue = func.apply( currentTooltip, parameters ); if ( stopAtTrue && returnValue ) { break; } } while ( currentTooltip = currentTooltip.parent ); if ( stopAtTrue ) { return returnValue; } }; } if ( !enabled ) { addEnableLink(); return; } teSelector = REF_LINK_SELECTOR; if ( tooltipsForComments ) { teSelector += ', ' + COMMENTED_TEXT_SELECTOR; } $content.find( teSelector ).each( function () { new TooltippedElement( $( this ) ); } ); } try { settingsString = mw.cookie.get( 'RTsettings' ); } catch (e) { } if ( settingsString ) { settings = settingsString.split( '|' ); enabled = Boolean( Number( settings[ 0 ] ) ); delay = Number( settings[ 1 ] ); activatedByClick = Boolean( Number( settings[ 2 ] ) ); // The forth value was added later, so we provide for a default value. See comments below // for why we use "IS_TOUCHSCREEN && IS_MOBILE". tooltipsForComments = settings[ 3 ] === undefined ? IS_TOUCHSCREEN && IS_MOBILE : Boolean( Number( settings[ 3 ] ) ); } else { enabled = true; delay = 200; // Since the mobile browser check is error-prone, adding IS_MOBILE condition here would probably // leave cases where a user interacting with the browser using touches doesn't know how to call // a tooltip in order to switch to activation by click. Some touch-supporting laptop users // interacting by touch (though probably not the most popular use case) would not be happy too. activatedByClick = IS_TOUCHSCREEN; // Arguably we shouldn't convert native tooltips into gadget tooltips for devices that have // mouse support, even if they have touchscreens (there are laptops with touchscreens). // IS_TOUCHSCREEN check here is for reliability, since the mobile check is prone to false // positives. tooltipsForComments = IS_TOUCHSCREEN && IS_MOBILE; } mw.hook( 'wikipage.content' ).add( rt ); /* Default: Fired when wiki content is being added to the DOM */ /* mw.hook( 'wikipage.categories' ).add( rt ); */ /* Custom: Fired when categories are being added to the DOM */ }; $(referenceTooltips); /* Run stuff after page have finished loading */ jQuery( document ).ready( function ( $ ) { /* Stupid Firefox DOM related bug: Forces a refresh of the elements to fix rendering bugs */ $(".article-image").hide().show(0); /* Move Gamesplanet row to the top (but not above Retail row) */ var rowGamesplanet = $('.table-availability-body-row').find('[data-sort-value="Gamesplanet"]').parent(); var rowRetail = $('.table-availability-body-row').find('[data-sort-value="aaa- Retail"]').last().parent(); if (rowRetail.length > 0) { rowGamesplanet.insertAfter ( rowRetail ); } else { rowGamesplanet.insertAfter ( rowGamesplanet.parent().find('tr:first-child') ); } /* Add Edit/History/Purge to nav -> toolbar */ if ( mw.config.get ('wgIsArticle') === true ) { if ( mw.config.get( 'wgUserGroups', [] ).indexOf( 'user' ) !== -1 ) { var edit_label = (mw.config.get('wgIsProbablyEditable') === true) ? 'Edit' : 'Source'; ModifySidebar( 'add', 'toolbox', edit_label, mw.util.getUrl(null, {'action': 'edit'}) ); } ModifySidebar( 'add', 'toolbox', 'History', mw.util.getUrl(null, {'action': 'history'}) ); ModifySidebar( 'add', 'toolbox', 'Purge cache', mw.util.getUrl(null, {'action': 'purge'}) ); } /* Add Recent changes (unpatrolled) to nav -> explore */ if ( mw.config.get( 'wgUserGroups', [] ).indexOf( 'editor' ) !== -1 || // Editor mw.config.get( 'wgUserGroups', [] ).indexOf( 'moderator' ) !== -1 || // Moderator mw.config.get( 'wgUserGroups', [] ).indexOf( 'sysop' ) !== -1 ) // Administrators { ModifySidebar( 'add', 'explore', 'Recent changes (unpatrolled)', mw.util.getUrl('Special:RecentChanges', {'hidepatrolled': '1'}) ); } } ); //</nowiki>