Vector Layout for Wikipedia (Fast)

returns old Wikipedia layout. (layout before 2023 redesign of the website)

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name Vector Layout for Wikipedia (Fast)
// @namespace -
// @version 1.3.1
// @description returns old Wikipedia layout. (layout before 2023 redesign of the website)
// @author NotYou
// @match *://wikipedia.org/*
// @match *://*.wikipedia.org/*

// @match *://wiktionary.org/*
// @match *://*.wiktionary.org/*

// @match *://wikinews.org/*
// @match *://*.wikinews.org/*

// @match *://wikivoyage.org/*
// @match *://*.wikivoyage.org/*

// @match *://wikiquote.org/*
// @match *://*.wikiquote.org/*

// @match *://wikiversity.org/*
// @match *://*.wikiversity.org/*

// @match *://wikibooks.org/*
// @match *://*.wikibooks.org/*

// @match *://wikifunctions.org/*
// @match *://*.wikifunctions.org/*

// @match *://wikidata.org/*
// @match *://*.wikidata.org/*

// @match *://wikisource.org/*
// @match *://*.wikisource.org/*

// @match *://mediawiki.org/*
// @match *://*.wikimedia.org/*

// @run-at document-start
// @license GPL-3.0-or-later
// @grant GM.openInTab
// ==/UserScript==

!function () {
    'use strict';

    const MAKE_CLEAN_URL = false // removes "useskin=vector" after loading

    const IS_DEBUG_MODE = false // instead of redirecting, logs information in console

    class Redirector {
        static canParseUrl(url) {
            try {
                new URL(url)

                return true
            } catch {
                return false
            }
        }

        static getNewUrl(inputUrl) {
            if (!this.canParseUrl(inputUrl)) return null

            const url = new URL(inputUrl)
            const { searchParams, pathname, origin, hash } = url
            const cleanUrl = origin + pathname

            if (searchParams.get('useskin') === 'vector' || url.pathname === '/') {
                return null
            }

            searchParams.set('useskin', 'vector')

            const params = '?' + searchParams.toString()
            const resultUrl = cleanUrl + params + hash

            return resultUrl
        }

        static tryToRedirect(options) {
            options = Object.assign({
                inputUrl: null, // mandatory
                saveHistory: false,
                openInNewTab: false
            }, options)

            if (typeof options.inputUrl !== 'string') {
                throw new Error('"inputUrl" is not a string')
            }

            if (!this.canParseUrl(options.inputUrl)) {
                throw new Error('Cannot parse "inputUrl": ' + String(options.inputUrl))
            }

            if (typeof options.saveHistory !== 'boolean') {
                throw new Error('"saveHistory" is not a boolean')
            }

            if (typeof options.openInNewTab !== 'boolean') {
                throw new Error('"openInNewTab" is not a boolean')
            }

            const newUrl = this.getNewUrl(options.inputUrl)

            if (newUrl === null) {
                return false
            }

            if (IS_DEBUG_MODE) {
                console.log('VLW - Debug\n', newUrl)

                return false
            }

            if (options.openInNewTab) {
                GM.openInTab(newUrl, {
                    active: true
                })
            } else if (options.saveHistory) {
                location.assign(newUrl)
            } else {
                location.replace(newUrl)
            }

            return true
        }
    }

    class Main {
        static isNullish(value) {
            return value === undefined || value === null
        }

        static getLinkNode(node) {
            if (node.tagName.toLowerCase() === 'a') {
                return node
            } else if (node.tagName.toLowerCase() === 'html' || this.isNullish(node.tagName)) {
                return null
            }

            return this.getLinkNode(node.parentNode)
        }

        static clearUrl() {
            const url = new URL(location.href)
            const { searchParams, pathname, hash } = url

            if (searchParams.get('useskin') !== 'vector') {
                return
            }

            searchParams.delete('useskin')

            const newSearchParams = searchParams.toString()
            const newPath = pathname + (newSearchParams ? '?' + newSearchParams : newSearchParams) + hash

            console.log(newPath)

            history.replaceState({}, '', newPath)
        }

        static onClick(ev) {
            if (ev.button !== 0 || ev.button !== 1) return

            const link = this.getLinkNode(ev.target)

            if (link !== null && !(ev.ctrlKey || ev.metaKey)) {
                const url = new URL(link.href)
                const isOrigin = url.hostname === location.hostname
                const isNotAnchor = !link.getAttribute('href').startsWith('#')
                const isOnlyLink = !link.getAttribute('role')

                if (isOrigin && isNotAnchor && isOnlyLink) {
                    ev.preventDefault()

                    Redirector.tryToRedirect({
                        inputUrl: link.href,
                        saveHistory: true,
                        openInNewTab: ev.type === 'auxclick'
                    })
                }
            }
        }

        static init() {
            let didRedirect = Redirector.tryToRedirect({
                inputUrl: location.href,
                saveHistory: false
            })

            if (didRedirect) {
                return
            }

            window.addEventListener('click', ev => this.onClick(ev))
            window.addEventListener('auxclick', ev => this.onClick(ev))

            if (MAKE_CLEAN_URL) {
                this.clearUrl()
            }
        }
    }

    Main.init()
}()