Last active
March 27, 2020 21:41
-
-
Save fuddl/3fab12d74b52bb9b150d89ead0ac114c to your computer and use it in GitHub Desktop.
Userscript Wikia → Wikidata
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name WikiaData | |
// @namespace https://gist.github.com/fuddl | |
// @version 1.0.7 | |
// @description Enrich Wikia with wikidata ids. Enrich wikidata with wikia ids. | |
// @updateURL https://gist.githubusercontent.com/fuddl/3fab12d74b52bb9b150d89ead0ac114c/raw/wikia-data.user.js | |
// @downloadURL https://gist.githubusercontent.com/fuddl/3fab12d74b52bb9b150d89ead0ac114c/raw/wikia-data.user.js | |
// @author fuddl | |
// @match https://*.fandom.com/* | |
// @grant GM_xmlhttpRequest | |
// ==/UserScript== | |
(function() { | |
let mode = 'browsing'; | |
let style = document.createElement('style'); | |
let pageQid = ''; | |
let selectedProperty = ''; | |
let flipped = false; | |
style.appendChild(document.createTextNode('.wikia-module [type="search"] { border: 1px solid #a2a9b1; border-radius: 2px; padding: 0.4em 1.84615385em 0.4em 0.4em; width: 100% }')); | |
style.appendChild(document.createTextNode('.wikidata-entity-list { margin: 1em 0; padding-left: 1em;} .wikidata-entity-list > li { list-style-type: initial; }')); | |
style.appendChild(document.createTextNode('.wikidata-link::before { content: " (" }')); | |
style.appendChild(document.createTextNode('.wikidata-link::after { content: ")" }')); | |
style.appendChild(document.createTextNode('.mode-qid-select .wikidata-link { background: #339966; color:white; border-radius: 4px; font-size: 0.45em; line-height: 16px; display: inline-block; padding: 0 5px; margin: 0 5px; vertical-align: middle; border: 1px solid white; transition: transform .25s, background .25s }')); | |
style.appendChild(document.createTextNode('.mode-qid-select .wikidata-link::before, .mode-qid-select .wikidata-link::after { display: none }')); | |
style.appendChild(document.createTextNode('.mode-qid-select .wikidata-link--active, .mode-qid-select .wikidata-link:hover { background: #006699; text-decoration: none }')); | |
style.appendChild(document.createTextNode('.mode-qid-select .wikidata-link--already-present { background: #006699 }')); | |
style.appendChild(document.createTextNode('.wikia-module__mode-qid-select { position: sticky; top: 100px } .wikia-module__mode-qid-select ~ * {display: none !important}')); | |
document.querySelector('head').appendChild(style); | |
let itemList = {}; | |
function addToItemList(id, title, section, hash) { | |
if (!(id in itemList)) { | |
itemList[id] = { | |
title: title, | |
hash: hash, | |
section: section | |
} | |
} else { | |
delete itemList[id]; | |
} | |
console.log(itemList); | |
let allLinks = document.querySelectorAll('[data-wikidata]'); | |
for (let link of allLinks) { | |
link.classList.remove('wikidata-link--active'); | |
} | |
for (let key of Object.keys(itemList)) { | |
let links = document.querySelectorAll('[data-wikidata="' + key + '"]'); | |
for (let link of links) { | |
link.classList.add('wikidata-link--active'); | |
} | |
} | |
updateItemList(); | |
updateSubmitButton(); | |
} | |
function updateItemList() { | |
let list = document.querySelector('.wikidata-entity-list'); | |
list.innerHTML = ''; | |
if (list != null && Object.entries(itemList).length > 0) { | |
for (let key of Object.keys(itemList)) { | |
let listItem = document.createElement('li'); | |
listItem.innerText = itemList[key].title; | |
list.appendChild(listItem); | |
} | |
} | |
} | |
function updateSubmitButton() { | |
let button = document.querySelector('.wikidata-add-quickstatements'); | |
if (Object.entries(itemList).length > 0) { | |
button.removeAttribute('hidden'); | |
let statements = []; | |
for (let key of Object.keys(itemList)) { | |
let item = itemList[key]; | |
let srcUrl = location.href + '?oldid=' + mw.config.values.wgRevisionId; | |
if (item.hash) { | |
srcUrl = srcUrl + '#' + item.hash; | |
} | |
let statement = [ | |
!flipped ? pageQid : key, selectedProperty, !flipped ? key : pageQid, | |
'S854', '"' + srcUrl + '"', | |
's813', getCurrentDate(), | |
's1810', '"' + item.title + '"', | |
]; | |
if (item.section != null) { | |
statement.push('s958', '"' + item.section + '"'); | |
} | |
statements.push(statement.join('|')); | |
} | |
localStorage.setItem('lastProp', selectedProperty); | |
button.setAttribute('href', 'https://tools.wmflabs.org/quickstatements/#/v1=' + encodeURIComponent(statements.join('||'))); | |
} else { | |
button.setAttribute('hidden', true); | |
} | |
} | |
function getPageTitle() { | |
return document.querySelector('.page-header__title').innerText; | |
} | |
function getCurrentDate() { | |
let now = new Date(); | |
return '+' + now.toISOString().substring(0,10) + 'T00:00:00Z/11' | |
} | |
function getClosestHeadline(element) { | |
let subject = element; | |
while (element.parentNode && !element.parentNode.classList.contains('mw-content-text')) { | |
element = element.parentNode; | |
} | |
while (element.previousElementSibling && !element.matches('h1, h2, h3, h4, h5, h6')) { | |
element = element.previousElementSibling; | |
} | |
if (element.children.length > 0 && element.children[0].matches('.mw-headline')) { | |
let span = element.children[0]; | |
return { | |
section: span.innerText, | |
hash: span.getAttribute('id'), | |
}; | |
} | |
return {section: null, hash: null}; | |
} | |
function setUpConnecterBox(target) { | |
let moduleTitle = document.createElement('h2'); | |
moduleTitle.innerText = 'Connect to Wikidata'; | |
let moduleTitleIcon = document.createElement('img'); | |
moduleTitleIcon.setAttribute('src', 'https://upload.wikimedia.org/wikipedia/commons/f/ff/Wikidata-logo.svg'); | |
moduleTitleIcon.classList.add('wds-icon'); | |
moduleTitleIcon.classList.add('wds-icon-small'); | |
moduleTitle.classList.add('has-icon'); | |
moduleTitle.prepend(moduleTitleIcon); | |
style.appendChild(document.createTextNode('.wikia-module .wds-icon { margin-right: 7px }')); | |
style.appendChild(document.createTextNode('.WikiaRail .wikia-module h2 { color: inherit; border-bottom: 1px solid #a2a9b1; }')); | |
let module = document.createElement('div'); | |
module.classList.add('rail-module'); | |
module.classList.add('wikia-module'); | |
module.appendChild(moduleTitle); | |
style.appendChild(document.createTextNode('.wikia-module { border: 1px solid #a7d7f9; background: white; padding: 1em; color: #222222; font-family: sans-serif; }')); | |
let rail = document.getElementById('WikiaRail'); | |
rail.prepend(module); | |
if (typeof target != 'string') { | |
let query = document.createElement('input'); | |
query.setAttribute('type', 'search'); | |
let title = getPageTitle(); | |
query.value = title; | |
module.appendChild(query); | |
let suggestions = document.createElement('ol'); | |
suggestions.classList.add('wikia-suggestions'); | |
style.appendChild(document.createTextNode('.wikia-suggestions > li { margin-top: .5em }')); | |
style.appendChild(document.createTextNode('.wikia-suggestions__link { color: inherit; display: block; padding: 1em; border: 1px solid #C9C9C9 } .wikia-suggestions__link:hover { background-color: #4C59A6; color: white; text-decoration: none }')); | |
style.appendChild(document.createTextNode('.wikia-suggestions__label { font-weight: bold }')); | |
module.appendChild(suggestions); | |
let updateSuggestions = function() { | |
let request = new XMLHttpRequest(); | |
GM_xmlhttpRequest({ | |
method: "GET", | |
url: labelQuery(query.value), | |
onload: function(response) { | |
suggestions.innerText = ''; | |
let answer = JSON.parse(response.responseText); | |
answer.search.push({ | |
label: getPageTitle(), | |
description: "Create a new entity", | |
id: null, | |
}); | |
for (let suggestion of answer.search) { | |
let item = document.createElement('li'); | |
let link = document.createElement('a'); | |
link.classList.add('wikia-suggestions__link'); | |
let title = document.createElement('div'); | |
title.classList.add('wikia-suggestions__label'); | |
title.innerText = suggestion.label; | |
let desc = document.createElement('div'); | |
desc.innerText = suggestion.description; | |
link.href = createConnecterQS(getWikiaId(location), suggestion.id); | |
link.appendChild(title); | |
link.appendChild(desc); | |
item.appendChild(link); | |
suggestions.appendChild(item); | |
} | |
} | |
}); | |
} | |
query.addEventListener('focus', updateSuggestions); | |
query.addEventListener('keyup', updateSuggestions); | |
query.addEventListener('change', updateSuggestions); | |
updateSuggestions(); | |
} else { | |
let propsUrl = 'https://tools.wmflabs.org/hay/propbrowse/props.json'; | |
GM_xmlhttpRequest({ | |
method: "GET", | |
url: propsUrl, | |
onload: function(response) { | |
let propSelector = document.createElement('input'); | |
propSelector.setAttribute('type', "search") | |
propSelector.setAttribute('list', "wikidata-property-list"); | |
propSelector.setAttribute('placeholder', "add statement to " + getPageTitle()); | |
module.appendChild(propSelector); | |
let datalist = document.createElement('datalist'); | |
datalist.setAttribute('id', "wikidata-property-list"); | |
module.appendChild(datalist); | |
let flipCheckbox = document.createElement('input'); | |
flipCheckbox.setAttribute('type', 'checkbox'); | |
flipCheckbox.setAttribute('id', 'wikidata-page-is-object'); | |
module.appendChild(flipCheckbox); | |
let flipCheckboxLabel = document.createElement('label'); | |
flipCheckboxLabel.setAttribute('for', 'wikidata-page-is-object'); | |
flipCheckboxLabel.innerText = 'treat as object of statement.' | |
module.appendChild(flipCheckboxLabel); | |
flipCheckbox.addEventListener('change', function() { | |
flipped = flipCheckbox.checked ? true : false; | |
}); | |
let itemList = document.createElement('ul'); | |
itemList.classList.add('wikidata-entity-list'); | |
module.appendChild(itemList); | |
let submit = document.createElement('a'); | |
submit.classList.add('wikidata-add-quickstatements'); | |
submit.setAttribute('hidden', true); | |
submit.innerText = 'Send to Quickstatements'; | |
module.appendChild(submit); | |
let props = JSON.parse(response.responseText); | |
for (let prop of props) { | |
if (prop.datatype === 'wikibase-item') { | |
let option = document.createElement('option'); | |
option.setAttribute('value', prop.label + ' (' + prop.id + ')'); | |
datalist.appendChild(option); | |
} | |
} | |
let updatePropSelector = function() { | |
if (propSelector.value.match(/(P\d+)(\)|$)/)) { | |
mode = 'qid-select'; | |
selectedProperty = propSelector.value.match(/(P\d+)(\)|$)/)[1]; | |
document.querySelector('.WikiaArticle').classList.add('mode-qid-select'); | |
document.querySelector('.wikia-module').classList.add('wikia-module__mode-qid-select'); | |
checkIfStatementIsAlreadyPresent(); | |
} else { | |
mode = 'browsing'; | |
document.querySelector('.WikiaArticle').classList.remove('mode-qid-select'); | |
} | |
} | |
propSelector.addEventListener('change', updatePropSelector); | |
propSelector.addEventListener('keyup', updatePropSelector); | |
propSelector.addEventListener('blur', updatePropSelector); | |
propSelector.addEventListener('click', function() { | |
if (localStorage.getItem('lastProp') && propSelector.value == '') { | |
propSelector.value = localStorage.getItem('lastProp'); | |
propSelector.setSelectionRange(0, propSelector.value.length); | |
updatePropSelector(); | |
} | |
}); | |
} | |
}); | |
} | |
} | |
function createConnecterQS(id, qid) { | |
let prefix = ''; | |
if (qid === null) { | |
qid = "LAST"; | |
prefix = 'CREATE||' + [qid, "L" + document.documentElement.lang, '"' + getPageTitle() + '"'].join('|') + '||'; | |
} | |
let statement = [ | |
qid, 'P6262', '"' + id + '"', | |
'S854', '"' + location.href + '?oldid=' + mw.config.values.wgRevisionId + '"', | |
's813', getCurrentDate(), | |
's1810', '"' + getPageTitle() + '"', | |
]; | |
return 'https://tools.wmflabs.org/quickstatements/#/v1=' + encodeURIComponent(prefix + statement.join('|')); | |
} | |
function labelQuery(input) { | |
return "https://www.wikidata.org/w/api.php?action=wbsearchentities&limit=20&language=" + document.documentElement.lang + "&format=json&search=" + input; | |
} | |
function getWikiaId(location) { | |
let subdomain = location.host.split('.')[0]; | |
let languagePrefixExtract = location.pathname.match(/^\/([a-z]{2})\//); | |
let languagePrefix = ''; | |
if (languagePrefixExtract && languagePrefixExtract.length == 2) { | |
languagePrefix = languagePrefixExtract[1] + '.'; | |
} | |
let articleTitle = decodeURI(location.pathname.match(/\/wiki\/(.*)$/)[1]).replace(' ', '_'); | |
return languagePrefix + subdomain + ':' + articleTitle; | |
} | |
function getQidFromWikiaId(wikiaId, callback, failCallback) { | |
let query = "SELECT ?item ?itemLabel"; | |
query += ' WHERE { ?item wdt:P6262 "' + wikiaId + '". SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". } } #1'; | |
let queryUrl = 'https://query.wikidata.org/sparql?format=json&query=' + encodeURIComponent(query); | |
let request = new XMLHttpRequest(); | |
request.open('GET', queryUrl); | |
request.send(); | |
request.onreadystatechange = function(request) { | |
let response = request.target; | |
if (response.readyState == 4 && response.status === 200) { | |
let answer = JSON.parse(response.responseText); | |
if (answer.results.bindings[0]) { | |
let wdurl = answer.results.bindings[0].item.value; | |
let wdId = wdurl.replace("http://www.wikidata.org/entity/", ''); | |
callback({wdurl: wdurl, wdId: wdId}); | |
} else if (typeof failCallback === "function") { | |
failCallback(); | |
} | |
} | |
if (response.readyState == 4 && response.status > 400) { | |
setTimeout(function() { | |
request.send(); | |
}, 1000); | |
} | |
}; | |
} | |
function addLinkToTitle() { | |
let currentLocation = window.location; | |
let wikiaId = getWikiaId(currentLocation); | |
getQidFromWikiaId(wikiaId, function(answer) { | |
let link = document.createElement('a'); | |
link.setAttribute('href', answer.wdurl); | |
link.innerText = " (" + answer.wdId + ")"; | |
let title = document.querySelector("h1"); | |
title.appendChild(link); | |
setUpConnecterBox(answer.wdId); | |
pageQid = answer.wdId; | |
}, function() { | |
setUpConnecterBox(); | |
}); | |
} | |
function addLinkToLink(link) { | |
let wikiaId = getWikiaId(link); | |
getQidFromWikiaId(wikiaId, function(answer) { | |
let appendlink = document.createElement('a'); | |
appendlink.setAttribute('href', answer.wdurl); | |
appendlink.innerText = answer.wdId; | |
appendlink.setAttribute('data-wikidata', answer.wdId); | |
appendlink.setAttribute('data-title', link.innerText); | |
appendlink.classList.add('wikidata-link'); | |
link.parentElement.insertBefore(appendlink, link.nextSibling); | |
appendlink.addEventListener('click', function(e){ | |
if (mode == 'qid-select') { | |
e.preventDefault(); | |
let title = getClosestHeadline(link); | |
addToItemList(answer.wdId, link.innerText, title.section, title.hash, link); | |
} | |
}); | |
}); | |
} | |
function checkIfStatementIsAlreadyPresent() { | |
let allLinks = document.querySelectorAll('[data-wikidata]'); | |
for (let link of allLinks) { | |
link.classList.remove('wikidata-link--already-present'); | |
} | |
for (let link of allLinks) { | |
let subject = pageQid; | |
let object = link.getAttribute('data-wikidata'); | |
let url = location.href.replace(/(\?|\#).*/,''); | |
let prop = selectedProperty; | |
let query = "ASK { wd:" + subject + " p:" + prop + " ?stmt . ?stmt ps:" + prop + " wd:" + object + " . ?stmt prov:wasDerivedFrom ?ref . ?ref pr:P854 ?url . FILTER(STRSTARTS(str(?url), '" + url + "')) }"; | |
let queryUrl = 'https://query.wikidata.org/sparql?format=json&query=' + encodeURIComponent(query); | |
let request = new XMLHttpRequest(); | |
request.open('GET', queryUrl); | |
request.send(); | |
request.onreadystatechange = function(request) { | |
let response = request.target; | |
if (response.readyState == 4 && response.status === 200) { | |
let answer = JSON.parse(response.responseText); | |
console.log(query); | |
console.log(answer.boolean); | |
if (answer.boolean === true) { | |
link.classList.add('wikidata-link--already-present'); | |
} | |
} | |
if (response.readyState == 4 && response.status > 400) { | |
setTimeout(function() { | |
request.send(); | |
}, 1000); | |
} | |
}; | |
} | |
} | |
function addLinkToLinks() { | |
let links = document.querySelectorAll('.mw-content-text a[href^="/"]:not([href*="?action"])'); | |
let i = 0; | |
for (let link of links) { | |
setTimeout(function(){ | |
if(link.classList.contains('mw-redirect')) { | |
resolveRedirect(link, function() { | |
addLinkToLink(link); | |
}); | |
} | |
addLinkToLink(link); | |
}, i * 10); | |
i++; | |
} | |
} | |
function extractCanonocal(markup) { | |
return markup.match(/<link rel="canonical" href="([^"]+)" \/>/)[1]; | |
} | |
function resolveRedirect(link, callback) { | |
let request = new XMLHttpRequest(); | |
request.open('GET', link.href, true); | |
request.send(); | |
request.onreadystatechange = function(request) { | |
let response = request.target; | |
if (response.readyState == 4 && response.status === 200) { | |
link.href = extractCanonocal(response.responseText); | |
callback(); | |
} | |
}; | |
} | |
'use strict'; | |
addLinkToTitle(); | |
addLinkToLinks(); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment