Järjestelmäviesti:Gadget-Artikkeliyhdistaja.js
Huomautus: Selaimen välimuisti pitää tyhjentää asetusten tallentamisen jälkeen, jotta muutokset tulisivat voimaan.
- Firefox ja Safari: Napsauta Shift-näppäin pohjassa Päivitä, tai paina Ctrl-F5 tai Ctrl-R (⌘-R Macilla)
- Google Chrome: Paina Ctrl-Shift-R (⌘-Shift-R Macilla)
- Internet Explorer ja Edge: Napsauta Ctrl-näppäin pohjassa Päivitä tai paina Ctrl-F5
- Opera: Paina Ctrl-F5.
(mw.config.get("wgAction") === "edit")
&& (function () {
(function () {
/**
* Otsikoiden järjestysdata.
**/
window.sectOrder = {};
var titles = [];
titles[2] = {
"Kansainvälinen": [-20],
"Suomi": [-10],
// muut [0],
"Lähteet": [+10],
"Viitteet": [+20],
};
titles[3] = {
// muut [0],
"Lähteet": [+10],
"Viitteet": [+20],
};
titles[4] = {
"Ääntäminen": [-100],
"Taivutus": [-90],
"Huomautukset": [-80],
"Etymologia": [-70],
"Käännökset": [-60],
"Lainaukset": [-50],
"Liittyvät sanat": [-40],
"Idiomit": [-30],
"Katso myös": [-20],
"Aiheesta muualla": [-10],
// muut [0],
"Lähteet": [+10],
"Viitteet": [+20],
};
titles[5] = {
"Rinnakkaiset kirjoitusasut": [-140],
"Rinnakkaismuodot": [-130],
"Lyhenteet": [-120],
"Synonyymit": [-110],
"Vastakohdat": [-100],
"Johdokset": [-90],
"Yhdyssanat": [-80],
"Tästä johtuvat tytärkielten sanat": [-70],
"Lainat muissa kielissä": [-60],
"Yläkäsitteet": [-50],
"Vieruskäsitteet": [-40],
"Alakäsitteet": [-30],
"Osakäsitteet": [-20],
"Kokonaiskäsitteet": [-10],
// muut [0]
};
/**
* Palauttaa arvoparin, jolla otsikkon järjestystä voi verrata
* toiseen otsikkoon.
**/
function getCmpValue(title, level) {
var cmpVal = titles[level] && titles[level][title];
if ( cmpVal ) {
return [cmpVal[0], title];
}
return [0, title];
}
/**
* Palauttaa 0, jos otsikot ovat samat,
* -1, jos sect1 tulee ennen sect2:ta,
* +1, jos sect1 tulee sect2:n jälkeen.
**/
sectOrder.compareTitles = function (sect1, sect2) {
var a = getCmpValue(sect1.title, sect1.level),
b = getCmpValue(sect2.title, sect2.level),
i = 0;
console.assert ( sect1.level === sect2.level );
while ( i < 2 ) {
if ( a[i] === b[i] ) {
i++;
} else if ( a[i] < b[i] ) {
return -1;
} else { // ( a[i] > b[i] )
return +1;
}
}
return 0;
};
}());
(function () {
/**
* Yksinkertainen osioparseri.
**/
window.sectParser = {};
/**
* Parseroi wikitekstin ja palauttaa taulukon, jossa jokainen osio on
* esitetty muodossa:
* {
* title: <otsikko, =-merkit ja rivinvaihto>, // Esim. "Suomi"
* level: <otsikon taso> // Esim. 2
* content: <sisältö ilman aliosoita> // Esim. "{{fi-subs|ka|na}}\n# lintu\n"
* }
* TODO: luokkien yhdistäminen
**/
sectParser.parseText = function (text) {
var osat, otst, osot, classes;
console.assert ( typeof(text) === "string", "Virheellinen parametri" );
main_end = text.search(/\n(\[\[Luokka:[^\n]+\]\]\s*\n*)+$/);
if ( main_end === -1 ) {
main_end = text.length;
}
classes = text.substring(main_end);
text = text.substring(0, main_end);
osat = text.split(/\B=+[^\n]+=+\n/) || text; // Osioiden sisällöt.
otst = text.match(/\B=+[^\n]+=+\n/g) || []; // Osioiden otsikot.
otst.unshift('PAGENAME\n'); // Osa ennen ensimmäistä otsikkoa
osot = otst.map(function (elt, i) {
m = elt.match(/^(=*) *(.*?) *(\1)\n$/);
return {
title: m[2],
content: osat[i],
level: Math.min(m[1].length, m[3].length)
};
});
osot.push({ title: "LUOKAT", content: classes, level: 0 });
return osot;
};
}());
(function () {
/**
* Artikkeleiden yhdistäjä.
**/
window.articleMerger = {};
/**
* Palauttaa +1, jos title1 tulee ennen title2:ta;
* -1, jos title2 tulee ennen title1:tä;
* 0, jos otsikot ovat samat.
**/
function compare(sect1, sect2) {
console.assert ( sect1 && sect2, "Virheelliset parametrit" );
console.assert ( sect1.level === sect2.level, "Eritasoiset otsikot" );
return sectOrder.compareTitles(sect1, sect2);
}
/**
* Tarkistaa onko annettu osio tyhjä (sisältää vain tyhjiä merkkejä).
* @param sect: Osiotaulukon alkio, esim. { title: "otsikko", level: 3, content: "sisältö" }
**/
function isEmpty(sect) {
console.assert ( sect, "Virheellinen parametri" );
return sect.content.match(/^[ \n\t]*$/);
}
/**
* Poistaa tyhjät osiot unmerged-osiotaulukosta.
**/
function removeEmpty(sects) {
var top,
out = [],
level = -1;
top = sects.pop();
while ( top ) {
if ( !isEmpty(top) || top.level <= level ) {
out.push(top);
level = top.level - 1;
}
top = sects.pop();
}
return out.reverse();
}
/**
* @param sects1: <osio-[]> Alkuperäisen sivun osiot.
* @param sects2: <osio-[]> Lisättävät osiot.
* @param settings: <str> Yhdistysmoodi:
* "2": Ei yhdistetä mahdollisen olemassa olevan kieliosion kanssa.
* "3": Yhdistetään kieliosiot, muttei sanaluokkaosiota, jos
* olemassa.
* "3+": Lisätään olemassa olevan sanaluokkaosion perään.
* "4": Yhdistetään kieli- ja sanaluokkaosiot, muttei alempia.
* "5": Yhdistetään kieli-, sanaluokka- ja 4-tason osiot, muttei
* alempia.
* @return: <{}> jossa .merged sisältää uuden sivun osiot,
* .unmerged sisältää osiot, joita ei voitu yhdistää
**/
function mergeSects(sects1, sects2, mode) {
var merged = [], // Yhdistetyt osiot
unmerged = [], // Osiot, joita ei voitu yhdistää.
top1,
top2,
cmp,
base,
addOnConflict, // Lisätäänkö uusi osio konfliktitilanteessa edellisen perään.
mergeLevel; // Taso, johon asti osiot yhdistetään.
if ( mode ) {
mode = mode + "";
} else {
mode = "3"; // Oletus. Yhdistetään kieliosiot, muttei sanaluokkaosioita.
}
mergeLevel = parseInt(mode);
addOnConflict = (mode.substring(mode.length-1) === "+");
// Tehdään taulukoista pinot, josta saa osion kerrallaan ylhäältä
// alaspäin pop()lla.
sects1 = sects1.reverse();
sects2 = sects2.reverse();
// Ensimmäiset osiot. Pitää olla artikkelitason osio (level = 0).
top1 = sects1.pop();
top2 = sects2.pop();
while ( top1 || top2 ) {
if ( !top2 ) {
merged.push(top1);
top1 = sects1.pop();
} else if ( !top1 ) {
merged.push(top2);
top2 = sects2.pop();
} else if ( top1.level > top2.level ) {
merged.push(top1);
top1 = sects1.pop();
} else if ( top2.level > top1.level ) {
merged.push(top2);
top2 = sects2.pop();
} else {
cmp = compare(top1, top2);
if ( cmp < 0 ) { // Alkuperäinen ensin
base = top1.level;
do {
merged.push(top1);
top1 = sects1.pop();
} while ( top1 && top1.level > base );
} else if ( cmp > 0 ) { // Uusi ensin
base = top2.level;
do {
merged.push(top2);
top2 = sects2.pop();
} while ( top2 && top2.level > base );
} else { // Otsikot samat.
// Yhdistetään osiot tai jätetään yhdistämättä riippuen osion
// tasosta.
if ( top1.level < mergeLevel ) { // Jos taso 2, käsitellään alaosiot erikseen.
// Tarkistetaan muuttuuko osion alkuosa (osa ennen ensimmäistä aliosiota).
// Jos lisättävällä osiolla on tässä sisältöä, tarkistetaan onko olemassa olevalla.
// Jos ei korvataan se uudella, muuten lisätään uusi unmergediin.
if ( !isEmpty(top2) ) {
if ( isEmpty(top1) ) {
merged.push(top2);
unmerged.push(top1); // Lisätään, jotta puurakenne pysyy. Poistetaan myöhemmin,
// jos alaosioita ei tule.
} else {
merged.push(top1);
unmerged.push(top2);
}
} else {
merged.push(top1);
unmerged.push(top2);
}
top1 = sects1.pop();
top2 = sects2.pop();
} else { // 3:s tasosta ylöspäin jätetään yhdistämättä.
base = top1.level;
do {
merged.push(top1);
top1 = sects1.pop();
} while ( top1 && top1.level > base );
if ( addOnConflict ) {
do {
merged.push(top2);
top2 = sects2.pop();
} while ( top2 && top2.level > base );
} else {
do {
unmerged.push(top2);
top2 = sects2.pop();
} while ( top2 && top2.level > base );
}
}
}
}
}
return { merged: merged, unmerged: removeEmpty(unmerged) };
}
/**
* Toistaa merkkijonoa str times kertaa.
**/
function repeat(str, times) {
return (new Array(times+1)).join(str);
}
/**
* Tulostaa osiotaulukon tekstinä.
**/
function sectsToText(sects) {
var output = [],
level,
i;
for ( i = 0; i < sects.length; i++ ) {
if ( sects[i].level > 0 ) {
level = repeat("=", sects[i].level);
output.push(level);
output.push(sects[i].title);
output.push(level);
output.push("\n");
}
// Varmistetaan, että osioiden väliin jää tyhjä rivi (paitsi, jos osio on tyhjä).
if ( sects[i].content !== "" && sects[i].content !== "\n" ) {
sects[i].content = sects[i].content.replace(/\n*$/, "\n\n");
}
output.push(sects[i].content);
}
return output.join("");
}
/**
* Yhdistää annetut tekstit ja palauttaa yhdistetyn ja yhdistämättömän osan.
* @param text1: <str> Alkuperäinen teksti.
* @param text2: <str> Siihen yhdistettävä teksti.
* @param mode: <str> Yhdistysmoodi.
* @return: <{}>, jossa .merged <str> yhdistetty teksti, .unmerged <str>
* osat, joita ei saatu yhdistettyä.
**/
articleMerger.mergeText = function(text1, text2, mode) {
var sects1 = sectParser.parseText(text1),
sects2,
result;
sects2 = sectParser.parseText(text2);
result = mergeSects(sects1, sects2, mode);;
if ( result.unmerged.length > 0 ) {
return { merged: sectsToText(result.merged), unmerged: sectsToText(result.unmerged) };
} else {
return { merged: sectsToText(result.merged), unmerged: null };
}
};
/**
* Skrollaa annetulle tekstilaatikon riville.
**/
function scrollToLine($textarea, lineNumber) {
var lineHeight = parseInt($textarea.css('line-height'));
$textarea.scrollTop(lineNumber * lineHeight);
}
/**
* Palauttaa merkkijonojen str1 ja str2 yhteisen alun.
**/
function getCommonPrefix(str1, str2) {
var l = Math.min(str1.length, str2.length),
i = 0;
while ( i < l && str1.charAt(i)=== str2.charAt(i)) i++;
return str1.substring(0, i);
}
/**
* Palauttaa ensimmäisen muuttuneen rivin indeksin.
**/
function getFirstChangedLine(str1, str2) {
var pref = getCommonPrefix(str1, str2),
a = pref.split("\n");
return a.length - 1;
}
/**
* Yhdistää annetun tekstin editointilaatikon artikkeliin moodilla mode.
* @param text2: Yhdistettävä artikkeli. TODO tarkistus, että kokonainen
* @param mode: Yhdistysmoodi.
**/
articleMerger.mergeToArticle = function (text2, mode) {
var $textbox = $("#wpTextbox1"),
$divMain = $("#articleMerger"),
$taUnmerged = $("#articleMerger-unmerged"),
$statusImg,
$statusText,
orig = $textbox.val(),
result = articleMerger.mergeText(orig, text2, mode); // Alkuperäinen teksti.
window.articleMerger.originalText = $textbox.val();
if ( $divMain.length === 0 ) {
$divMain = $('<div id="articleMerger" style="border-bottom-width: 1px; border-bottom-style: solid;">'
+ '<img src=""/>'
+ '<span id="articleMerger-status">'
+ '</span>'
+ '</div>');
$("#wpTextbox1").before($divMain);
}
$statusText = $divMain.find("span");
$statusImg = $divMain.find("img");
if ( $taUnmerged.length === 0 ) {
$taUnmerged = $('<textarea id="articleMerger-unmerged" rows="10" '
+ 'style="background-color: blanchedalmond; margin-bottom: 5px; border-tyle: inset; border-width: 2px;">'
+ '</textarea>');
$divMain.append($taUnmerged);
$taUnmerged.hide();
}
$taUnmerged.val(result.unmerged);
if ( result.unmerged ) {
$taUnmerged.show();
$statusText.text(" " + "Seuraavia osioita ei voitu yhdistää automaattisesti: ");
$statusImg.attr(
'src',
'//upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Symbol_opinion_vote.svg/15px-Symbol_opinion_vote.svg.png'
);
} else {
$taUnmerged.hide();
$statusText.text(" " + "Artikkelipohja yhdistetty artikkeliin onnistuneesti!");
$statusImg.attr(
'src',
'https://upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Symbol_keep_vote.svg/15px-Symbol_keep_vote.svg.png'
);
}
$textbox.val(result.merged);
scrollToLine($textbox, getFirstChangedLine(orig, result.merged));
};
}());
mw.loader.using( 'mediawiki.api' ).then(function () {
/**
* Yhdistettävän artikkelin lataus.
**/
window.mergeLoader = {};
/**
* Palauttaa urlin parametrit assosiatiivisena taulukkona.
**/
function getParams() {
var params = {},
i,
list = window.location.search.substring(1).split(/[&=]/);
for ( i = 0; i < list.length; i += 2 ) {
key = decodeURIComponent(list[i].replace(/\+/g, " "));
val = decodeURIComponent(list[i + 1].replace(/\+/g, " "));
if ( key.substring(key.length - 2) === '[]' ) {
key = key.substring(0, key.length - 2);
if ( !(key in params) ) {
params[key] = [];
}
params[key].push(val);
} else {
params[key] = val;
}
}
return params;
}
/**
* Tekee parametrien korvaukset ym. preloadattuun tekstiin.
**/
function preparePreload(content, preloadparams) {
// Poistetaan noinclude- ja include-tagit, muttei niiden sisältöä.
content = content.replace(/<\/?noinclude\/?>/g, '');
content = content.replace(/<\/?includeonly\/?>/g, '');
// Korvataan muuttujanimet arvoilla.
content = content.replace(/\$([0-9]+)/g, function (m, n) {
var index = n - 1;
if ( preloadparams.length > index ) {
return preloadparams[index];
} else {
// Palautetaan muuttujanimi, jos parametreja on liian
// vähän.
return m;
}
});
return content + "\n\n";
}
/**
* Lataa tekstin, korvaa muuttujat ja kutsuu funktiota f_callback sillä.
* @param: title <string> Ladattavan preload-pohjan nimi.
**/
mergeLoader.forPreloadedText = function (title) {
return new Promise(function (resolve, reject) {
var api = new mw.Api({
ajax: {
headers: {
'Api-User-Agent': 'Artikkeliyhdistäjä/0.2'
}
}
});
console.assert(title, "Virheelliset parametrit");
api.get({
format: 'json',
prop: 'revisions',
rvprop: 'content',
titles: title
}).done(function (data) {
var curPageData = (data && data.query && data.query.pages &&
data.query.pages[Object.keys(data.query.pages) [0]]),
content = (curPageData && curPageData.revisions &&
curPageData.revisions.length > 0 && curPageData.revisions[0]['*']);
if ( content ) {
resolve(content);
} else {
reject("Artikkelia ei ole")
}
});
});
};
/**
* Yhdistää annetun artikkelin nykyiseen, kun ollaan editointinäkymässä.
* @param title: Yhdistettävän artikkelin nimi.
* @param mode: (valinnainen, oletus = "3") Yhdistämismoodi (taso).
* @param preloadparams: (valinnainen) $n-merkit korvaavat arvot.
**/
mergeLoader.mergeWith = function (title, mode, preloadparams) {
preloadparams = preloadparams || [];
mode = mode || "3";
mergeLoader.forPreloadedText(title)
.then(function (content) {
content = preparePreload(content, preloadparams);
articleMerger.mergeToArticle(content, mode);
}).catch(function (reason) {
$("#mw-content-text").prepend($('<div class="errorbox">'
+ '<strong>Artikkelin <a href="/w/index.php?title='
+ encodeURIComponent(title)
+ '&action=edit&redlink=1" class="new">'
+ title
+ '</a> lataaminen ei onnistunut</strong>. '
+ 'Syy: ' + reason + '.'
+ '</div>'));
});
};
// Käynnistetään vain, jos muokataan olemassa olevaa sivua. Jos luodaan
// uutta sivua, annetaan sisäisen preload-systeemin hoitaa.
var params = getParams();
if ( params && params.redlink !== "1" ) {
(function () {
if ( params.preload ) {
mergeLoader.mergeWith(params.preload, params.mergemode, params.preloadparams);
}
})();
}
});
}());