Editing.js – MediaWiki
Jump to navigation
Jump to search
Lou Montana (talk | contribs) m (same) |
Lou Montana (talk | contribs) m (Fix) |
||
(7 intermediate revisions by the same user not shown) | |||
Line 4: | Line 4: | ||
*****************************************************************************************/ | *****************************************************************************************/ | ||
(function() { // BEGIN WRAPPER | |||
/***************************************************************************************** | /***************************************************************************************** | ||
* Variables required by all functions below | * Variables required by all functions below | ||
*/ | */ | ||
var editingTextbox = document.getElementById("wpTextbox1"); | |||
/***************************************************************************************** | /***************************************************************************************** | ||
* Add checkbox toggle of allowance to use TAB key in editing textarea to insert TAB chars | * Add checkbox toggle of allowance to use TAB key in editing textarea to insert TAB chars | ||
* Maintainer: [[User:Fred Gandt]] | * Maintainer: [[User:Fred Gandt]] | ||
*/ | */ | ||
function allowTabs() { | |||
var ecb = document.getElementsByClassName("editCheckboxes")[0], | |||
cb = document.createElement("input"), | |||
cb_lbl = document.createElement("label"), | |||
tabsAllowed = function(evt) { | |||
if (evt.keyCode == 9) { | |||
evt.preventDefault(); | |||
var ss = editingTextbox.selectionStart; | |||
editingTextbox.value = editingTextbox.value.substr(0, ss) + String.fromCharCode(evt.keyCode) + editingTextbox.value.substr(editingTextbox.selectionEnd); | |||
editingTextbox.setSelectionRange(ss + 1, ss + 1); | |||
} | } | ||
} | |||
initTabsAllowed = function() { | |||
editingTextbox.addEventListener("keydown", tabsAllowed, false); | |||
editingTextbox.form.action += "&allowTabs"; | |||
}; | |||
cb.setAttribute("id", "allowTabs"); | |||
cb.setAttribute("type", "checkbox"); | |||
cb.setAttribute("style", "margin-left: 3em"); | |||
cb_lbl.setAttribute("for", "allowTabs"); | |||
cb_lbl.setAttribute("title", "Allows insertion of Tab characters when and where the Tab key is pressed"); | |||
cb_lbl.innerHTML = " Allow tabulations usage"; | |||
cb.addEventListener("change", function() { | |||
if (this.checked) { | |||
initTabsAllowed(); | initTabsAllowed(); | ||
} else { | |||
editingTextbox.removeEventListener("keydown", tabsAllowed, false); | |||
editingTextbox.form.action = editingTextbox.form.action.replace("&allowTabs", ""); | |||
} | } | ||
}, false); | |||
if (~window.location.search.indexOf("&allowTabs")) { | |||
cb.checked = true; | |||
initTabsAllowed(); | |||
} | } | ||
ecb.appendChild(cb); | |||
ecb.appendChild(cb_lbl); | |||
} | |||
/***************************************************************************************** | /***************************************************************************************** | ||
* Prevents page body scrolling when scrolling of the editing textarea reaches the top or bottom. | * Prevents page body scrolling when scrolling of the editing textarea reaches the top or bottom. | ||
* Maintainer: [[User:Fred Gandt]] | * Maintainer: [[User:Fred Gandt]] | ||
*/ | */ | ||
function holdStill() { | |||
editingTextbox.addEventListener("mousewheel", function(evt) { | |||
evt.preventDefault(); | |||
editingTextbox.scrollTop -= evt.wheelDelta / 3; | |||
}, false); | |||
} | |||
/***************************************************************************************** | /***************************************************************************************** | ||
* Add a checkbox to preview darkmode (?useskin=darkvector) | * Add a checkbox to preview darkmode (?useskin=darkvector) | ||
* Maintainer: [[User:Lou Montana]] | * Maintainer: [[User:Lou Montana]] | ||
*/ | */ | ||
function darkModePreview() { | |||
var ecb = document.getElementsByClassName("editCheckboxes")[0], | var ecb = document.getElementsByClassName("editCheckboxes")[0], | ||
cb = document.createElement("input"), | cb = document.createElement("input"), | ||
Line 88: | Line 81: | ||
cb.setAttribute("id", "previewDarkMode"); | cb.setAttribute("id", "previewDarkMode"); | ||
cb.setAttribute("type", "checkbox"); | cb.setAttribute("type", "checkbox"); | ||
cb.setAttribute("style", "margin-left: 3em | cb.setAttribute("style", "margin-left: 3em"); | ||
label.setAttribute("for", "previewDarkMode"); | label.setAttribute("for", "previewDarkMode"); | ||
label.setAttribute("title", "Preview Dark Mode render (next preview)"); | label.setAttribute("title", "Preview Dark Mode render (next preview)"); | ||
Line 94: | Line 87: | ||
cb.addEventListener("change", function() { | cb.addEventListener("change", function() { | ||
if (this.checked) { | if (this.checked) { | ||
editingTextbox.form.action = | editingTextbox.form.action = editingTextbox.form.action.replace(actionLightAdd, "") | ||
editingTextbox.form.action += actionDarkAdd; | editingTextbox.form.action += actionDarkAdd; | ||
} else { | } else { | ||
editingTextbox.form.action = | editingTextbox.form.action = editingTextbox.form.action.replace(actionDarkAdd, ""); | ||
editingTextbox.form.action += actionLightAdd; | editingTextbox.form.action += actionLightAdd; | ||
} | } | ||
}, false); | }, false); | ||
cb.checked = document.getElementsByTagName("body")[0].classList.contains("skin-darkvector"); | |||
let event = new Event("change"); | |||
cb.dispatchEvent(event); | |||
ecb.appendChild(cb); | ecb.appendChild(cb); | ||
ecb.appendChild(label); | ecb.appendChild(label); | ||
} | } | ||
/***************************************************************************************** | /***************************************************************************************** | ||
* | * Allow moving source code textarea's selected line(s) with Alt+Up / Alt+Down | ||
* Allow duplicating source code textarea's selected line(s) with Alt+Shift+Up / Alt+Shift+Down | |||
* Allow duplicating code textarea's current line/selection with Ctrl+D | |||
* Maintainer: [[User:Lou Montana]] | |||
*/ | */ | ||
function allowLineMoveAndDuplication() { | |||
// | |||
editingTextbox.addEventListener('keydown', (ev) => { | |||
/******************************* | |||
// duplicate line below | |||
if (ev.ctrlKey && !ev.altKey && !ev.shiftKey && (ev.key == 'd' || ev.key == 'D')) // Ctrl+D | |||
{ | |||
duplicateSelection(editingTextbox); | |||
ev.preventDefault(); | |||
return; | |||
} | |||
if (ev.key != 'ArrowUp' && ev.key != 'ArrowDown') | |||
return; | |||
let up = ev.key == 'ArrowUp'; | |||
// move line(s) up/down | |||
if (!ev.ctrlKey && ev.altKey && !ev.shiftKey) // Alt up/down | |||
{ | |||
moveLines(editingTextbox, up); | |||
return; | |||
} | |||
// duplicate line(s) up/down | |||
if (!ev.ctrlKey && ev.altKey && ev.shiftKey) // Alt+Shift+Up/Down | |||
{ | |||
duplicateLines(editingTextbox, up); | |||
return; | |||
} | |||
}); | |||
} | |||
/** | |||
* | |||
* @param {HTMLTextAreaElement} editingTextbox | |||
* @param {bool} moveUp | |||
*/ | |||
function moveLines(editingTextbox, moveUp) { | |||
let lines = editingTextbox.value.split('\n'); | |||
let linesCount = lines.length; | |||
if (linesCount < 1) | |||
return; | |||
let [firstHighlightedLine, lastHighlightedLine] = getHighlightedLines(lines, editingTextbox.selectionStart, editingTextbox.selectionEnd); | |||
if ((moveUp && firstHighlightedLine == 0) || (!moveUp && lastHighlightedLine == linesCount - 1)) | |||
return; | |||
let selectionStart = editingTextbox.selectionStart; | |||
let selectionEnd = editingTextbox.selectionEnd; | |||
if (moveUp) | |||
{ | |||
let movedLine = lines[firstHighlightedLine - 1]; | |||
lines.splice(firstHighlightedLine - 1, 1); | |||
lines.splice(lastHighlightedLine, 0, movedLine); | |||
selectionStart -= movedLine.length + 1; | |||
selectionEnd -= movedLine.length + 1; | |||
} | |||
else | |||
{ | |||
let movedLine = lines[lastHighlightedLine + 1]; | |||
lines.splice(lastHighlightedLine + 1, 1); | |||
lines.splice(firstHighlightedLine, 0, movedLine); | |||
selectionStart += movedLine.length + 1; | |||
selectionEnd += movedLine.length + 1; | |||
} | |||
setTextAreaValue(editingTextbox, lines, selectionStart, selectionEnd); | |||
} | |||
/** | |||
* triggered by Ctrl+D | |||
* @param {HTMLTextAreaElement} editingTextbox | |||
*/ | |||
function duplicateSelection(editingTextbox) { | |||
let originalValue = editingTextbox.value; | |||
// no selection; duplicate current line below | |||
if (editingTextbox.selectionStart == editingTextbox.selectionEnd) | |||
{ | |||
let lines = originalValue.split('\n'); | |||
let [firstHighlightedLine] = getHighlightedLines(lines, editingTextbox.selectionStart, editingTextbox.selectionEnd); | |||
lines.splice(firstHighlightedLine, 0, lines[firstHighlightedLine]); | |||
setTextAreaValue(editingTextbox, lines); | |||
return; | |||
} | |||
// selection that should simply be inserted after the selection | |||
let selection = originalValue.substring(editingTextbox.selectionStart, editingTextbox.selectionEnd); | |||
originalValue = originalValue.slice(0, editingTextbox.selectionEnd) + selection + originalValue.slice(editingTextbox.selectionEnd); | |||
let lines = originalValue.split('\n'); | |||
setTextAreaValue(editingTextbox, lines); | |||
} | |||
/** | |||
* @param {HTMLTextAreaElement} editingTextbox | |||
* @param {bool} dupeUp | |||
*/ | |||
function duplicateLines(editingTextbox, dupeUp) { | |||
let lines = editingTextbox.value.split('\n'); | |||
let linesCount = lines.length; | |||
let [firstHighlightedLine, lastHighlightedLine] = getHighlightedLines(lines, editingTextbox.selectionStart, editingTextbox.selectionEnd); | |||
if ((dupeUp && firstHighlightedLine == 0) || (!dupeUp && lastHighlightedLine == linesCount - 1)) | |||
return; | |||
let selectionStart = editingTextbox.selectionStart; | |||
let selectionEnd = editingTextbox.selectionEnd; | |||
if (dupeUp) | |||
{ | |||
let selectionOffset = 0; | |||
for (let i = firstHighlightedLine; i < lastHighlightedLine + 1; ++i) | |||
{ | |||
selectionOffset += lines[i].length + 1; | |||
lines.splice(firstHighlightedLine, 0, lines[i]); | |||
} | |||
selectionStart += selectionOffset; | |||
selectionEnd += selectionOffset; | |||
} | |||
else | |||
{ | |||
for (let i = lastHighlightedLine; i >= firstHighlightedLine; --i) | |||
{ | |||
lines.splice(lastHighlightedLine + 1, 0, lines[i]); | |||
} | |||
} | |||
setTextAreaValue(editingTextbox, lines, selectionStart, selectionEnd); | |||
} | |||
/** | |||
* | |||
* @param {HTMLTextAreaElement} editingTextbox | |||
* @param {array} lines | |||
* @returns {array} [firstHighlightedLine, lastHighlightedLine, selectionStartColumn, selectionEndColumn] | |||
*/ | |||
function getHighlightedLines(lines, selectionStart, selectionEnd) { | |||
let linesCount = lines.length; | |||
let firstHighlightedLine = -1; | |||
let lastHighlightedLine = -1; | |||
let selectionStartColumn = -1; | |||
let selectionEndColumn = -1; | |||
let calcLength = 0; | |||
let lineLength; | |||
for (let i = 0; i < linesCount; ++i) { | |||
lineLength = lines[i].length; | |||
if (selectionStartColumn == -1 && selectionStart <= calcLength + lineLength) { | |||
firstHighlightedLine = i; | |||
selectionStartColumn = selectionStart - calcLength; | |||
} | |||
if (selectionEndColumn == -1 && selectionEnd <= calcLength + lineLength) { | |||
lastHighlightedLine = i; | |||
selectionEndColumn = selectionStart - calcLength; | |||
} | |||
if (selectionEndColumn != -1 && selectionEndColumn != -1) | |||
break; | |||
calcLength += lineLength + 1; // "\n" | |||
} | |||
if (selectionStartColumn == -1 || selectionEndColumn == -1) | |||
return; | |||
if (firstHighlightedLine < lastHighlightedLine) | |||
return [firstHighlightedLine, lastHighlightedLine, selectionStartColumn, selectionEndColumn]; | |||
else | |||
return [lastHighlightedLine, firstHighlightedLine, selectionStartColumn, selectionEndColumn]; | |||
} | |||
/** | |||
* | |||
* @param {HTMLTextAreaElement} editingTextbox | |||
* @param {Array} lines | |||
* @param {number} selectionStart | |||
* @param {number} selectionEnd | |||
*/ | |||
function setTextAreaValue(editingTextbox, lines, selectionStart, selectionEnd) { | |||
if (!selectionStart && selectionStart !== 0) | |||
selectionStart = editingTextbox.selectionStart; | |||
if (!selectionEnd && selectionEnd !== 0) | |||
selectionEnd = editingTextbox.selectionEnd; | |||
// editingTextbox.value = lines.join('\n'); | |||
editingTextbox.select(); | |||
document.execCommand('insertText', false, lines.join('\n')); | |||
editingTextbox.selectionStart = selectionStart; | |||
editingTextbox.selectionEnd = selectionEnd; | |||
} | |||
/***************************************************************************************** | /***************************************************************************************** | ||
* Call the required functions from those above | * Call the required functions from those above | ||
*/ | */ | ||
allowTabs(); | |||
darkModePreview(); | |||
allowLineMoveAndDuplication(); | |||
}()); // END WRAPPER | |||
Revision as of 12:28, 14 June 2024
/*****************************************************************************************
* JavaScript here will be loaded by MediaWiki:Common.js when "editing mode" is detected.
*
*****************************************************************************************/
(function() { // BEGIN WRAPPER
/*****************************************************************************************
* Variables required by all functions below
*/
var editingTextbox = document.getElementById("wpTextbox1");
/*****************************************************************************************
* Add checkbox toggle of allowance to use TAB key in editing textarea to insert TAB chars
* Maintainer: [[User:Fred Gandt]]
*/
function allowTabs() {
var ecb = document.getElementsByClassName("editCheckboxes")[0],
cb = document.createElement("input"),
cb_lbl = document.createElement("label"),
tabsAllowed = function(evt) {
if (evt.keyCode == 9) {
evt.preventDefault();
var ss = editingTextbox.selectionStart;
editingTextbox.value = editingTextbox.value.substr(0, ss) + String.fromCharCode(evt.keyCode) + editingTextbox.value.substr(editingTextbox.selectionEnd);
editingTextbox.setSelectionRange(ss + 1, ss + 1);
}
}
initTabsAllowed = function() {
editingTextbox.addEventListener("keydown", tabsAllowed, false);
editingTextbox.form.action += "&allowTabs";
};
cb.setAttribute("id", "allowTabs");
cb.setAttribute("type", "checkbox");
cb.setAttribute("style", "margin-left: 3em");
cb_lbl.setAttribute("for", "allowTabs");
cb_lbl.setAttribute("title", "Allows insertion of Tab characters when and where the Tab key is pressed");
cb_lbl.innerHTML = " Allow tabulations usage";
cb.addEventListener("change", function() {
if (this.checked) {
initTabsAllowed();
} else {
editingTextbox.removeEventListener("keydown", tabsAllowed, false);
editingTextbox.form.action = editingTextbox.form.action.replace("&allowTabs", "");
}
}, false);
if (~window.location.search.indexOf("&allowTabs")) {
cb.checked = true;
initTabsAllowed();
}
ecb.appendChild(cb);
ecb.appendChild(cb_lbl);
}
/*****************************************************************************************
* Prevents page body scrolling when scrolling of the editing textarea reaches the top or bottom.
* Maintainer: [[User:Fred Gandt]]
*/
function holdStill() {
editingTextbox.addEventListener("mousewheel", function(evt) {
evt.preventDefault();
editingTextbox.scrollTop -= evt.wheelDelta / 3;
}, false);
}
/*****************************************************************************************
* Add a checkbox to preview darkmode (?useskin=darkvector)
* Maintainer: [[User:Lou Montana]]
*/
function darkModePreview() {
var ecb = document.getElementsByClassName("editCheckboxes")[0],
cb = document.createElement("input"),
label = document.createElement("label"),
actionDarkAdd = "&useskin=darkvector";
actionLightAdd = "&useskin=vector";
cb.setAttribute("id", "previewDarkMode");
cb.setAttribute("type", "checkbox");
cb.setAttribute("style", "margin-left: 3em");
label.setAttribute("for", "previewDarkMode");
label.setAttribute("title", "Preview Dark Mode render (next preview)");
label.innerHTML = " Preview Dark Mode";
cb.addEventListener("change", function() {
if (this.checked) {
editingTextbox.form.action = editingTextbox.form.action.replace(actionLightAdd, "")
editingTextbox.form.action += actionDarkAdd;
} else {
editingTextbox.form.action = editingTextbox.form.action.replace(actionDarkAdd, "");
editingTextbox.form.action += actionLightAdd;
}
}, false);
cb.checked = document.getElementsByTagName("body")[0].classList.contains("skin-darkvector");
let event = new Event("change");
cb.dispatchEvent(event);
ecb.appendChild(cb);
ecb.appendChild(label);
}
/*****************************************************************************************
* Allow moving source code textarea's selected line(s) with Alt+Up / Alt+Down
* Allow duplicating source code textarea's selected line(s) with Alt+Shift+Up / Alt+Shift+Down
* Allow duplicating code textarea's current line/selection with Ctrl+D
* Maintainer: [[User:Lou Montana]]
*/
function allowLineMoveAndDuplication() {
editingTextbox.addEventListener('keydown', (ev) => {
// duplicate line below
if (ev.ctrlKey && !ev.altKey && !ev.shiftKey && (ev.key == 'd' || ev.key == 'D')) // Ctrl+D
{
duplicateSelection(editingTextbox);
ev.preventDefault();
return;
}
if (ev.key != 'ArrowUp' && ev.key != 'ArrowDown')
return;
let up = ev.key == 'ArrowUp';
// move line(s) up/down
if (!ev.ctrlKey && ev.altKey && !ev.shiftKey) // Alt up/down
{
moveLines(editingTextbox, up);
return;
}
// duplicate line(s) up/down
if (!ev.ctrlKey && ev.altKey && ev.shiftKey) // Alt+Shift+Up/Down
{
duplicateLines(editingTextbox, up);
return;
}
});
}
/**
*
* @param {HTMLTextAreaElement} editingTextbox
* @param {bool} moveUp
*/
function moveLines(editingTextbox, moveUp) {
let lines = editingTextbox.value.split('\n');
let linesCount = lines.length;
if (linesCount < 1)
return;
let [firstHighlightedLine, lastHighlightedLine] = getHighlightedLines(lines, editingTextbox.selectionStart, editingTextbox.selectionEnd);
if ((moveUp && firstHighlightedLine == 0) || (!moveUp && lastHighlightedLine == linesCount - 1))
return;
let selectionStart = editingTextbox.selectionStart;
let selectionEnd = editingTextbox.selectionEnd;
if (moveUp)
{
let movedLine = lines[firstHighlightedLine - 1];
lines.splice(firstHighlightedLine - 1, 1);
lines.splice(lastHighlightedLine, 0, movedLine);
selectionStart -= movedLine.length + 1;
selectionEnd -= movedLine.length + 1;
}
else
{
let movedLine = lines[lastHighlightedLine + 1];
lines.splice(lastHighlightedLine + 1, 1);
lines.splice(firstHighlightedLine, 0, movedLine);
selectionStart += movedLine.length + 1;
selectionEnd += movedLine.length + 1;
}
setTextAreaValue(editingTextbox, lines, selectionStart, selectionEnd);
}
/**
* triggered by Ctrl+D
* @param {HTMLTextAreaElement} editingTextbox
*/
function duplicateSelection(editingTextbox) {
let originalValue = editingTextbox.value;
// no selection; duplicate current line below
if (editingTextbox.selectionStart == editingTextbox.selectionEnd)
{
let lines = originalValue.split('\n');
let [firstHighlightedLine] = getHighlightedLines(lines, editingTextbox.selectionStart, editingTextbox.selectionEnd);
lines.splice(firstHighlightedLine, 0, lines[firstHighlightedLine]);
setTextAreaValue(editingTextbox, lines);
return;
}
// selection that should simply be inserted after the selection
let selection = originalValue.substring(editingTextbox.selectionStart, editingTextbox.selectionEnd);
originalValue = originalValue.slice(0, editingTextbox.selectionEnd) + selection + originalValue.slice(editingTextbox.selectionEnd);
let lines = originalValue.split('\n');
setTextAreaValue(editingTextbox, lines);
}
/**
* @param {HTMLTextAreaElement} editingTextbox
* @param {bool} dupeUp
*/
function duplicateLines(editingTextbox, dupeUp) {
let lines = editingTextbox.value.split('\n');
let linesCount = lines.length;
let [firstHighlightedLine, lastHighlightedLine] = getHighlightedLines(lines, editingTextbox.selectionStart, editingTextbox.selectionEnd);
if ((dupeUp && firstHighlightedLine == 0) || (!dupeUp && lastHighlightedLine == linesCount - 1))
return;
let selectionStart = editingTextbox.selectionStart;
let selectionEnd = editingTextbox.selectionEnd;
if (dupeUp)
{
let selectionOffset = 0;
for (let i = firstHighlightedLine; i < lastHighlightedLine + 1; ++i)
{
selectionOffset += lines[i].length + 1;
lines.splice(firstHighlightedLine, 0, lines[i]);
}
selectionStart += selectionOffset;
selectionEnd += selectionOffset;
}
else
{
for (let i = lastHighlightedLine; i >= firstHighlightedLine; --i)
{
lines.splice(lastHighlightedLine + 1, 0, lines[i]);
}
}
setTextAreaValue(editingTextbox, lines, selectionStart, selectionEnd);
}
/**
*
* @param {HTMLTextAreaElement} editingTextbox
* @param {array} lines
* @returns {array} [firstHighlightedLine, lastHighlightedLine, selectionStartColumn, selectionEndColumn]
*/
function getHighlightedLines(lines, selectionStart, selectionEnd) {
let linesCount = lines.length;
let firstHighlightedLine = -1;
let lastHighlightedLine = -1;
let selectionStartColumn = -1;
let selectionEndColumn = -1;
let calcLength = 0;
let lineLength;
for (let i = 0; i < linesCount; ++i) {
lineLength = lines[i].length;
if (selectionStartColumn == -1 && selectionStart <= calcLength + lineLength) {
firstHighlightedLine = i;
selectionStartColumn = selectionStart - calcLength;
}
if (selectionEndColumn == -1 && selectionEnd <= calcLength + lineLength) {
lastHighlightedLine = i;
selectionEndColumn = selectionStart - calcLength;
}
if (selectionEndColumn != -1 && selectionEndColumn != -1)
break;
calcLength += lineLength + 1; // "\n"
}
if (selectionStartColumn == -1 || selectionEndColumn == -1)
return;
if (firstHighlightedLine < lastHighlightedLine)
return [firstHighlightedLine, lastHighlightedLine, selectionStartColumn, selectionEndColumn];
else
return [lastHighlightedLine, firstHighlightedLine, selectionStartColumn, selectionEndColumn];
}
/**
*
* @param {HTMLTextAreaElement} editingTextbox
* @param {Array} lines
* @param {number} selectionStart
* @param {number} selectionEnd
*/
function setTextAreaValue(editingTextbox, lines, selectionStart, selectionEnd) {
if (!selectionStart && selectionStart !== 0)
selectionStart = editingTextbox.selectionStart;
if (!selectionEnd && selectionEnd !== 0)
selectionEnd = editingTextbox.selectionEnd;
// editingTextbox.value = lines.join('\n');
editingTextbox.select();
document.execCommand('insertText', false, lines.join('\n'));
editingTextbox.selectionStart = selectionStart;
editingTextbox.selectionEnd = selectionEnd;
}
/*****************************************************************************************
* Call the required functions from those above
*/
allowTabs();
darkModePreview();
allowLineMoveAndDuplication();
}()); // END WRAPPER