// ==UserScript==
// @name Wikipedia Side-by-side Edit
// @namespace https://en.wikipedia.orghttps://demo.azizisearch.com/lite/wikipedia/page/User:Arthurfragoso/SideBySideEdit.js
// @version 1.0.4
// @description Wikipedia Side-by-side Edit
// @author Arthurfragoso
// @match https://*.wikipedia.org/w/index.php?*action=*
// @icon https://en.wikipedia.org/static/favicon/wikipedia.ico
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.info
// @run-at document-end
// ==/UserScript==
/*
* This script should be able to run in any Userscript Manager. (greasyfork.org)
* My personal recomendation is the open source Violentmonkey.
* It also works directly as a Wikipedia Userscript for conveniency.
*
* Once installed, it should just work seamlessly.
*
* Feel free to fork and make customisations.
* The license is the same as the content in Wikipedia. (CC BY-SA 4.0)
*
* */
(async function() {
'use strict';
let scriptHandler = null;
// Get the <html> element
const htmlElement = document.documentElement;
if(!document.body.classList.contains('skin-vector-2022')){
console.log("Wikipedia Side-by-side Edit is only compatible with Vector (2022).");
return false;
}
// Adds the WpExtnSideBySideEnabled CSS class to the HTML tag,
// This allows for external scripts to check if it is enabled.
// It also prevents two versions of the script to be run at the same time,
// In case it is activated in Wikipedia and as a browser user script
if (htmlElement.classList.contains("WpExtnSideBySideEnabled")) {
return;
}
htmlElement.classList.add("WpExtnSideBySideEnabled");
if (typeof GM !== 'undefined' && GM.info && GM.info.scriptHandler) {
scriptHandler = GM.info.scriptHandler;
} else {
const stackTrace = new Error().stack;
scriptHandler = stackTrace.includes("wikipedia.org")
? "Wikipedia UserScript Manager"
: stackTrace;
}
console.log('Wikipedia Side-by-side Edit extension enabled by ' + scriptHandler);
//let wTextareaDefaultSize = '680px';
// Select the wikiPreview element
const wikiPreview = document.querySelector('#wikiPreview');
// If there is no wikiPreview, then it's not in a edit page.
if(!wikiPreview){
return false;
}
// Makes a Greasemonkey compatible setValue/getValue
if (typeof GM === 'undefined') {
// Define a custom GM object to handle localStorage operations
window.GM = {
setValue: async function(key, value) {
try {
// Convert the value to a JSON string before saving
localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error("Error setting value in localStorage:", error);
}
},
getValue: async function(key, defaultValue) {
try {
// Get the value from localStorage
const storedValue = localStorage.getItem(key);
// Parse the JSON string back to its original form
return storedValue !== null ? JSON.parse(storedValue) : defaultValue;
} catch (error) {
console.error("Error getting value from localStorage:", error);
return defaultValue;
}
}
};
}
// Helper function to create buttons
const createButton = (id, label, callback) => {
const button = document.createElement('button');
button.id = id;
button.textContent = label;
button.style.marginRight = 'calc(50vw - 39em)';
button.type = 'button'; // Ensure the button is of type "button" to prevent form submission
button.addEventListener('click', callback);
return button;
}
const swapElements = (element1, element2) => {
// Get the parent nodes of both elements
const parent1 = element1.parentNode;
const parent2 = element2.parentNode;
// Get the next siblings (to maintain insertion point)
const sibling1 = element1.nextSibling;
const sibling2 = element2.nextSibling;
// Move element1 to the position of element2
if (sibling2) {
parent2.insertBefore(element1, sibling2);
} else {
parent2.appendChild(element1);
}
// Move element2 to the position of element1
if (sibling1) {
parent1.insertBefore(element2, sibling1);
} else {
parent1.appendChild(element2);
}
}
// Editor
// To the left -> set it to: false
// To the right side -> set it to: true
let editorAtRightSide = await GM.getValue('editorAtRightSide', false);
// Define the behavior for "seek next image" and place cursor at insertion point
const sideBySideEdit = () => {
// Select necessary elements
const editForm = document.getElementById('editform');
const stickyContainer = document.querySelector('.vector-column-end .vector-sticky-pinned-container');
const mwBody = document.querySelector('.mw-body');
const wpTextbox1 = document.getElementById('wpTextbox1');
const pSearch = document.getElementById('p-search');
let ContentToBeMovedToTheRight = editorAtRightSide ? editForm : wikiPreview;
if (ContentToBeMovedToTheRight.closest('.vector-sticky-pinned-container')) {
console.log('Already in Side-by-Side mode');
return true;
};
// Changes to Wide view mode
// Check if the <html> tag has the class "vector-feature-limited-width-clientpref-1"
if (htmlElement.classList.contains("vector-feature-limited-width-clientpref-1")) {
// Replace the class with "vector-feature-limited-width-clientpref-0"
htmlElement.classList.replace(
"vector-feature-limited-width-clientpref-1",
"vector-feature-limited-width-clientpref-0"
);
console.log("Wikipedia layout set to wide mode.");
}
// Check if the Apperance options are in the right panel, and hides it.
if (htmlElement.classList.contains("vector-feature-appearance-pinned-clientpref-1")) {
// Replace the class with "vector-feature-limited-width-clientpref-0"
htmlElement.classList.replace(
"vector-feature-appearance-pinned-clientpref-1",
"vector-feature-appearance-pinned-clientpref-0"
);
console.log("Hide apperance menu");
}
// Move #editform into .vector-sticky-pinned-container
if (ContentToBeMovedToTheRight && stickyContainer) {
pSearch.parentNode.insertBefore(
createButton('SwitchEditFormAndPreviewPosition', '⇄', function() {
swapElements(editForm, wikiPreview);
editorAtRightSide = !editorAtRightSide;
GM.setValue('editorAtRightSide', editorAtRightSide); //save settings
}), pSearch.nextSibling);
stickyContainer.appendChild(ContentToBeMovedToTheRight);
console.log('#editform has been moved inside .vector-sticky-pinned-container');
// Add padding-right to the form
ContentToBeMovedToTheRight.style.paddingRight = '10px';
console.log('Padding-right added to #editform');
} else {
console.warn('Could not find #editform or .vector-sticky-pinned-container');
}
// Apply grid layout to .mw-body
if (mwBody) {
mwBody.style.display = 'grid';
mwBody.style.gridTemplateColumns = 'minmax(0, 1fr) minmax(0, 1fr)';
mwBody.style.columnGap = '24px';
console.log('Grid layout applied to .mw-body');
} else {
console.warn('Could not find .mw-body');
}
if (document.querySelector('.vector-column-end')) {
document.querySelector('.vector-column-end').style.marginTop = 0;
}
// Adjust height of #wpTextbox1
if (wpTextbox1 && typeof wTextareaDefaultSize !== 'undefined') {
wpTextbox1.style.height = wTextareaDefaultSize;
console.log('Height of #wpTextbox1 set');
}
};
// Attaches the side-by-side activation to the Preview button
if (window.getComputedStyle(wikiPreview).display === 'none') {
console.log('#wikiPreview is set to display: none');
const PreviewBtn = document.querySelector('#wpPreview');
if (PreviewBtn) {
console.log('Wikipedia Side-by-side Edit extension successfully loaded ');
PreviewBtn.addEventListener('click', sideBySideEdit);
} else {
console.log('Wikipedia Side-by-side Edit failed to load');
}
} else {
console.log('#wikiPreview is available');
sideBySideEdit();
}
return true;
})();