Skip to content

Instantly share code, notes, and snippets.

@danielvy
Last active April 17, 2018 13:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danielvy/9953c722446ccbef27292534e62b117b to your computer and use it in GitHub Desktop.
Save danielvy/9953c722446ccbef27292534e62b117b to your computer and use it in GitHub Desktop.
Userscript to add addtional UI to Prodigy's NER manual interface
// ==UserScript==
// @name Prodigy NER extended info
// @version 1
// @grant none
// @include http://localhost:8080/
// @run-at document-start
// ==/UserScript==
let displayElement;
let metaDataElement;
let annotationsElement;
let metaLinkElement;
let googleSearchLinkElement;
const displayElementClassName = ".prodigy-container";
const metaDataElementClassName = ".prodigy-meta";
const annotationsElementClassName = ".prodigy-content";
function createContainer() {
let style = document.createElement("style");
style.innerHTML = css_string
document.body.appendChild(style)
let container = document.createElement("div");
container.classList.add("extended_info");
let divElement;
divElement = document.createElement("div");
divElement.innerHTML = '<a href="#" target="_blank"></a>';
metaLinkElement = divElement.firstChild;
metaLinkElement.insertAdjacentHTML("beforebegin", "Go To Youtube: ");
container.appendChild(divElement);
divElement = document.createElement("div");
divElement.innerHTML = '<a href="#" target="_blank"></a>';
googleSearchLinkElement = divElement.firstChild;
googleSearchLinkElement.insertAdjacentHTML("beforebegin", "Search Google: ");
container.appendChild(divElement);
displayElement.after(container);
}
// This will be called whenever the meta info displayed changes
function metaDataChanged() {
// The key for meta data to look for
let metaDataKeyName = "VIDEO_ID";
let span = Array.from(metaDataElement.getElementsByTagName("span")).find(span =>
(t = span.getElementsByTagName("strong")) ? t[0].textContent.trim() == metaDataKeyName + ":": false
);
let videoId;
if (span) {
videoId = (t = Array.from(span.childNodes).filter(node => node.nodeType == Node.TEXT_NODE)) ? t[0].data : null;
};
if (videoId) {
metaLinkElement.innerHTML = "video " + videoId;
metaLinkElement.setAttribute("href", "https://www.youtube.com/watch?v=" + videoId)
}
}
// This will be called whenever the annotation changes
function annotationsChanged() {
// Get an array of text in for "marked" tokens
let marked = Array.from(annotationsElement.getElementsByTagName("mark")).map( node =>
(t = Array.from(node.childNodes).filter(node => node.nodeType == Node.TEXT_NODE)) ? t[0].data : null
)
let query = marked.filter((v, i, a) => a.indexOf(v) === i).map((item, i, a) => `<span class="ner_label">${item}</span>`)
.join("&nbsp;&nbsp;&nbsp;")
googleSearchLinkElement.setAttribute("href", "https://www.google.com/search?q=" + encodeURI(marked.join(" ")));
googleSearchLinkElement.innerHTML = query;
}
// Observer for meta data changes
let metaDataChangedObserver = new MutationObserver( function(mutationsList) {
metaDataChanged();
});
// Observer for annotation changes
let annotationsChangedObserver = new MutationObserver( function(mutationsList) {
annotationsChanged();
});
let rootElement = document.getElementById("root");
// Observes when the root div initialized
let rootObserver = new MutationObserver( function(mutationsList) {
// Find Prodigy's "main" display container div (by class name)
displayElement = document.querySelector(displayElementClassName);
if (displayElement != null) {
// find the meta data div container (by class name) and set up observing
metaDataElement = document.querySelector(metaDataElementClassName);
metaDataChangedObserver.observe(metaDataElement, {subtree: true, characterData: true});
// find the annotations div container (by class name) and set up observing
annotationsElement = document.querySelector(annotationsElementClassName);
annotationsChangedObserver.observe(annotationsElement, {childList: true, subtree: true, characterData: true});
// No need to observe root anymore
rootObserver.disconnect();
// Create the container for the extended information
createContainer();
// Need to call these the first time
metaDataChanged();
annotationsChanged();
}
});
rootObserver.observe(rootElement, {childList: true, subtree: true});
const css_string = `
.extended_info {
padding: 2rem;
font-size: 2rem;
border-radius: 0;
border: 1px solid #ddd;
max-width: 675px;
min-height: 100px;
min-width: 300px;
margin: 3rem auto 0 auto;
background: #fff;
}
.extended_info a {
text-decoration: none;
border-bottom: 1px solid #c8c8c8;
transition: border 0.2s ease;
}
.extended_info a:hover {
color: #252a33;
border-bottom-color: #565656;
}
.ner_label {
box-decoration-break: clone;
color: inherit;
display: inline;
font-weight: bold;
line-height: 1;
background: rgba(255, 225, 132, .4) none repeat scroll 0% 0%;
}
`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment