Участник:Jack who built the house/copyWikilinks.js
Перейти к навигации
Перейти к поиску
Страница персонального оформления. У этого JS-кода есть документация: Участник:Jack who built the house/copyWikilinks.
После сохранения очистите кэш браузера.
После сохранения очистите кэш браузера.
/**
* copyWikilinks.js — 15.07.2017
* By Jack who built the house
* Selection-dedicated functions by Tim Down and gilly3 (see below)
* keymaster.js by Thomas Fuchs copied here from https://github.com/madrobby/keymaster, licenced under MIT
*
* Documentation: https://ru.wikipedia.org/wiki/Участник:Jack_who_built_the_house/copyWikilinks
*/
(function() {
// keymaster.js
// (c) 2011-2013 Thomas Fuchs
// keymaster.js may be freely distributed under the MIT license.
;(function(global){
var k,
_handlers = {},
_mods = { 16: false, 18: false, 17: false, 91: false },
_scope = 'all',
// modifier keys
_MODIFIERS = {
'⇧': 16, shift: 16,
'⌥': 18, alt: 18, option: 18,
'⌃': 17, ctrl: 17, control: 17,
'⌘': 91, command: 91
},
// special keys
_MAP = {
backspace: 8, tab: 9, clear: 12,
enter: 13, 'return': 13,
esc: 27, escape: 27, space: 32,
left: 37, up: 38,
right: 39, down: 40,
del: 46, 'delete': 46,
home: 36, end: 35,
pageup: 33, pagedown: 34,
',': 188, '.': 190, '/': 191,
'`': 192, '-': 189, '=': 187,
';': 186, '\'': 222,
'[': 219, ']': 221, '\\': 220,
'а': 1072, 'б': 1073, 'в': 1074, 'г': 1075, 'д': 1076, 'е': 1077, 'ё': 1105, 'ж': 1078, 'з': 1079, 'и': 1080, 'й': 1081, 'к': 1082, 'л': 1083, 'м': 1084, 'н': 1085, 'о': 1086, 'п': 1087, 'р': 1088, 'с': 1089, 'т': 1090, 'у': 1091, 'ф': 1092, 'х': 1093, 'ц': 1094, 'ч': 1095, 'ш': 1096, 'щ': 1097, 'ъ': 1098, 'ы': 1099, 'ь': 1100, 'э': 1101, 'ю': 1102, 'я': 1103,
},
code = function(x){
return _MAP[x] || x.toUpperCase().charCodeAt(0);
},
_downKeys = [];
for(k=1;k<20;k++) _MAP['f'+k] = 111+k;
// IE doesn't support Array#indexOf, so have a simple replacement
function index(array, item){
var i = array.length;
while(i--) if(array[i]===item) return i;
return -1;
}
// for comparing mods before unassignment
function compareArray(a1, a2) {
if (a1.length != a2.length) return false;
for (var i = 0; i < a1.length; i++) {
if (a1[i] !== a2[i]) return false;
}
return true;
}
var modifierMap = {
16:'shiftKey',
18:'altKey',
17:'ctrlKey',
91:'metaKey'
};
function updateModifierKey(event) {
for(k in _mods) _mods[k] = event[modifierMap[k]];
};
// handle keydown event
function dispatch(event) {
var key, handler, k, i, modifiersMatch, scope;
key = event.keyCode;
if (index(_downKeys, key) == -1) {
_downKeys.push(key);
}
// if a modifier key, set the key.<modifierkeyname> property to true and return
if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko
if(key in _mods) {
_mods[key] = true;
// 'assignKey' from inside this closure is exported to window.key
for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true;
return;
}
updateModifierKey(event);
// see if we need to ignore the keypress (filter() can can be overridden)
// by default ignore key presses if a select, textarea, or input is focused
if(!assignKey.filter.call(this, event)) return;
// abort if no potentially matching shortcuts found
if (!(key in _handlers)) return;
scope = getScope();
// for each potential shortcut
for (i = 0; i < _handlers[key].length; i++) {
handler = _handlers[key][i];
// see if it's in the current scope
if(handler.scope == scope || handler.scope == 'all'){
// check if modifiers match if any
modifiersMatch = handler.mods.length > 0;
for(k in _mods)
if((!_mods[k] && index(handler.mods, +k) > -1) ||
(_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false;
// call the handler and stop the event if neccessary
if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){
if(handler.method(event, handler)===false){
if(event.preventDefault) event.preventDefault();
else event.returnValue = false;
if(event.stopPropagation) event.stopPropagation();
if(event.cancelBubble) event.cancelBubble = true;
}
}
}
}
};
// unset modifier keys on keyup
function clearModifier(event){
var key = event.keyCode, k,
i = index(_downKeys, key);
// remove key from _downKeys
if (i >= 0) {
_downKeys.splice(i, 1);
}
if(key == 93 || key == 224) key = 91;
if(key in _mods) {
_mods[key] = false;
for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false;
}
};
function resetModifiers() {
for(k in _mods) _mods[k] = false;
for(k in _MODIFIERS) assignKey[k] = false;
};
// parse and assign shortcut
function assignKey(key, scope, method){
var keys, mods;
keys = getKeys(key);
if (method === undefined) {
method = scope;
scope = 'all';
}
// for each shortcut
for (var i = 0; i < keys.length; i++) {
// set modifier keys if any
mods = [];
key = keys[i].split('+');
if (key.length > 1){
mods = getMods(key);
key = [key[key.length-1]];
}
// convert to keycode and...
key = key[0]
key = code(key);
// ...store handler
if (!(key in _handlers)) _handlers[key] = [];
_handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
}
};
// unbind all handlers for given key in current scope
function unbindKey(key, scope) {
var multipleKeys, keys,
mods = [],
i, j, obj;
multipleKeys = getKeys(key);
for (j = 0; j < multipleKeys.length; j++) {
keys = multipleKeys[j].split('+');
if (keys.length > 1) {
mods = getMods(keys);
}
key = keys[keys.length - 1];
key = code(key);
if (scope === undefined) {
scope = getScope();
}
if (!_handlers[key]) {
return;
}
for (i = 0; i < _handlers[key].length; i++) {
obj = _handlers[key][i];
// only clear handlers if correct scope and mods match
if (obj.scope === scope && compareArray(obj.mods, mods)) {
_handlers[key][i] = {};
}
}
}
};
// Returns true if the key with code 'keyCode' is currently down
// Converts strings into key codes.
function isPressed(keyCode) {
if (typeof(keyCode)=='string') {
keyCode = code(keyCode);
}
return index(_downKeys, keyCode) != -1;
}
function getPressedKeyCodes() {
return _downKeys.slice(0);
}
function filter(event){
var tagName = (event.target || event.srcElement).tagName;
// ignore keypressed in any elements that support keyboard data input
return true; // !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
}
// initialize key.<modifier> to false
for(k in _MODIFIERS) assignKey[k] = false;
// set current scope (default 'all')
function setScope(scope){ _scope = scope || 'all' };
function getScope(){ return _scope || 'all' };
// delete all handlers for a given scope
function deleteScope(scope){
var key, handlers, i;
for (key in _handlers) {
handlers = _handlers[key];
for (i = 0; i < handlers.length; ) {
if (handlers[i].scope === scope) handlers.splice(i, 1);
else i++;
}
}
};
// abstract key logic for assign and unassign
function getKeys(key) {
var keys;
key = key.replace(/\s/g, '');
keys = key.split(',');
if ((keys[keys.length - 1]) == '') {
keys[keys.length - 2] += ',';
}
return keys;
}
// abstract mods logic for assign and unassign
function getMods(key) {
var mods = key.slice(0, key.length - 1);
for (var mi = 0; mi < mods.length; mi++)
mods[mi] = _MODIFIERS[mods[mi]];
return mods;
}
// cross-browser events
function addEvent(object, event, method) {
if (object.addEventListener)
object.addEventListener(event, method, false);
else if(object.attachEvent)
object.attachEvent('on'+event, function(){ method(window.event) });
};
// set the handlers globally on document
addEvent(document, 'keydown', function(event) { dispatch(event) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48
addEvent(document, 'keyup', clearModifier);
// reset modifiers to false whenever the window is (re)focused.
addEvent(window, 'focus', resetModifiers);
// store previously defined key
var previousKey = global.key;
// restore previously defined key and return reference to our key object
function noConflict() {
var k = global.key;
global.key = previousKey;
return k;
}
// set window.key and window.key.set/get/deleteScope, and the default filter
global.key = assignKey;
global.key.setScope = setScope;
global.key.getScope = getScope;
global.key.deleteScope = deleteScope;
global.key.filter = filter;
global.key.isPressed = isPressed;
global.key.getPressedKeyCodes = getPressedKeyCodes;
global.key.noConflict = noConflict;
global.key.unbind = unbindKey;
if(typeof module !== 'undefined') module.exports = assignKey;
})(this);
/*** Technical functions ***/
function trim(str) {
return str.replace(/^\s+|\s+$/g, '');
}
// By Tim Down, posted at http://stackoverflow.com/a/5379408
function getSelectionText() {
var text = "";
if (window.getSelection) {
text = window.getSelection().toString();
} else if (document.selection && document.selection.type != "Control") {
text = document.selection.createRange().text;
}
return text;
}
// By Tim Down, posted at http://stackoverflow.com/a/3966822
function getInputSelection(el) {
var start = 0, end = 0, normalizedValue, range,
textInputRange, len, endRange;
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
start = el.selectionStart;
end = el.selectionEnd;
} else {
range = document.selection.createRange();
if (range && range.parentElement() == el) {
len = el.value.length;
normalizedValue = el.value.replace(/\r\n/g, "\n");
// Create a working TextRange that lives only in the input
textInputRange = el.createTextRange();
textInputRange.moveToBookmark(range.getBookmark());
// Check if the start and end of the selection are at the very end
// of the input, since moveStart/moveEnd doesn't return what we want
// in those cases
endRange = el.createTextRange();
endRange.collapse(false);
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
start = end = len;
} else {
start = -textInputRange.moveStart("character", -len);
start += normalizedValue.slice(0, start).split("\n").length - 1;
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
end = len;
} else {
end = -textInputRange.moveEnd("character", -len);
end += normalizedValue.slice(0, end).split("\n").length - 1;
}
}
}
}
return {
start: start,
end: end
};
}
// By Tim Down, posted at http://stackoverflow.com/a/3966822
// Modified by Jack who built the house
function replaceSelectedText(el, text, restoreSelection) {
var sel = getInputSelection(el), val = el.value;
el.value = val.slice(0, sel.start) + text + val.slice(sel.end);
if (restoreSelection)
setCursorPos(el, sel.start, sel.start + text.length);
else
setCursorPos(el, sel.start + text.length);
}
// By gilly3, posted at http://stackoverflow.com/a/7745998
function setCursorPos(input, start, end) {
if (arguments.length < 3) end = start;
if ("selectionStart" in input) {
input.selectionStart = start;
input.selectionEnd = end;
} else if (input.createTextRange) {
var rng = input.createTextRange();
rng.moveStart("character", start);
rng.collapse();
rng.moveEnd("character", end - start);
rng.select();
}
}
function dotToPercent(code) {
return code.replace(/\.([0-9A-F][0-9A-F])/g, '%$1');
}
function htmlEncodeWikiMarkup(text) {
var map = {
'.3C': '<', // <
'.3E': '>', // >
'.5B': '[', // [
'.5D': ']', // ]
'.7B': '{', // {
'.7C': '|', // |
'.7D': '}', // }
};
return text.replace(/\.(?:3C|3E|5B|5D|7B|7C|7D)/g, function(ch) { return map[ch]; });
}
function focusEdge() {
if (navigator.userAgent.indexOf('Edge') !== -1) {
document.focus();
}
}
/*** Main functions ***/
/* Sorted from more to less specific */
function processAnchorAndSpaces(link, processAll) {
processAll = !!processAll;
return htmlEncodeWikiMarkup(link
.replace(/(^|[^0-9A-F\.])(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/g, '$1$2,$3,$4,$5') // hide IP
.replace(/\.F[0-4]\.[89AB][\dA-F]\.[89AB][\dA-F]\.[89AB][\dA-F]/g, dotToPercent) // The codes were borrowed from
.replace(/\.E[\dA-F]\.[89AB][\dA-F]\.[89AB][\dA-F]/g, dotToPercent) // MediaWiki:Gadget-urldecoder.js
.replace(/\.[CD][\dA-F]\.[89AB][\dA-F]/g, dotToPercent)
.replace(/\.[2-7][0-9A-F]/g, function(code) {
var ch = decodeURIComponent(dotToPercent(code));
if ('!"#$%&\'()*+,/;<=>?@\\^`~'.indexOf(ch) != -1 || processAll)
return dotToPercent(code);
else
return code;
})
.replace(/(^|[^0-9A-F\.])(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?),(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?),(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?),(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/g, '$1$2.$3.$4.$5') // restore IP
.replace(/_/g, ' '));
}
function handlePrefixes(wikilink) {
// w:en:wikt (en.wikipedia.org/wiki/wikt:) → :en:wikt
wikilink = wikilink.replace(/^([a-z]+:)([a-z]+:)((w|wikt|s|q|n|b|v|voy):)/, function(s, p1, p2, p3) {
return ':' + p2 + p3;
});
// w:en:ru (en.wikipedia.org/wiki/ru:) → w:ru
wikilink = wikilink.replace(/^([a-z]+:)([a-z]+:)([a-z]{2}:)/, function(s, p1, p2, p3) {
return p1 + p3;
});
wikilink = wikilink.replace(homeProjectPrefixDetector, function(s, p1, p2) {
return p1 + (p2 ? p2 : (homeLangPrefix ? ':' : '' ));
});
if (homeLangPrefix) wikilink = wikilink.replace(homeLangPrefixDetector, '$1$2');
wikilink = wikilink.replace(/^(:?([a-z]+:){1,2}):/, '$1'); // ...::File, ...::Category
return wikilink;
}
function processURI(uri, type, brackets, linkText) {
if (type == 'wikilink') {
uri = uri
.replace(/%(5B|5D|7B|7D|7C|3C|3E)/g, '%25$1') // []{}|<>
.replace(/%C2%A0/g, '.C2.A0'); // Non-breaking space. Otherwise parser turns it into normal space.
// Should appear only in section links, so we can use dot notation
// here (percent signs don't work).
try {
uri = decodeURIComponent(uri);
} catch(e) {
return null;
}
} else if (type == 'link') {
uri = uri
.replace(linksDetector, function(s) {
link = s
.replace(/%(20|0A|0D|5B|5D|3C|3E|2B|22|3F|26|3D|23|27)/g, '%25$1') // space, \r, \n, []<>+"?&=#'
.replace(/%C2%A0/g, '%25C2%25A0'); // Non-breaking space in links is treated as space by parser,
// so it should stay encoded.
try {
link = decodeURIComponent(link);
} catch(e) {
return s;
}
if (brackets)
link = '[' + link + (linkText ? ' ' + linkText : '') + ']';
return link;
});
} else { // type == 'text' || type == 'cyrillicAnchor'
try {
uri = decodeURIComponent(uri);
} catch(e) {
return null;
}
}
return trim(uri);
}
function links2Wikilinks(link, isMonolithicLink, brackets, singleBrackets, linkText, useIwTemplate) {
function constructWikilink(prefix, page, lb, rb) {
if (lb != rb)
return null;
var trailingPunct = '';
if (!isMonolithicLink) {
var trailingPunctRegex = new RegExp(
'[,;\\\\\.:!\\?' // Borrowed from MediaWiki:Gadget-urldecoder.js:
+ (/\(/.test(page) ? '' : '\\)') // closing bracket without opening bracket
+ ']+$|\'\'+$' // or possible bold/italic at the end of url
);
page = page.replace(trailingPunctRegex, function(s) {
trailingPunct = s;
return '';
});
}
page = page.replace(/\?uselang=\w+($|#)/g, '$1');
if (page.search(/\?/) != -1) {
return null;
} else {
var opener = brackets ? '[[' : '';
var closer = brackets ? ']]' : '';
var fullpage;
page = processURI(processAnchorAndSpaces(page), 'wikilink', false);
if (useIwTemplate && prefix.match(/^w:/) && prefix != 'w:ru' && !page.match(/#/)) {
return '{{iw|||' + (prefix == 'w:en' ? '' : prefix.replace(/^w:/, '')) + '|' + page + '}}';
} else {
if (!isMonolithicLink || copyWikilinksColonFile || page.match(/#/))
page = page
.replace(/^Файл:/i, ':Файл:')
.replace(/^File:/i, ':File:');
if (!isMonolithicLink || copyWikilinksColonCategory || page.match(/#/))
page = page
.replace(/^Категория:/i, ':Категория:')
.replace(/^Category:/i, ':Category:');
if (isMonolithicLink && !copyWikilinksColonFile && prefix == 'commons' && page.match(/^File:/) && !page.match(/#/)) {
prefix = '';
if (homeLangPrefix == 'ru')
page = page.replace(/^File:/, 'Файл:');
}
fullpage = handlePrefixes((prefix ? prefix + ':' : '') + page);
if (fullpage[0] == '/') {
fullpage = ':' + fullpage;
}
closer =
(brackets && linkText && fullpage != linkText && fullpage != ':' + linkText && linkText.search(linksDetector) == -1
? '|' + linkText
: '')
+ closer
+ (trailingPunct ? trailingPunct : '');
return opener + fullpage + closer;
}
}
}
useIwTemplate = !!useIwTemplate;
return link
.replace(/(\[?)https?:\/\/phabricator\.wikimedia\.org\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, page, rb) { return constructWikilink('phab', page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/ru\.wikimedia\.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, page, rb) { return constructWikilink('wmru', page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/([^\.]+)\.wikipedia\.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, langPrefix, page, rb) { return constructWikilink('w:' + langPrefix, page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/([^\.]+)\.wikipedia\.org\/w\/index\.php\?title=([^&]+)&action=edit&redlink=1(\]?)/ig,
// Counting red links could be done for all sites, but have to refactor this
function(s, lb, langPrefix, page, rb) { return constructWikilink('w:' + langPrefix, page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/([^\.]+)\.wikimedia\.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, projectPrefix, page, rb) { return constructWikilink(projectPrefix, page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/([^\.]+)\.wikimedia\.org\/w\/index\.php\?title=([^&]+)&action=edit&redlink=1(\]?)/ig,
function(s, lb, projectPrefix, page, rb) { return constructWikilink(projectPrefix, page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/([^\.]+)\.wiktionary\.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, langPrefix, page, rb) { return constructWikilink('wikt:' + langPrefix, page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/([^\.]+)\.wiktionary\.org\/w\/index\.php\?title=([^&]+)&action=edit&redlink=1(\]?)/ig,
function(s, lb, langPrefix, page, rb) { return constructWikilink('wikt:' + langPrefix, page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/([^\.]+)\.wikisource\.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, langPrefix, page, rb) { return constructWikilink('s:' + langPrefix, page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/([^\.]+)\.wikiquote\.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, langPrefix, page, rb) { return constructWikilink('q:' + langPrefix, page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/([^\.]+)\.wikinews\.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, langPrefix, page, rb) { return constructWikilink('n:' + langPrefix, page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/([^\.]+)\.wikibooks\.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, langPrefix, page, rb) { return constructWikilink('b:' + langPrefix, page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/([^\.]+)\.wikiversity\.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, langPrefix, page, rb) { return constructWikilink('v:' + langPrefix, page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/([^\.]+)\.wikivoyage\.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, langPrefix, page, rb) { return constructWikilink('voy:' + langPrefix, page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/(?:www\.)?wikidata.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, page, rb) { return constructWikilink('d', page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/(?:www\.)?mediawiki.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, page, rb) { return constructWikilink('mw', page, !!lb, !!rb) || s; })
.replace(/(\[?)https?:\/\/(?:www\.)?wikimediafoundation.org\/wiki\/([^\[\]\n\r<>" ]+)(\]?)/ig,
function(s, lb, page, rb) { return constructWikilink('foundation', page, !!lb, !!rb) || s; })
// The rest of the links in the selection
.replace(linksDetector, function(s) { return processURI(s, 'link', singleBrackets, linkText); });
}
function cyrillicAnchors() {
var sectionHeadings = document.getElementsByClassName('mw-headline');
var processedAnchor;
for (var i = 0; i < sectionHeadings.length; i++) {
processedAnchor = processURI(processAnchorAndSpaces(sectionHeadings[i].id, true), 'cyrillicAnchor', false);
if (processedAnchor)
sectionHeadings[i].innerHTML =
'<a name="' + processedAnchor.replace(/ /g, ' ') .replace(/"/g, '"') + '"></a>'
+ '<a name="' + processedAnchor.replace(/ /g, '_').replace(/ /g, '_').replace(/"/g, '"') + '"></a>'
+ sectionHeadings[i].innerHTML;
}
if (location.hash.search(/[а-яё]/i) != -1)
document.querySelector('a[name="' + location.hash.substring(1).replace(/"/g, '\\22') + '"]').scrollIntoView();
}
/************************************************************/
cyrillicAnchors();
var defaultHomeLangPrefix = 'ru';
var potentialWikilinkDetector = new RegExp('https?:\\/\\/([^\\.]+\\.)?((wikipedia|wikimedia|wiktionary|wikisource|wikiquote|wikinews'
+ '|wikibooks|wikiversity|wikivoyage|wikidata|mediawiki|wikimediafoundation)\\.org)', 'i');
var linksDetector = new RegExp('https?:\\/\\/[^\\[\\]\\n\\r<>" ]+', 'ig');
// This is not used, but may be used to detect links without square brackets in the following fashion
// (given linkText is the link text to be displayed, extracted from somewhere):
// text = text.replace(noBracketsLinksDetector, '$1[$2' + (linkText ? ' ' + linkText : '') + ']');
// Though problems arise here as usually we decode links, and decoding after this statement would decode linkText as well,
// while decoding before it would replace %22 with " and make link boundaries incorrectly identified.
// See how this is solved in the processURI function.
// var noBracketsLinksDetector = new RegExp('([^\\[]|^)(https?:\\/\\/[^\\[\\]\\n\\r<>" ]+)', 'ig');
var linkBeforeCursorDetector = new RegExp('\\[?https?:\\/\\/[^\\[\\]\\n\\r<>" ]+\\]?[ \\t\\n\\r]*$', 'i');
var homeProjectPrefix, homeLangPrefix, homeProjectPrefixDetector, homeLangPrefixDetector, currentPrefix, cleanPagename, pagename, copyURL, temp;
/* If uncommented, this makes "public" functions globally visible, making it easier to debug */
window.cw = {
cyrillicAnchors: cyrillicAnchors,
links2Wikilinks: links2Wikilinks,
processURI: processURI,
handlePrefixes: handlePrefixes,
processAnchorAndSpaces: processAnchorAndSpaces
};
/* */
// defaults
if (!('copyWikilinksUseUrlDecoder' in window)) window.copyWikilinksUseUrlDecoder = true;
if (!('copyWikilinksSqBrackets' in window)) window.copyWikilinksSqBrackets = true;
if (!('copyWikilinksSingleSqBrackets' in window)) window.copyWikilinksSingleSqBrackets = true;
if (!('copyWikilinksUseLinkText' in window)) window.copyWikilinksUseLinkText = false;
if (!('copyWikilinksUseIwTemplate' in window)) window.copyWikilinksUseIwTemplate = false;
if (!('copyWikilinksHomePrefix' in window)) window.copyWikilinksHomePrefix = 'w:' + defaultHomeLangPrefix;
if (!('copyWikilinksCurrentIsHome' in window)) window.copyWikilinksCurrentIsHome = false;
if (!('copyWikilinksColonFile' in window)) window.copyWikilinksColonFile = false;
if (!('copyWikilinksColonCategory' in window)) window.copyWikilinksColonCategory = false;
if (!('copyWikilinksKeyCombination1' in window)) window.copyWikilinksKeyCombination1 = 'ctrl+`';
if (!('copyWikilinksKeyCombination2' in window)) window.copyWikilinksKeyCombination2 = 'ctrl+shift+`';
if (!('copyWikilinksKeyCombination3' in window)) window.copyWikilinksKeyCombination3 = 'alt+`';
// Only sites where this script can be loaded
currentPrefix = location.href
.replace(/^https?:\/\/ru\.wikimedia\.org\/.*/, 'wmru:')
.replace(/^https?:\/\/([^\.]+)\.wikipedia\.org\/.*/, 'w:$1:')
.replace(/^https?:\/\/([^\.]+)\.wikimedia\.org\/.*/, '$1:')
.replace(/^https?:\/\/([^\.]+)\.wiktionary\.org\/.*/, 'wikt:$1:')
.replace(/^https?:\/\/([^\.]+)\.wikisource\.org\/.*/, 's:$1:')
.replace(/^https?:\/\/([^\.]+)\.wikiquote\.org\/.*/, 'q:$1:')
.replace(/^https?:\/\/([^\.]+)\.wikinews\.org\/.*/, 'n:$1:')
.replace(/^https?:\/\/([^\.]+)\.wikibooks\.org\/.*/, 'b:$1:')
.replace(/^https?:\/\/([^\.]+)\.wikiversity\.org\/.*/, 'v:$1:')
.replace(/^https?:\/\/([^\.]+)\.wikivoyage\.org\/.*/, 'voy:$1:')
.replace(/^https?:\/\/(www\.)?wikidata.org\/.*/, 'd:')
.replace(/^https?:\/\/(www\.)?mediawiki.org\/wiki\/.*/, 'mw:')
.replace(/^https?:\/\/(www\.)?wikimediafoundation.org\/wiki\/.*/, 'foundation:')
.replace(/^https?:\/\/.*/, '');
if (copyWikilinksCurrentIsHome) {
copyWikilinksHomePrefix = currentPrefix.slice(0, -1);
}
if (copyWikilinksHomePrefix.search(/^(w|commons|meta|wikitech|wikt|s|q|n|b|v|voy|d|mw|foundation|wmru):?/) === -1) {
homeProjectPrefix = 'w';
homeLangPrefix = copyWikilinksHomePrefix;
} else if (copyWikilinksHomePrefix.search(/^(w|wikt|s|q|n|b|v|voy)$/) !== -1) {
homeProjectPrefix = copyWikilinksHomePrefix;
homeLangPrefix = defaultHomeLangPrefix;
} else {
temp = copyWikilinksHomePrefix.split(':');
homeProjectPrefix = temp[0];
homeLangPrefix = temp[1] || '';
}
copyWikilinksHomePrefix = homeProjectPrefix + (homeLangPrefix ? ':' + homeLangPrefix : '');
homeProjectPrefixDetector = new RegExp('(^|\\[\\[)(:?[a-z]+:)?' + homeProjectPrefix + ':', 'g');
homeLangPrefixDetector = new RegExp('(^|\\[\\[):?([a-z]+:)?' + homeLangPrefix + ':', 'g');
// Remove home prefix
currentPrefix = handlePrefixes(currentPrefix);
if (!currentPrefix && mw.config.get('wgNamespaceNumber') == 14 && copyWikilinksColonCategory ||
!currentPrefix && mw.config.get('wgNamespaceNumber') == 6 && copyWikilinksColonFile) {
currentPrefix = ':';
}
cleanPagename = mw.config.get('wgPageName').replace(/_/g, ' ');
if (!copyWikilinksColonFile && currentPrefix == 'commons:' && cleanPagename.match(/^File:/)) {
currentPrefix = '';
if (homeLangPrefix == 'ru') {
cleanPagename = cleanPagename.replace(/^File:/, 'Файл:');
}
}
pagename = ((!copyWikilinksCurrentIsHome || currentPrefix === ':') ? currentPrefix : '') + cleanPagename;
if (location.pathname.search('/wiki/') == -1 &&
location.search.search('action=edit') == -1 && location.search.search('action=submit') == -1) {
copyURL = true;
} else {
copyURL = false;
}
key(copyWikilinksKeyCombination1, ctrlyo);
key(copyWikilinksKeyCombination2, ctrlshiftyo);
key(copyWikilinksKeyCombination3, altyo);
function ctrlyo() {
var link, wikilink, iwTemplate;
if (!copyURL) {
if (copyWikilinksUseIwTemplate && !currentPrefix.match(/^:?$/) && location.hostname.search('wikipedia.org') != -1 && !document.getElementsByClassName('interwiki-ru')[0]) {
prompt('Шаблон «iw» для текущей страницы:', '{{iw|||' + (currentPrefix.match(/^w?:en:$/) ? '' : currentPrefix.replace(/^w:/, '').replace(/:/g, '')) + '|' + cleanPagename + '}}');
} else {
prompt('Вики-ссылка на текущую страницу:', (copyWikilinksSqBrackets ? '[[' : '') + pagename + (copyWikilinksSqBrackets ? ']]' : ''));
}
focusEdge();
} else {
link = processURI(location.href.split("#")[0], 'link', copyWikilinksSingleSqBrackets);
if (link) {
prompt('Раскодированный адрес текущей страницы:', link);
focusEdge();
}
}
return false;
}
function ctrlshiftyo () {
var link, wikilink, iwTemplate;
var hoveredLink = document.querySelector('a:hover');
if (hoveredLink) {
var linkText;
link = hoveredLink.href;
if (copyWikilinksUseLinkText)
linkText = hoveredLink.textContent;
if (link.search(potentialWikilinkDetector) != -1) {
link = links2Wikilinks(link, true, copyWikilinksSqBrackets, copyWikilinksSingleSqBrackets, linkText,
copyWikilinksUseIwTemplate && !(
hoveredLink.parentNode.className.match(/\binterwiki-/) && (
currentPrefix.match(/^:?$/) ||
!currentPrefix.match(/^:?$/) && document.getElementsByClassName('interwiki-ru')[0]
)
)
);
// "iw" template is NOT generated when the hovered link is in the interwiki block on ruwiki, OR on other wiki AND there's ru interwiki
if (copyWikilinksUseIwTemplate)
iwTemplate = link;
else if (link.search(linksDetector) == -1)
wikilink = link;
} else {
link = processURI(link, 'link', copyWikilinksSingleSqBrackets, linkText);
// This line is arguable and can be removed, if it will make URLs broken too often
}
if (iwTemplate) {
prompt('Шаблон «iw» для страницы по этой ссылке:', iwTemplate);
} else if (wikilink) {
prompt('Вики-ссылка на страницу по этой ссылке:', wikilink);
} else {
prompt('Раскодированный адрес этой ссылки:', link);
}
focusEdge();
} else {
var hoveredElements = document.querySelectorAll(":hover");
if (hoveredElements.length) {
var hoveredElement = hoveredElements[hoveredElements.length-1];
var currentElement = hoveredElement;
var sectionHeadingElement;
for (var i = 0; i < 300; i++) {
switch (currentElement.nodeName) {
case 'H2': case 'H3': case 'H4': case 'H5': case 'H6':
sectionHeadingElement = currentElement;
break;
}
if (!sectionHeadingElement) {
if (currentElement.previousElementSibling) {
currentElement = currentElement.previousElementSibling;
} else {
currentElement = currentElement.parentElement;
if (!currentElement)
break;
}
} else {
//alert('found!' + i + ' ' + sectionHeadingElement.textContent);
var mwHeadlineElement = sectionHeadingElement.getElementsByClassName('mw-headline')[0];
if (mwHeadlineElement) {
var anchor = mwHeadlineElement.id;
if (!copyURL) {
var processedAnchor = processURI(processAnchorAndSpaces(anchor), 'wikilink', false);
if (processedAnchor) {
wikilink = (copyWikilinksSqBrackets ? '[[' : '') + pagename + '#' + processedAnchor + (copyWikilinksSqBrackets ? ']]' : '');
prompt('Вики-ссылка на этот раздел:', wikilink);
focusEdge();
}
} else {
link = processURI(location.href.split("#")[0] + '#' + anchor, 'link', copyWikilinksSingleSqBrackets);
prompt('Раскодированная ссылка на этот раздел:', link);
focusEdge();
}
}
break;
}
}
}
}
}
function altyo() {
var link, wikilink, iwTemplate;
var selectionText = getSelectionText();
var noSelection = false;
var input = document.activeElement;
var urlDecoderButton = document.querySelector('a[rel="urldecoder"]');
var lastUrl;
if (input.nodeName == 'TEXTAREA' && copyWikilinksUseUrlDecoder && urlDecoderButton) {
urlDecoderButton.click();
} else {
var cursorPos;
if (input.nodeName == 'TEXTAREA' ||
input.nodeName == 'INPUT' && (input.type == 'text' || input.type == 'search')) {
if (!selectionText) {
cursorPos = getInputSelection(input);
if (cursorPos.start == cursorPos.end) {
noSelection = true;
cursorPos = cursorPos.start;
lastUrl = input.value.substring(cursorPos - 1000, cursorPos).match(linkBeforeCursorDetector);
if (lastUrl) {
lastUrl = lastUrl[0];
selectionText = lastUrl;
}
} else { // In Firefox, getSelectionText() doesn't work for text fields
selectionText = input.value.substring(cursorPos.start, cursorPos.end);
}
}
} else {
selectionText = trim(selectionText);
}
if (!selectionText) {
} else if (selectionText.search(lastUrl ? new RegExp('^' + potentialWikilinkDetector.source) : potentialWikilinkDetector) != -1) {
wikilink = links2Wikilinks(selectionText, false, true, false);
if (!wikilink) {
} else if (input.nodeName == 'TEXTAREA' ||
input.nodeName == 'INPUT' && (input.type == 'text' || input.type == 'search')) {
if (lastUrl)
setCursorPos(input, cursorPos - lastUrl.length, cursorPos);
replaceSelectedText(input, wikilink, !noSelection);
} else {
prompt('Вики-ссылка на страницу по этой ссылке:', wikilink);
}
} else if (selectionText.search(linksDetector) != -1) {
link = selectionText.replace(linksDetector, function(s) { return processURI(s, 'link', false); });
if (!link) {
} else if (input.nodeName == 'TEXTAREA' ||
input.nodeName == 'INPUT' && (input.type == 'text' || input.type == 'search')) {
if (lastUrl)
setCursorPos(input, cursorPos - lastUrl.length, cursorPos);
replaceSelectedText(input, link, !noSelection);
} else {
prompt('Раскодированный адрес этой ссылки:', link);
}
} else if (selectionText) {
var decodedText = processURI(selectionText, 'text', false);
if (decodedText && decodedText != selectionText) {
if (input.nodeName == 'TEXTAREA' ||
input.nodeName == 'INPUT' && (input.type == 'text' || input.type == 'search')) {
replaceSelectedText(input, decodedText);
} else {
prompt('Раскодированный текст:', decodedText);
}
}
}
}
}
var browser = navigator.userAgent.toLowerCase();
if (browser.indexOf('firefox') != -1) {
document.addEventListener('keydown', function (event) {
// keypress: ` is 96, ё is 1105; keydown: both are 192. In Firefox, ё is 0 for some reason,
// along with some other keys.
if ((copyWikilinksKeyCombination1 == 'ctrl+`' || copyWikilinksKeyCombination1 == 'ctrl+ё') &&
event.ctrlKey && event.key == 'ё')
{
ctrlyo();
}
if ((copyWikilinksKeyCombination1 == 'ctrl+shift+`' || copyWikilinksKeyCombination1 == 'ctrl+shift+ё') &&
event.ctrlKey && event.key == 'Ё')
{
ctrlshiftyo();
}
if ((copyWikilinksKeyCombination1 == 'alt+`' || copyWikilinksKeyCombination1 == 'alt+ё') &&
event.altKey && event.key == 'ё')
{
altyo();
}
}, false);
}
})();