User:Gary Queen/layout.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
Documentation for this user script can be added at User:Gary Queen/layout. |
/*
hook
*/
addOnloadHook(accessKeys);
addOnloadHook(pagesLayout);
/*
useful functions
*/
function $(element)
{
return document.getElementById(element);
}
function addClass(element, newClass)
{
if (!element instanceof Object) return false;
if (element.className)
{
var classes = element.className.split(' ');
classes.push(newClass);
return element.className = classes.join(' ');
}
else return element.className = newClass;
}
function hasClass(element, classToCheck)
{
if (!element instanceof Object || !element.className) return false;
var classes = element.className.split(' ');
for (var i = 0; i < classes.length; i++)
{
if (classes[i] == classToCheck)
return true;
}
return false;
}
function removeClass(element, oldClass)
{
if (!element instanceof Object || !element.className) return false;
var classes = element.className.split(' ');
var newClasses = [];
for (var i = 0; i < classes.length; i++)
{
if (classes[i] != oldClass)
newClasses.push(classes[i]);
}
return element.className = newClasses;
}
String.prototype.trim = function()
{
return this.replace(/^[\s|\n]+|[\s|\n]+$/g, '');
}
String.prototype.ltrim = function()
{
return this.replace(/^[\s|\n]+/, '');
}
String.prototype.rtrim = function()
{
return this.replace(/[\s|\n]+$/, '');
}
/*
do access keys
*/
function accessKeys()
{
var caEdit = $('ca-edit');
var tPrint = $('t-print');
var content = $('content');
/*
DISABLE ACCESS KEYS
*/
// Disable logo
$('p-logo').childNodes[1].accessKey = null;
// disable access keys, so quickedit can use them
if (wgAction == 'view' || wgAction == 'purge')
{
if (caEdit) caEdit.firstChild.accessKey = null; // edit this page
if (tPrint) tPrint.firstChild.accessKey = null; // printable version
}
/*
ENABLE ACCESS KEYS
*/
// viewing a non-existent page
if (caEdit && caEdit.firstChild && !caEdit.firstChild.accessKey && caEdit.firstChild.firstChild.nodeValue == 'Create this page')
caEdit.firstChild.accessKey = 'e';
// Diff navigation links
// check if we're viewing an oldid page
var revisionNav = $('mw-revision-nav');
var mwPrevlink = document.getElementsByClassName('mw-prevlink');
var mwNextlink = document.getElementsByClassName('mw-nextlink');
var prevlink, nextlink;
if (revisionNav)
{
if (revisionNav.childNodes[1].firstChild.nodeValue == 'diff')
{
// have both prev and next
prevlink = revisionNav.childNodes[1];
nextlink = revisionNav.childNodes[11];
}
else // only have next
nextlink = revisionNav.childNodes[7];
}
else if (mwPrevlink.length > 0 || mwNextlink.length > 0)
{
prevlink = mwNextlink[0];
nextlink = mwPrevlink[0];
}
else
{
prevlink = $('differences-prevlink');
nextlink = $('differences-nextlink');
}
// assign previous link
if (prevlink && !$('wpPreview'))
{
if (tPrint) tPrint.firstChild.accessKey = null;
prevlink.accessKey = 'p';
}
// assign next link
if (nextlink)
{
$('pt-mytalk').firstChild.accessKey = null;
nextlink.accessKey = 'n';
}
// Content accesskey
var book = $('ca-nstab-book');
var portal = $('ca-nstab-portal');
var special = $('ca-nstab-special');
var contentTab;
if (book) contentTab = book;
else if (portal) contentTab = portal;
else if (special) contentTab = special;
else contentTab = '';
if (contentTab) contentTab.firstChild.accessKey = 'c';
// QuickEdit link
var contentSub = $('contentSub');
if (contentSub.innerHTML != '' && !special)
{
var qeAccessKey = 'b';
var qeLeadLink = $('sectionlink-0');
if (qeLeadLink) qeLeadLink.accessKey = qeAccessKey;
console.log('1: ' + qeLeadLink);
}
else if ($('t-find-edit'))
{
$('t-find-edit').firstChild.setAttribute('accesskey', 'b');
console.log('2');
}
// creating new page, from search results page
var newLinks = content.getElementsByClassName('new');
if (wgPageName == 'Special:Search' && newLinks[0])
newLinks[0].accessKey = 'e';
// Sidebar links
var drafts = $('t-drafts');
var goals = $('t-goals');
var pageSize = $('t-page-size');
if (drafts) drafts.firstChild.setAttribute('accesskey', 'd'); // Drafts
if (goals) goals.firstChild.setAttribute('accesskey', 'g'); // Goals
// if (pageSize) pageSize.firstChild.setAttribute('accesskey', 'a'); // Page Size
// access key for edit box on uneditable page
var permissionErrors = content.getElementsByClassName('permissions-errors');
if (permissionErrors.length > 0) $('wpTextbox1').accessKey = ',';
}
/*
do page layout
*/
function pagesLayout()
{
/*
variables
*/
var content = $('content');
var jumpToNav = $('jump-to-nav');
var pPersonal = $('p-personal');
var afterJumpToNav = jumpToNav.nextSibling.nextSibling.nextSibling.nextSibling;
var bodyContent = $('bodyContent');
var cactions = $('p-cactions');
var caEdit = $('ca-edit');
var caMain = $('ca-nstab-main');
var contentSub = $('contentSub');
var firstDiffElement = content.getElementsByClassName('diff')[0];
var globalWrapper = $('globalWrapper');
var h2 = content.getElementsByTagName('h2');
var paragraphs = content.getElementsByTagName('p');
var pBody = pPersonal.getElementsByClassName('pBody')[0];
var pendingChanges = $('mw-fr-revisiontag');
var relLinks = content.getElementsByClassName('rellink');
var section0 = $('section-0');
var siteSub = $('siteSub');
var toc = $('toc');
var tPrint = $('t-print');
var userMessages = content.getElementsByClassName('usermessage');
var wikiPreview = $('wikiPreview');
var wikitables = content.getElementsByClassName('wikitable');
/*
Layout
*/
// Fatter pages except when it would exceed page width
if (window.innerWidth > 1425) globalWrapper.style.width = cactions.style.width = pBody.style.width = '1400px';
else globalWrapper.style.width = cactions.style.width = pBody.style.width = (window.innerWidth - 25) + 'px';
// Thinner page width for articles (1000 pixels wide)
var thinnerPage = false;
var fatterPage = false;
// TODO fatterPages
// fatterPageTerms
if (typeof(fatterPageTerms) == 'object' && fatterPageTerms.length > 0)
{
for (var i = 0; i < fatterPageTerms.length; i++)
{
if (wgPageName.replace(/_/g, ' ').indexOf(fatterPageTerms[i]) != -1)
{
fatterPage = true;
break;
}
}
}
// thinnerPages
if (typeof(thinnerPages) == 'object' && thinnerPages.length > 0)
{
for (var i = 0; i < thinnerPages.length; i++)
{
if (wgPageName.indexOf(thinnerPages[i].replace(/ /g, '_')) == 0)
{
thinnerPage = true;
break;
}
}
}
// TODO thinnerPageTerms
// Thinner pages for articles
if (window.innerWidth > 1025 && fatterPage == false && (wgCanonicalNamespace == '' || thinnerPage) && (wgAction == 'view' || wgAction == 'submit' || wgAction == 'edit' || wgAction == 'purge') && (location.href.indexOf('title=') && location.href.indexOf('diff=')) == -1 && !(wgCanonicalNamespace == '' && wgTitle == wgMainPageTitle))
globalWrapper.style.width = cactions.style.width = pBody.style.width = '1000px';
// Forced page to use article styles
if ($('thinner-page'))
{
globalWrapper.style.width = cactions.style.width = pBody.style.width = '1000px';
appendCSS('#bodyContent > p, #wikiPreview > p, .text-indent { text-indent: 2em !important; }');
}
// Shorter search text (searchGoButton, mw-searchButton)
$('searchGoButton').value = 'G';
$('mw-searchButton').value = 'S';
// remove the extra space after editsections
var editSections = document.getElementsByClassName('editsection');
var nextSibling;
for (var i = 0; i < editSections.length; i++)
{
nextSibling = editSections[i].nextSibling;
if (nextSibling && nextSibling.nodeType == 3 && nextSibling.nodeValue == ' ')
nextSibling.parentNode.removeChild(nextSibling);
}
// adjust references
var references = document.getElementsByClassName('references-small');
for (var i = 0; i < references.length; i++)
{
var ref = references[i];
var colCount = parseInt(ref.style.MozColumnCount || 1);
var colWidth = ref.style.MozColumnWidth || '30em';
if (colCount == 2 || (colWidth.substr(colWidth.length - 2, 2) == 'em' && parseInt(colWidth) >= 30)) ref.style.MozColumnCount = ref.style.MozColumnWidth = 'auto';
if (ref.scrollHeight > 250)
{
addClass(ref, 'grey-border');
ref.style.clear = 'both';
}
// Indicate how many references there are in the box.
var numberOfReferences = Math.floor(ref.childNodes[1].childNodes[1].childNodes.length / 2);
var referencesHeading = ref.previousSibling.previousSibling;
if (referencesHeading.nodeName == 'H2')
{
var referencesHeadline = referencesHeading.getElementsByClassName('mw-headline')[0];
var newReferencesNode = document.createElement('span');
newReferencesNode.style.fontSize = '75%';
newReferencesNode.style.color = 'grey';
newReferencesNode.appendChild(document.createTextNode(' (' + numberOfReferences + ' total)'));
referencesHeadline.appendChild(newReferencesNode);
}
}
// changes for non-discussion and discussion pages; covers discussion pages not in a "talk:" namespace, such as many noticeboards
isDiscussionPage = ($('ca-addsection'));
// don't add fancy text changes on pages with short paragraphs (discussions), AND better separate discussions
if (isDiscussionPage)
{
importStylesheet('User:Gary King/short paragraphs.css');
importStylesheet('User:Gary King/discussions.css');
}
// insert clear: right; after h3, h4, h5
function addClears(elements)
{
for (var i = 0; i < elements.length; i++)
{
h = elements[i];
if (!h.nextSibling) continue;
div = document.createElement('div');
addClass(div, 'clear-right');
h.parentNode.insertBefore(div, h.nextSibling);
}
}
h3 = content.getElementsByTagName('h3');
h4 = content.getElementsByTagName('h4');
h5 = content.getElementsByTagName('h5');
/*addClears(h3);
addClears(h4);
addClears(h5);*/
// adjust top icons distance from right
var topIcons = document.getElementsByClassName('topicon');
var numberOfTopIcons = topIcons.length;
function getRightDist(element)
{
return getLengthInPixels(element.style.right)[0];
}
// loop through top icons and get the one with the largest style.right value
var leftMostTopIconRightValue = 0;
for (var i = 0; i < numberOfTopIcons; i++)
{
var rightDist = getRightDist(topIcons[i])
if (rightDist > leftMostTopIconRightValue)
leftMostTopIconRightValue = rightDist;
}
var trumpIconLength = getTrumpIconLength(), distanceRight;
if (trumpIconLength) distanceRight = trumpIconLength;
else if (leftMostTopIconRightValue) distanceRight = leftMostTopIconRightValue + 25;
else distanceRight = 0;
function getTrumpIconLength()
{
var trumpIcons = { 'status-top': 175, 'TemplateUserinfo': 225 };
var trumpIconLength = 0;
for (var icon in trumpIcons)
{
if ($(icon))
{
var trumpIconLength = trumpIcons[icon];
break;
}
}
return trumpIconLength;
}
/*var distanceRight = 0;
// ordered from left to right
var firstIcon = $('protected-icon') || $('status-top');
var secondIcon = $('spoken-icon');
var thirdIcon = $('commons-icon') || $('featured-star') || $('good-star') || $('rollback-icon') || $('script-icon');
var trumpIcons = { 'status-top': 150, 'TemplateUserinfo': 225 };
var trumpIconLength = getTrumpIconLength();
if (trumpIconLength) distanceRight = trumpIconLength;
else if (firstIcon) distanceRight = getRightDist(firstIcon) + 25;
else if (secondIcon) distanceRight = getRightDist(secondIcon) + 25;
else if (thirdIcon) distanceRight = getRightDist(thirdIcon) + 30;*/
// move QuickEdit section-0 link to top-right corner of page
sectionLink0 = $('sectionlink-0');
// QE is adding an edit link to the lead section, so move it to the corner
if (sectionLink0)
{
link = sectionLink0.parentNode;
link.id = 'editsection-0';
addClass(link, 'lead-qe-link');
link.style.marginRight = distanceRight + 'px';
// add new edit link into QE edit link
newLink = document.createElement('a');
newLink.href = wgScript + '?title=' + encodeURIComponent(mw.config.get('wgPageName')) + '&action=edit§ion=0';
newLink.title = 'Edit section';
newLink.appendChild(document.createTextNode('edit'));
link.insertBefore(newLink, sectionLink0);
link.insertBefore(document.createTextNode('/'), sectionLink0);
// move it
content.insertBefore(link, section0);
}
// better padding for boxes aligned to the left and right
toccolours = content.getElementsByClassName('toccolours');
for (var i = 0; i < toccolours.length; i++)
{
tocColour = toccolours[i];
textSideMargin = '1.5em';
if (tocColour.style.cssFloat == 'right')
{
tocColour.style.marginRight = 0;
tocColour.style.marginLeft = textSideMargin;
}
else if (tocColour.style.cssFloat == 'left')
{
tocColour.style.marginLeft = 0;
tocColour.style.marginRight = textSideMargin;
}
}
// don't indent lines in certain cases
// italicized lines that are not indented and therefore look like hatnotes
for (var i = 0; i < paragraphs.length; i++)
{
p = paragraphs[i];
if (p.parentNode != bodyContent && p.parentNode != wikiPreview) continue;
if (p.childNodes.length == 1 && p.firstChild.nodeName == 'I' && p.previousSibling.previousSibling.previousSibling.previousSibling != jumpToNav) p.style.textIndent = 0;
}
// merge multiple hatnotes together
// TODO Make these work together? i.e. if a dablink is followed by a reflink, merge them anyway.
function mergeLinks(className)
{
links = content.getElementsByClassName(className);
for (var i = links.length - 1; i >= 0; i--)
{
l = links[i];
// give "title" attribute to node
l.title = className;
if (!l.nextSibling || !l.nextSibling.nextSibling || !hasClass(l.nextSibling.nextSibling, className)) continue;
nextEl = l.nextSibling.nextSibling;
addClass(nextEl, 'merged-hatnote');
text = document.createTextNode(className == 'dablink' ? ' ' : '. ');
l.appendChild(text);
l.appendChild(nextEl);
}
}
mergeLinks('dablink');
mergeLinks('rellink');
// contentSub (redirects, contribution page user info, etc.)
/*if (contentSub.firstChild)
{
if (pendingChanges)
{
newPCDiv = document.createElement('div');
addClass(newPCDiv, 'contentSub');
newPCDiv.style.display = 'block';
newPCDiv.appendChild(pendingChanges);
contentSub.parentNode.insertBefore(newPCDiv, jumpToNav);
}
addClass(contentSub, 'merged-content-sub');
if (contentSub.firstChild.nodeType == 3) contentSub.firstChild.nodeValue = contentSub.firstChild.nodeValue.replace(/^\s+/g, '');
newDiv = document.createElement('div');
newDiv.style.display = 'block';
addClass(newDiv, 'contentSub');
contentSub.parentNode.insertBefore(newDiv, contentSub.nextSibling);
}
else
addClass(contentSub, 'contentSub');*/
// move a left-aligned thumb image to before any header that immediately precedes it
leftAlignedThumb = 'tleft';
thumbs = content.getElementsByClassName(leftAlignedThumb);
for (var i = 0; i < thumbs.length; i++)
{
t = thumbs[i];
movedText = 'This left-aligned image thumb was moved from the section below to the section above, in accordance with the Manual of Style (WP:MOS).';
// immediately precedes it
if (!t.previousSibling && !t.previousSibling.previousSibling) continue;
prev = t.previousSibling.previousSibling;
if (prev.nodeName == 'H3' || prev.nodeName == 'H4' || prev.nodeName == 'H5')
{
t.parentNode.insertBefore(t, prev);
t.title = movedText;
}
// preceded by a rellink, then precedes it
if (!prev.previousSibling && !prev.previousSibling.previousSibling && !hasClass(prev.previousSibling.previousSibling, 'rellink')) continue;
prev = prev.previousSibling.previousSibling;
if (prev && (prev.nodeName == 'H3' || prev.nodeName == 'H4' || prev.nodeName == 'H5'))
{
t.parentNode.insertBefore(t, prev);
t.title = movedText;
}
}
// indent rellinks if an image is to the left of it
for (var i = 0; i < relLinks.length; i++)
{
l = relLinks[i];
if (!l.previousSibling || !l.previousSibling.previousSibling) continue;
two = l.previousSibling.previousSibling;
if (!two.previousSibling) continue;
three = two.previousSibling;
if (!three.previousSibling) continue;
four = three.previousSibling;
if ((two && hasClass(two, leftAlignedThumb)) || (three && hasClass(three, leftAlignedThumb)) || (four && hasClass(four, leftAlignedThumb))) addClass(l, 'text-indent');
}
// get length in pixels
function getLengthInPixels(length, emInPixels)
{
// 1em ~ 13px
if (typeof(emInPixels) == 'undefined') emInPixels = 13;
var value = parseInt(length);
var type = length.substring(length.length - 2, length.length);
if (type == 'em')
{
value = value * emInPixels;
type = 'px';
}
return [value, type];
}
// proper padding for left- and right-aligned tables
function checkAndFixTableMargins(table)
{
var alignment, result;
// calculate "alignment"
if (wikitables[i].align) alignment = wikitables[i].align;
else if (wikitables[i].style.cssFloat) alignment = wikitables[i].style.cssFloat;
if (alignment != 'left' && alignment != 'right') return false;
var margin = alignment == 'left' ? table.style.marginRight : table.style.marginLeft;
var pixelLengths = getLengthInPixels(margin)
var value = pixelLengths[0];
var type = pixelLengths[1];
if (type == 'px' && value < 13) result = '13px';
else if ((type == 'em' && value < 1) || !type) result = '1em';
else result = '1em'; // may need to add checks for more "type"s
if (alignment == 'left') table.style.marginRight = result;
else table.style.marginLeft = result;
return result;
}
for (var i = 0; i < wikitables.length; i++)
checkAndFixTableMargins(wikitables[i])
// add more space above .usermessage on Main Page
if (wgCanonicalNamespace == '' && wgTitle == wgMainPageTitle && userMessages.length > 0)
userMessages[0].style.margin = '2em 0 0 0';
// have no max width for non-standard TOCs
toctitle = $('toctitle');
if (!toctitle) appendCSS('.toc { max-width: none; }');
// add accesskeys for QE section links
for (var i = 1; i <= 9; i++)
{
link = $('sectionlink-' + i);
if (!link) break;
link.accessKey = i;
}
// number h2 headers
if (!(wgCanonicalNamespace == '' && wgTitle == wgMainPageTitle) && (wgAction == 'view' || wgAction == 'purge'))
{
if (firstDiffElement) start = 2;
else if (toc) start = 1;
else start = 0;
begin = 0;
for (var i = start; i < h2.length; i++)
{
number = document.createElement('span');
addClass(number, 'heading-number');
number.appendChild(document.createTextNode(begin + 1 + '. '));
h2[i].insertBefore(number, h2[i].firstChild);
begin++;
}
}
// remove diff whitespace, around .diff-deletedline and .diff-addedline
deletedLines = content.getElementsByClassName('diff-deletedline');
addedLines = content.getElementsByClassName('diff-addedline');
function trimDiffs(elements)
{
for (var i = 0; i < elements.length; i++)
{
if (!elements[i].firstChild) continue;
elements[i].firstChild.innerHTML = elements[i].firstChild.innerHTML.trim();
}
}
// FIXME Removes * and perhaps other characters at beginning of the line.
/*trimDiffs(deletedLines);
trimDiffs(addedLines);*/
// TODO Use white-space: pre-wrap; on deletedLines[i].firstChild and strip whtiespace added
// by the software so that added/removed whitespaces are more clear?
// remove extra line breaks - only in articles, for now at least
if (wgCanonicalNamespace == '')
{
// at the beginning of the page
next = afterJumpToNav;
if (hasClass(next, 'dablink') || next.nodeName == 'DL')
{
next2 = next.nextSibling.nextSibling;
next3 = next2.nextSibling.nextSibling;
if (next2.nodeName == 'P' && next2.firstChild.nodeName == 'BR')
next2.removeChild(next2.firstChild);
else if (next3.nodeName == 'P' && next3.firstChild.nodeName == 'BR')
next3.removeChild(next3.firstChild);
}
// before the TOC
if (toc && toc.previousSibling.previousSibling)
{
beforeTOC = toc.previousSibling.previousSibling;
if (beforeTOC.nodeName == 'P' && beforeTOC.childNodes.length == 1 && beforeTOC.firstChild.nodeName == 'BR')
beforeTOC.parentNode.removeChild(beforeTOC);
}
}
}