User:Nux/replylinks.js
Appearance
< User:Nux
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:Nux/replylinks. |
/* global ve */
/* eslint-disable indent */
/**
@file Odpowiedzi z linkami (Reply links with backtrack links)
Opis (pl):
- http://pl.wikipedia.org/wiki/Wikipedia:Narz%C4%99dzia/Odpowiedzi_z_linkami
Main functions:
- adding reply links near user links
- inserting text given in newsectionname (as PHP param in the location string of the page)
Copyright: ©2006-2021 Maciej Jaros (pl:User:Nux, en:User:EcceNux)
Licencja: GNU General Public License v2
http://opensource.org/licenses/gpl-license.php
@note Please keep MW 1.16 compatible (i.e. do not use mw.config directly)
@note jQuery is required though
@note Dev version: http://pl.wikipedia.org/wiki/Wikipedysta:Nux/replylinks.dev.js
@note Prod version: https://pl.wikipedia.org/wiki/MediaWiki:Gadget-replylinks.js
*/
/* global mw, $ */
/* -=-=-=-=-=-=-=-=-=-=-=-
Object init
-=-=-=-=-=-=-=-=-=-=-=- */
if (typeof(window.oRepLinks) != 'undefined')
{
throw ("oRepLinks already used");
}
var oRepLinks = {};
/* -=-=-=-=-=-=-=-=-=-=-=-
Version
-=-=-=-=-=-=-=-=-=-=-=- */
oRepLinks.version = oRepLinks.ver = '1.7.0';
/* -=-=-=-=-=-=-=-=-=-=-=-
Preferences
-=-=-=-=-=-=-=-=-=-=-=- */
// i18n
oRepLinks.i18n = {'':''
,'en' : {'':''
,'std prefix' : 'Re:' // standard prefix to a replay
,'no section prefix' : 'Ad:' // prefix shown when a section header was not found
,'reply link text' : 'reply'
}
,'pl' : {'':''
,'std prefix' : 'Odp:'
,'no section prefix' : 'Ad:'
,'reply link text' : 'odp'
}
};
// IP will be added to the end to create a working link
oRepLinks.hrefOnlineIPwhois = 'http://www.ripe.net/perl/whois?form_type=simple&searchtext=';
/* -=-=-=-=-=-=-=-=-=-=-=-
Gadget code
$G = oRepLinks
-=-=-=-=-=-=-=-=-=-=-=- */
(function($G){
/**
@brief get MediaWiki configuration variable
MW 1.16 and 1.17+ compatible
*/
$G.getMediaWikiConfig = function(variableName)
{
if (typeof(mw) === 'object' && 'config' in mw) {
return mw.config.get(variableName);
}
return window[variableName];
};
//
// i18n setup
//
$G.Lang = "en";
if ($G.getMediaWikiConfig('wgUserLanguage') in $G.i18n)
{
$G.Lang = $G.getMediaWikiConfig('wgUserLanguage');
}
$G.i18n = $G.i18n[$G.Lang];
/**
@brief get all alternative namespaces for given \a namespaceNumber.
@return array of namespace names (including alternative names)
*/
$G.getNamespaceNames = function(namespaceNumber, encodingFunction)
{
var found = [];
var namespacesIds = $G.getMediaWikiConfig('wgNamespaceIds');
for (var id in namespacesIds)
{
if (namespacesIds[id] == namespaceNumber)
{
if (encodingFunction)
{
id = encodingFunction(id);
}
found.push(id);
}
}
return found;
};
//
// Technical Settings
//
//! @warning avoid using catching parenthesis by adding "?:"
// 'http://.../wiki/User:';
$G.strReHrefBase = $G.getMediaWikiConfig('wgServer') + $G.getMediaWikiConfig('wgArticlePath').replace('$1', '(?:' + $G.getNamespaceNames(2, encodeURIComponent).join('|') + ')') + ':';
// 'http://.../w/index.php\\?title=User:';
$G.strReHrefNewBase = $G.getMediaWikiConfig('wgServer') + $G.getMediaWikiConfig('wgScript') + '\\?title=' + '(?:' + $G.getNamespaceNames(2, encodeURIComponent).join('|') + ')' + ':';
// 'http://.../wiki/Specjal:Contributions';
$G.strReHrefAnonimBase = $G.getMediaWikiConfig('wgServer') + $G.getMediaWikiConfig('wgArticlePath').replace('$1', encodeURIComponent($G.getMediaWikiConfig('wgFormattedNamespaces')[-1])) + ':(?:Contributions|Wk%C5%82ad)/';
// 'http://.../wiki/User_talk:';
$G.strBaseUserTalkURL = $G.getMediaWikiConfig('wgServer') + $G.getMediaWikiConfig('wgArticlePath').replace('$1', encodeURIComponent($G.getMediaWikiConfig('wgFormattedNamespaces')[3])) + ':';
/*
http://pl.wikipedia.org/w/index.php?title=Wikipedia:Boty&action=edit§ion=2
...tableinfo\|([^|]+)\|[^\[\]]+\[\[(?:User|Wikipedysta):([^\[\]\|]+).*
,'$1':'$2'
*/
$G.oBotToOwner = window.oRepLinksCustomB2O || {'':''
,'Ab.awbot':'Abronikowski'
,'AkBot':'Ankry'
,'AlohaBOT':'Patrol110'
,'AutoPur':'Pur'
,'Beau.bot':'Beau'
,'Beau.bot.admin':'Beau'
,'Bluebot':'Blueshade'
,'BossBot':'The boss'
,'BotOks':'Skalee'
,'Bugbot':'Lcamtuf'
,'Bulwersator: bot':'Bulwersator'
,'Cookie.bot':'Jwitos'
,'DodekBot':'Dodek'
,'g.bot':'gregul'
,'Holek.Bot':'Holek'
,'KamikazeBot':'Karol007'
,'Kbot':'Kb'
,'Lambot':'Lampak'
,'LeafBot':'Leafnode'
,'MagulBot':'Magul'
,'MalarzBOT':'malarz_pl'
,'MastiBot':'Masti'
,'MatmaBot':'Matma_Rex'
,'MBot':'maikking'
,'McBot':'McMonster'
,'Merdis.bot':'Merdis'
,'MiszaBot':'Misza13'
,'Miner':'Saper'
,'NickyBot':'Wpedzich'
,'PowerBot':'Powerek38'
,'Putorobot':'Putoro'
,'RooBot':'Roo72'
,'RewersBot':'Awersowy'
,'Staszek_Jest_Jeszcze_Szybszy':'Staszek_Szybki_Jest'
,'Szczepan.bot':'Szczepan1990'
,'TAMMBot':'TAMM'
,'ToBot':'ToSter'
,'Trivelt.bot':'Trivelt'
,'tsca.bot':'Tsca'
,'Ver-bot':'Verwolff'
,'VindiBot':'Vindicator'
,'WarXboT':'WarX'
,'WiktorynBot':'Wiktoryn'
,'YarluBot':'Yarl'
};
/**
* Get data "sent" from previous page.
*
* (data from url param)
* @private
*/
$G.autoNewSectionData = function()
{
var data = {
title: '',
content: '',
};
var reParam = new RegExp ("&newsectionname=([^&]*)", "i"); // ignoring lettercase
var matches = reParam.exec(location.search);
var sectxt;
// append to input if all OK
if (matches)
{
sectxt = decodeURIComponent(matches[1]);
data.content = ';'+sectxt+'\n\n';
}
//
// Add some summary
matches = /[ ](.*)\]/.exec(sectxt);
// append to input if all OK
if (matches)
{
data.title = decodeURIComponent(matches[1]);
}
return data;
};
/**
*
* @param {String} content
* @private
*/
$G.vePrependContent = function(content) {
var rangeToReplace = new ve.Range(0),
surfaceModel = ve.init.target.getSurface().getModel(),
fragment = surfaceModel.getLinearFragment(rangeToReplace);
fragment.insertContent(content);
};
/**
@brief Inserting new section name and some info from the location string param.
@note newsectionname url param used
*/
$G.autoNewSectionInit = function()
{
var data = $G.autoNewSectionData();
if (data.content.length <= 0)
{
return;
}
//
// Discussion tools editor (VE)
//
// (restoring changes brakes this; plus prepending content adds nowiki tags in visual model...)
/**
var discussionToolsContainer = document.querySelector('.ext-discussiontools-ui-newTopic');
if (discussionToolsContainer)
{
var titleEl = discussionToolsContainer.querySelector('.oo-ui-fieldLayout-field input');
if (titleEl) {
titleEl.value = data.title;
}
if (mw && mw.loader) {
mw.loader.using( 'ext.visualEditor.desktopArticleTarget.init' ).then( function() {
console.log('[replylinks] ve desktopArticleTarget init');
$G.vePrependContent(data.content);
});
}
return;
}
/**/
//
// Standard new-section form
//
var elInput = document.getElementById('wpTextbox1');
if (elInput)
{
// section content (link)
if (data.content.length > 0)
{
elInput.value = data.content;
}
// section title
elInput = document.getElementById('wpSummary');
if (elInput)
{
if (data.title.length > 0)
{
elInput.value = data.title;
}
}
}
};
/**
@brief Adding reply links near user links.
*/
$G.addReplyLinks = function()
{
//
// When to run this...
//
// if (!document.getElementById('t-permalink') && !document.getElementById('t-ispermalink') ) // almost always
if ($G.getMediaWikiConfig('wgCurRevisionId')==0) // no versioning available
{
return;
}
//
// Get viewed page version link (may be something in history)
//
var hrefPermalink;
// this one means it is a perma link (comparing versions, showing one specfic version and such)
if (document.location.href.indexOf('&oldid=')!=-1)
{
hrefPermalink = document.location.href.replace(/#.+$/,'');
}
// get latest
else
{
hrefPermalink = '{{fullurl:' + $G.getMediaWikiConfig('wgPageName') + '|oldid=' + $G.getMediaWikiConfig('wgCurRevisionId') + '}}';
}
//
// Find user pages links and put links into them
//
//
// create regexpes for user links
var reHref = new RegExp ($G.strReHrefBase + "([^/]*)$", "i"); // with ignore case
var reHrefNew = new RegExp ($G.strReHrefNewBase + "([^/?&]*)", "i"); // with ignore case
var reHrefAnonim = new RegExp ($G.strReHrefAnonimBase + "([\\.0-9]*|[0-9a-f]*:[0-9a-f:]+)$", 'i');
var content = document.getElementById('content');
if (!content)
{
content = document.getElementById('mw_content'); // moder skin
if (!content)
{
return;
}
}
var bodyContent_id = 'bodyContent';
var bodyContent = document.getElementById(bodyContent_id);
if (!bodyContent)
{
bodyContent = document.getElementById('mw_contentholder'); // moder skin
}
//
// first header as a default section
var secAbove = {
'id' : bodyContent_id, // for link hash
'text' : $G.parseSectionText($G.getMediaWikiConfig('wgPageName')).replace(/_/g, ' ') // for display
};
var secReplyText = $G.i18n['no section prefix'];
//
// in search for links... and sections
var a = $G.getElementsByTagNames ('A,SPAN', bodyContent);
for (var i = 0; i < a.length; i++)
{
//
// checking if this is a user link
if (a[i].nodeName.toLowerCase()=='a' && a[i].href != '' && a[i].getAttribute('href').indexOf('#')==-1)
{
var anonimous = false;
var matches = (a[i].className.indexOf('new')>=0) ? reHrefNew.exec(a[i].href) : reHref.exec(a[i].href);
if (!matches)
{
matches = reHrefAnonim.exec(a[i].href);
anonimous = true;
}
// botname translation due to match with nonanonimous link
else if ($G.oBotToOwner[matches[1]] != undefined)
{
matches[1] = $G.oBotToOwner[matches[1]];
}
if (matches)
{
//
// creating reply href
// var userName = matches[1];
var hrefReply = $G.strBaseUserTalkURL + matches[1] + '?action=edit§ion=new';
//
// and now to create and add data for the new reply section name
var newSectionName = '['+hrefPermalink+'#'+secAbove.id+' '+secReplyText+secAbove.text+']';
hrefReply += '&dtenable=0'; // disable dicussion tools
hrefReply += '&newsectionname=' + encodeURIComponent(newSectionName);
var newEl = document.createElement('small');
var newA = document.createElement('a');
newA.className='gadget-replylinks-reply';
newA.setAttribute('href', hrefReply);
newA.setAttribute('title', $G.i18n['std prefix']+secAbove.text);
newA.appendChild(document.createTextNode('['+$G.i18n['reply link text']+']'));
newEl.appendChild(newA);
$G.insertAfterGivenElement(a[i],newEl);
// Anonimous whois checker
if (anonimous)
{
newA = document.createElement('a');
newA.setAttribute('href', $G.hrefOnlineIPwhois+matches[1]);
newA.setAttribute('title', 'IP whois');
newA.appendChild(document.createTextNode('[?]'));
newEl.appendChild(newA); // appending to previously created
//i++; // a is a dynamic list
}
}
}
//
// a little hunt for sections (anchor and text of the section above user links
if (a[i].nodeName.toLowerCase()=='a' && $G.getMediaWikiConfig('wgNamespaceNumber') != 6 && a[i].id != '' && a[i].parentNode.nodeName=='P') // skip obtaining headers on image pages and non-header links
{
var header = a[i].parentNode;
// moving forward in search for the header
var found;
for (found=3; found; found--) // max 3 forward
{
header = header.nextSibling;
if (header!=null && header.nodeType==document.ELEMENT_NODE && header.nodeName.search(/h[0-9]/i)==0)
{
break;
}
}
if (found)
{
secAbove.id = a[i].id;
// sometimes there could be a link in the header (maybe some more)
secAbove.text = $G.stripSectionNumbering($G.parseSectionText(header.innerHTML), secAbove.id);
// should be set only once (as it is always the same), but let's leave it that way
secReplyText = $G.i18n['std prefix'];
//header.innerHTML = '['+secAbove.id+'@'+found+']→'+secAbove.text;
}
}
//
// vector style sections
if (a[i].className=='mw-headline')
{
secAbove.id = a[i].id;
// sometimes there could be a link in the header (maybe some more)
secAbove.text = $G.stripSectionNumbering($G.parseSectionText(a[i].innerHTML), secAbove.id);
// should be set only once (as it is always the same), but let's leave it that way
secReplyText = $G.i18n['std prefix'];
//header.innerHTML = '['+secAbove.id+'@'+found+']→'+secAbove.text;
}
}
};
/**
@brief Inserting \a newEl element after given \a el element.
@param el
Element object to insert after
@param newEl
(new) element object to insert
\* ===================================================== */
$G.insertAfterGivenElement = function (el, newEl)
{
if (el.nextSibling)
{
el.parentNode.insertBefore(newEl, el.nextSibling);
}
else
{
el.parentNode.appendChild(newEl);
}
};
/**
@brief Parses Section HTML to Text
Stripping HTML tags from the HTML text and cleansing of some wikicode
@param html
The html string
@returns Stripped text
*/
$G.parseSectionText = function (html)
{
// with global match (all will be replaced)
html = html.replace(/<\S[^<>]*>/g, '');
// replace cut anything in brackets [] (editing sections links and such)
html = html.replace(/\[[^\]]*\]/,'');
// replace wiki stuff with null
html = html.replace(/[{}]/g,'');
// trim (right,left)
html = html.replace(/[ \t]*$/,'').replace(/^[ \t]*/,'');
return html;
};
/**
@brief Strips section numbering if present.
@param sectionText Text of the section.
@param sectionId Id of the section.
@returns Stripped text
*/
$G.stripSectionNumbering= function (sectionText, sectionId)
{
// strip section numering
if (sectionText.search(/^[0-9.]+ /) > -1)
{
var isNumbered = true;
if (sectionId.search(/^[0-9.]+_/) > -1)
{
if (sectionText.replace(/^([0-9. ]+) .*/, '$1').length == sectionId.replace(/^([0-9._]+)_.*/, '$1').length)
{
isNumbered = false;
}
}
if (isNumbered)
{
sectionText = sectionText.replace(/^[0-9.]+ (.*)/, '$1');
}
}
return sectionText;
};
/**
@brief Pobiera elementy o nazwach podanych na liście
Elementy zwracane są w kolejności występowania w dokumencie.
@param list
CSV list of tag names
@param obj [optional]
Element wzg. którego pobierać elementy z listy
jak w obj.getElementsByTagName('el.name')
*/
$G.getElementsByTagNames = function (list, obj)
{
// for some reason sourceIndex doesn't work in Opera when NOT in debug mode (returns -1)...
// so we use jQuery...
var resultArray = new Array();
$(list, obj).each(function(){
resultArray.push(this);
})
return resultArray;
};
//
// Init
//
// add text to textbox
// if ($G.getMediaWikiConfig('wgAction')=='edit'
// && $G.getMediaWikiConfig('wgCanonicalNamespace')=='User_talk')
// note, wgAction=view for dynamic new-section
if (location.search.indexOf('newsectionname=') > 0
&& $G.getMediaWikiConfig('wgCanonicalNamespace')=='User_talk')
{
$($G.autoNewSectionInit);
}
// add links
if ($G.getMediaWikiConfig('wgAction')!='edit'
&& $G.getMediaWikiConfig('wgAction')!='submit')
{
$($G.addReplyLinks);
}
/* -=-=-=-=-=-=-=-=-=-=-=-
Gadget code : END
-=-=-=-=-=-=-=-=-=-=-=- */
})(oRepLinks);