User:Teester/CheckShex.js

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/**
 * CheckShex.js adds an input box to a wikidata page wherein you can enter an entityschema 
 * (such as E10).  When you clikc "Check", it uses pyshexy to validate the entity against
 * the schema and displays whether the entity passes or fails. 
 **/

pyshexy_stylesheet = pyshexy_getStylesheet();
$('html > head').append("<style>" + pyshexy_stylesheet + "</style>");

var pyshexy_entitySchema;
var pyshexy_entity;

if (document.location.pathname.includes("EntitySchema")) {
	// Get the entitySchema of the page
	var pyshexy_entityschema_html = '<span><span id="simpleSearch"><span><input type="text" id="entityToCheck" placeholder="Enter an entity to check e.g.Q42"><input type="submit" id="schemaSearchButton" class="searchButton" onClick="pyshexy_update()" name="check" value="Check"></span></span><span id="entityCheckResponse"></span></span>';
	var pyshexy_entitySchemaTitle = $(".entityschema-title-id" )[0].innerText;
	pyshexy_entitySchema = pyshexy_entitySchemaTitle.substring(1, pyshexy_entitySchemaTitle.length-1);
	 
	$(".entityschema-schema-text-links" ).append( pyshexy_entityschema_html );
	pyshexy_autocomplete(document.getElementById("entityToCheck"));
}

var pyshexy_conditions = ["/wiki/Q", "/wiki/P", "/wiki/L"];
if (pyshexy_conditions.some(el => document.location.pathname.includes(el))) {
	var pyshexy_entity_html = '<div><span id="simpleSearch"><span><input type="text" id="entityToCheck" placeholder="Enter a schema to check against e.g. E10"><input type="submit" id="schemaSearchButton" class="searchButton" onClick="pyshexy_update()" name="check" value="Check"></span></span><span id="entityCheckResponse"></span></div>';
	var titleId = $(".wikibase-title-id" )[0];
	var pyshexy_entityTitle = titleId ? titleId.innerText : '';
	pyshexy_entity = pyshexy_entityTitle.substring(1, pyshexy_entityTitle.length-1);
	if (document.location.pathname.includes("/wiki/L")) {
		$(".mw-indicators" ).append( pyshexy_entity_html );
	} else {
		$(".wikibase-entitytermsview-heading" ).append( pyshexy_entity_html );
	}
}

function pyshexy_update() {
	if (pyshexy_entitySchema) {
		if ($("#entityToCheck")[0].attributes.wikidata) {
			pyshexy_entity = $("#entityToCheck")[0].attributes.wikidata.value;
		} else {
			pyshexy_entitySchema = $("#entityToCheck")[0].value.toUpperCase();
		}
	} else {
		pyshexy_entitySchema = $("#entityToCheck")[0].value.toUpperCase();
	}
	if (pyshexy_entitySchema && pyshexy_entity) {
		pyshexy_checkEntity(pyshexy_entitySchema, pyshexy_entity);
	}
}

function pyshexy_checkEntity(entitySchema, entity) {
	$("#entityCheckResponse").contents().remove();
	var url = "https://tools.wmflabs.org/pyshexy/api?entityschema=" + entitySchema + "&entity=" + entity;
	$.ajax({ 
		type: "GET",
		dataType: "json",
		url: url,
		success: function(data){      
			var html = "";
			if (data.results[0].result) {
				html = '<span class="response"><span class="pass">✔</span><span> Pass</span></span>';
			} else {
				html = pyshexy_parseResult(data.results[0]);
				console.log(data.results[0].reason);
			}
			$("#entityCheckResponse" ).append( html );
		},
		error: function(data) {
			$("#entityCheckResponse" ).append( '<span>Unable to validate schema</span>' );
		}
	});
}

function pyshexy_getStylesheet() {
    var stylesheet = "#schemaSearchButton { background-position: center center; background-repeat: no-repeat; position: absolute; top:0; right:0; overflow: hidden; height:100%; background-color: #1E90FF !important; color: #FFFFFF !important; padding: 2px !important}";
    stylesheet += "#entityToCheck { padding: 1em; margin:0; width: 100%;}";
    stylesheet += ".response { padding: 2px;}";
    stylesheet += ".pass { color: #008800; }";
    stylesheet += ".fail { color: #CC0000; }";
    stylesheet += ".missing { background-color: #CC0000; color: #ffffff; padding:2px; margin: 2px; font-size:75%; border-radius:2px;}";
    return stylesheet;
}

function pyshexy_parseResult(data) {
	var property = [];
	var html = '<span class="response" title="' + data.reason + '"><span class="fail">✘</span><span class="response"> Fail</span>';
	var no_matching_triples = "No matching triples found for predicate";
	if (data.reason.includes(no_matching_triples)) {
		property = data.reason.match(/P\d+/g);
	}
	if (property !== null) {
		property = property.reduce(function(a,b){if(a.indexOf(b)<0)a.push(b);return a;},[]);
		for (var i = 0; i < property.length; i++) {
			html += '<span class="missing"> Missing valid ' + property[i] + '</span>';
		}
	}
	html += "</span>";
	return html;
}

/**
 * This function handles all aspects of Autocomplete
 * 
 **/
function pyshexy_autocomplete(inp) {
    /*the autocomplete function takes two arguments,
    the text field element and an array of possible autocompleted values:*/
    var currentFocus;
    /*execute a function when someone writes in the text field:*/
    inp.addEventListener("input", function(e) {
        var a, b, i, val = this.value;
        /*close any already open lists of autocompleted values*/
        closeAllLists();
        if (!val) { return false;}
        currentFocus = -1;
        /*create a DIV element that will contain the items (values):*/
        a = document.createElement("DIV");
        a.setAttribute("id", this.id + "autocomplete-list");
        a.setAttribute("class", "autocomplete-items ui-suggester-list");
        /*append the DIV element as a child of the autocomplete container:*/
        this.parentNode.appendChild(a);
        if (val.length>2){
            // get array
            $.ajax({
                type: "GET",
                dataType: "json",
                url: "https://www.wikidata.org/w/api.php?action=wbsearchentities&search="+val+"&language=en&origin=*&format=json",
                success: function(data){
                    arr = data.search;
                    /*for each item in the array...*/
                    for (i = 0; i < arr.length; i++) {
                        /*check if the item starts with the same letters as the text field value:*/
                        /*create a DIV element for each matching element:*/
                        b = document.createElement("DIV");
                        /*make the matching letters bold:*/
                        b.innerHTML = "<span class='autocomplete-label ui-entityselector-label'>" + arr[i].label + "</span>";
                        if (arr[i].description) {
                            b.innerHTML += "<br/><span class='description ui-entityselector-description'>" + arr[i].description + "</span>";
                        }
                        /*insert a input field that will hold the current array item's value:*/
                        b.innerHTML += "<input type='hidden' value='" + arr[i].label + " (" + arr[i].id + ")'>";
                        b.innerHTML += "<input type='hidden' value='" + arr[i].id + "'>";
                        /*execute a function when someone clicks on the item value (DIV element):*/
                        b.addEventListener("click", function(e) {
                            /*insert the value for the autocomplete text field:*/
                            inp.value = this.getElementsByTagName("input")[0].value;
                            inp.setAttribute("wikidata", this.getElementsByTagName("input")[1].value);
                            /*close the list of autocompleted values,
                            (or any other open lists of autocompleted values:*/
                            closeAllLists();
                        });
                        a.appendChild(b);
                    }
                } 
            });
        }   
    });
    /*execute a function presses a key on the keyboard:*/
    inp.addEventListener("keydown", function(e) {
        var x = document.getElementById(this.id + "autocomplete-list");
        if (x) x = x.getElementsByTagName("div");
        if (e.keyCode == 40) {
            /*If the arrow DOWN key is pressed,
            increase the currentFocus variable:*/
            currentFocus++;
            /*and and make the current item more visible:*/
            addActive(x);
        } else if (e.keyCode == 38) { //up
            /*If the arrow UP key is pressed,
            decrease the currentFocus variable:*/
            currentFocus--;
            /*and and make the current item more visible:*/
            addActive(x);
        } else if (e.keyCode == 13) {
            /*If the ENTER key is pressed, prevent the form from being submitted,*/
            e.preventDefault();
            if (currentFocus > -1) {
                /*and simulate a click on the "active" item:*/
                if (x) x[currentFocus].click();
            }
        }
    });
    function addActive(x) {
        /*a function to classify an item as "active":*/
        if (!x) return false;
        /*start by removing the "active" class on all items:*/
        removeActive(x);
        if (currentFocus >= x.length) currentFocus = 0;
        if (currentFocus < 0) currentFocus = (x.length - 1);
        /*add class "autocomplete-active":*/
        x[currentFocus].classList.add("autocomplete-active");
    }
    function removeActive(x) {
        /*a function to remove the "active" class from all autocomplete items:*/
        for (var i = 0; i < x.length; i++) {
            x[i].classList.remove("autocomplete-active");
        }
    }
    function closeAllLists(elmnt) {
        /*close all autocomplete lists in the document,
        except the one passed as an argument:*/
        var x = document.getElementsByClassName("autocomplete-items");
        for (var i = 0; i < x.length; i++) {
            if (elmnt != x[i] && elmnt != inp) {
                x[i].parentNode.removeChild(x[i]);
            }
        }
    }
    /*execute a function when someone clicks in the document:*/
    document.addEventListener("click", function (e) {
        closeAllLists(e.target);
    });
}