MediaWiki:Gadget-CheckShex.js: verschil tussen versies

Uit Knowledge Graph Kunstenpunt
Ga naar:navigatie, zoeken
Geen bewerkingssamenvatting
Label: Ongedaan gemaakt
Geen bewerkingssamenvatting
Label: Ongedaan gemaakt
Regel 1: Regel 1:
/**
/**
  * CheckShex.js adds an input box to a wikidata page wherein you can enter an entityschema  
  * EntityShape.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
  * (such as E10).  When you click "Check", checks whether each statement and property conforms
  * the schema and displays whether the entity passes or fails.  
  * to the schema.  It then displays a summary at the top of the item for each property indicating
* whether they conform or not.  It also adds a badge to each statement and each property on the
* page indicating whether they conform or not.
  **/
  **/


pyshexy_stylesheet = pyshexy_getStylesheet();
(function() {
$('html > head').append("<style>" + pyshexy_stylesheet + "</style>");
    mw.util.addCSS(entityschema_getStylesheet());
    let entityschema_list = [];
    let value = window.localStorage.getItem("entityschema-auto");
    let schema = window.localStorage.getItem("entityschema");
    let property_list = [];
    let item_list = [];
    let entityID = mw.config.get( 'wbEntityId' );


var pyshexy_entitySchema;
    $(document).ready(function(){
var pyshexy_entity;


if (document.location.pathname.includes("EntitySchema")) {
        let entityschema_conditions = ["/wiki/Q", "/wiki/P", "/wiki/L"];
// Get the entitySchema of the page
        if (entityschema_conditions.some(el => document.location.pathname.includes(el))) {
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>';
            let entityschema_entity_html = `<div><span id="entityschema-simpleSearch">
var pyshexy_entitySchemaTitle = $(".entityschema-title-id" )[0].innerText;
                                            <span>
pyshexy_entitySchema = pyshexy_entitySchemaTitle.substring(1, pyshexy_entitySchemaTitle.length-1);
                                            <input type="text" id="entityschema-entityToCheck"
                                                  placeholder="Enter schema to check against e.g. E234"
$(".entityschema-schema-text-links" ).append( pyshexy_entityschema_html );
                                                  title="Enter 1 or more schemas to check against separated by commas e.g. E10, E236 or press Check to auto-determine schemas to check">
pyshexy_autocomplete(document.getElementById("entityToCheck"));
                                            <input type="submit" id="entityschema-schemaSearchButton"
}
                                                  class="searchButton" name="check" value="Check">
                                            <div class="entityshape-spinner" style="display:none"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div>
                                            </span>
                                            </span><input type="checkbox" id="entityschema-checkbox">
                                            <label for="entityschema-checkbox"><small>Automatically check schema</small></label>
                                            <span id="entityschemaResponse"></span></div>`;
            $(entityschema_entity_html).insertAfter("#siteSub");
            if (value == "true") {
                $("#entityschema-checkbox").prop('checked', true);
            } else {
                $("#entityschema-checkbox").prop('checked', false);
            }
            $("#entityschema-schemaSearchButton").click(function(){ entityschema_update(); });
            $("#entityschema-checkbox").click(function() { entityschema_checkbox(); });
        }
    });


var pyshexy_conditions = ["/wiki/Q", "/wiki/P", "/wiki/L"];
    mw.hook( 'wikibase.entityPage.entityLoaded' ).add( function ( data ) {
if (pyshexy_conditions.some(el => document.location.pathname.includes(el))) {
        let claims = data["claims"];
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>';
        for (let claim in claims) {
var titleId = $(".wikibase-title-id" )[0];
            property_list.push(claim);
var pyshexy_entityTitle = titleId ? titleId.innerText : '';
            let statements = claims[claim];
pyshexy_entity = pyshexy_entityTitle.substring(1, pyshexy_entityTitle.length-1);
            for (let statement in statements) {
if (document.location.pathname.includes("/wiki/L")) {
                let mainsnak = statements[statement]["mainsnak"];
$(".mw-indicators" ).append( pyshexy_entity_html );
                if (mainsnak["datavalue"] && mainsnak["datatype"] == "wikibase-item") {
} else {
                    if (!item_list.includes(mainsnak["datavalue"]["value"]["id"])) {
$(".wikibase-entitytermsview-heading" ).append( pyshexy_entity_html );
                        item_list.push(mainsnak["datavalue"]["value"]["id"]);
}
                    }
}
                }
            }
        }
        check_entity_for_schemas(item_list);
        check_entity_for_schemas(property_list);
    });


function pyshexy_update() {
    mw.hook( 'wikibase.statement.saved' ).add( function ( data ) {
if (pyshexy_entitySchema) {
        if (value == "true") {
if ($("#entityToCheck")[0].attributes.wikidata) {
            entityschema_update();
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) {
    mw.hook( 'wikibase.statement.removed' ).add( function ( data ) {
$("#entityCheckResponse").contents().remove();
        if (value == "true") {
var url = "https://tools.wmflabs.org/pyshexy/api?entityschema=" + entitySchema + "&entity=" + entity;
            entityschema_update();
$.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() {
    function check_entity_for_schemas(entity_list) {
    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}";
        for (let item in entity_list) {
    stylesheet += "#entityToCheck { padding: 1em; margin:0; width: 100%;}";
            let url = "https://kg.kunsten.be/w/api.php?action=wbgetclaims&property=P12861&format=json&entity=" + entity_list[item];
    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({
             $.ajax({
                 type: "GET",
                 type: "GET",
                 dataType: "json",
                 dataType: "json",
                 url: "https://kg.kunsten.be/w/api.php?action=wbsearchentities&search="+val+"&language=nl&origin=*&format=json",
                 url: url,
                 success: function(data){
                 success: function(data) {
                     arr = data.search;
                     if (data["claims"].hasOwnProperty("P12861")) {
                    /*for each item in the array...*/
                         for (let claim in data["claims"]["P12861"]) {
                    for (i = 0; i < arr.length; i++) {
                            entityschema_list.push(data["claims"]["P12861"][claim]["mainsnak"]["datavalue"]["value"]["id"]);
                         /*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);
                     }
                     }
                 }  
                 },
             });
             });
         }  
         }
    }
 
    $(document).ajaxStop(function () {
      if (value == "true" && mw.config.get( 'wbEntityId' )) {
        entityschema_update();
      }
      $("#entityschema-entityToCheck:text").val(schema);
      $(this).unbind('ajaxStop');
    });
 
    $("#entityschema-entityToCheck").on("keyup", function(event){
        if (event.key === "Enter") {
            event.preventDefault();
            $("#entityschema-schemaSearchButton").click();
            return false;
        }
     });
     });
     /*execute a function presses a key on the keyboard:*/
 
    inp.addEventListener("keydown", function(e) {
     function entityschema_update() {
         var x = document.getElementById(this.id + "autocomplete-list");
        let entityschema_entitySchema = $("#entityschema-entityToCheck")[0].value.toUpperCase();
         if (x) x = x.getElementsByTagName("div");
        if (entityschema_entitySchema.length == 0) {
         if (e.keyCode == 40) {
            entityschema_entitySchema = entityschema_list.join(", ");
             /*If the arrow DOWN key is pressed,
            window.localStorage.setItem("entityschema", "");
             increase the currentFocus variable:*/
        } else {
            currentFocus++;
            window.localStorage.setItem("entityschema", entityschema_entitySchema);
            /*and and make the current item more visible:*/
        }
             addActive(x);
        if (entityschema_entitySchema.length == 0) {
         } else if (e.keyCode == 38) { //up
            $("#entityschemaResponse").append( '<br/><span>No schemas entered and could not automatically determine schemas to check</span>' );
             /*If the arrow UP key is pressed,
         } else {
             decrease the currentFocus variable:*/
            let entityschema_entityName = document.location.pathname.substring(6);
            currentFocus--;
            let lang = mw.config.get( 'wgUserLanguage' );
             /*and and make the current item more visible:*/
            entityschema_checkEntity(entityschema_entityName, entityschema_entitySchema, lang);
             addActive(x);
        }
        } else if (e.keyCode == 13) {
    }
             /*If the ENTER key is pressed, prevent the form from being submitted,*/
 
             e.preventDefault();
    function entityschema_checkbox() {
             if (currentFocus > -1) {
        if ($('#entityschema-checkbox').is(":checked")) {
                 /*and simulate a click on the "active" item:*/
            window.localStorage.setItem("entityschema-auto", true);
                 if (x) x[currentFocus].click();
        } else {
            window.localStorage.setItem("entityschema-auto", false);
         }
    }
 
    function entityschema_checkEntity(entity, entitySchema, language) {
        $("#entityschemaResponse").contents().remove();
        $(".entityschema-property").remove();
         $(".entityschema-span").remove();
 
        let url = "https://entityshape.toolforge.org/api/v2?entityschema=" + entitySchema + "&entity=" + entity + "&language=" + language;
        // let url = "http://127.0.0.1:5000/api/v2 ?entityschema=" + entitySchema + "&entity=" + entity + "&language=" + language;
        $.ajax({
            type: "GET",
            dataType: "json",
            url: url,
            beforeSend: function() {
                $(".entityshape-spinner").show();
             },
             success: function(data){
                let html = "";
                for (let i = 0; i < data.schema.length; i++ ) {
                    if (data.properties[i]) {
                        entityschema_add_to_properties(data.properties[i], data.schema[i], data.name[i]);
                    }
                    if (data.statements[i]) {
                        entityschema_add_to_statements(data.statements[i], data.schema[i], data.name[i]);
                    }
                    if (data.general[i]) {
                        entityschema_add_general(data.general[i]);
                    }
                }
 
                const combined_properties = entityschema_combine_properties(data.properties, data.schema);
 
                html += "<br/>Checking against <b>";
                for (let schema in data.schema) {
                    html += `<a href='https://kg.kunsten.be/wiki/EntitySchema:${data.schema[schema]}'>${data.schema[schema]}: ${data.name[schema]}</a> `;
                }
                html += `</b>:
                        <div style="overflow-y: scroll; max-height:200px;">
                        <table style="width:100%;">
                        <th class="entityschema_table" title="Properties in this item which must be present">Required properties</th>
                        <th class="entityschema_table" title="Properties in this item which can be present but do not have to be">Optional properties</th>
                        <th class="entityschema_table" title="Properties in this item which are not allowed to be present or not mentioned in the entityschema">Other properties</th>
                        <tr>`;
 
                html += entityschema_process_combined_properties(combined_properties);
 
                $("#entityschemaResponse" ).append( html );
                $(".entityshape-spinner").hide();
            },
            error: function(data) {
                $("#entityschemaResponse").append( '<br/><span>Unable to validate schema</span>' );
             }
        });
    }
 
    function entityschema_process_combined_properties(properties) {
        let required_html = '<td class="entityschema_table required"><ul style="list-style-type:none";>';
         let optional_html = '<td class="entityschema_table optional"><ul style="list-style-type:none";>';
        let absent_html = '<td class="entityschema_table absent"><ul style="list-style-type:none";>';
        let other_array = [];
        let other_array_names = [];
        for (let key in properties) {
            let shape_html = "";
            let response1 = properties[key].response.combined;
            let response_class  = "";
            switch (response1) {
                case "present":
                    response_class = "present";
                    break;
                case "allowed":
                    response_class = "present";
                    break;
                case "correct":
                    response_class = "correct";
                    break;
                case "missing":
                    response_class = "missing";
                    break;
                default:
                    response_class = "wrong";
                    break;
            }
            if (properties[key].necessity.combined == "absent") {
                if (response1 == "too many statements") {
                    response1 = "not allowed";
                }
            }
            if (!response1) {
                response1 = "Not in schema";
                response_class = "notinschema";
            }
            if (response1 == null) {
                response1 = "";
                shape_html += `<a href="https://kg.kunsten.be/wiki/Property:${key}">${key} - <small>${properties[key].name}</small></a><br/>`;
             } else if (response1 == "Not in schema") {
                other_array.push(key);
                other_array_names.push(properties[key].name);
            } else {
                shape_html += `<li class="is_entityschema-${response_class}">
                              <span class="entityschema-span entityschema-${response_class}">${response1}</span>
                              <a href="https://kg.kunsten.be/wiki/Property:${key}"
                                  class="is_entityschema-${response_class}">
                              ${key} - <small>${properties[key].name}</small></a></li>`
            }
             switch (properties[key].necessity.combined) {
                case "required":
                    required_html += shape_html;
                    break;
                case "optional":
                    optional_html += shape_html;
                    break;
                default:
                    absent_html += shape_html;
                    break;
            }
        }
        let other_html = `<details><summary>${other_array.length} properties not in any schema checked</summary>
                          <ul style='list-style-type:none';>`;
        for (let item in other_array) {
             other_html += `<li><span class="entityschema-span entityschema-notinschema">Not in schema</span>
                              <a href="https://kg.kunsten.be/wiki/Property:${other_array[item]}"
                                  class="is_entityschema-notinschema">${other_array[item]} - <small>${other_array_names[item]}</small></a></li>`;
        }
        other_html += "</ul></details>";
        absent_html += "</ul>" + other_html;
        required_html += "</ul></td>";
        optional_html += "</ul></td>";
        absent_html += "</td>";
        html = required_html + optional_html + absent_html + '</tr></table></div>';
        return html;
    }
 
    function entityschema_add_to_properties(properties, schema, name) {
        for (let key in properties) {
          let response = properties[key].response;
          let response_class = "";
             switch (response) {
                case "present":
                    response_class = "present";
                    break;
                case "correct":
                    response_class = "correct";
                    break;
                case "missing":
                    response_class = "missing";
                    break;
                default:
                    response_class = "wrong";
                    break;
            }
            if (properties[key].necessity == "absent") {
                if (response == "too many statements") {
                    response = "not allowed";
                }
             }
             if (!response) {
                response = "Not in schema";
                response_class = "notinschema";
            }
             if (response == null) {
                 response = "Not in schema";
                 response_class = "notinschema";
            }
            if (response != "Not in schema" && $("#" + key)[0]) {
                let html =`<br class='entityschema-property'/>
                          <div style='display:inline-block;'
                                class='entityschema-span entityschema-property entityschema-${response_class}'
                                title='${schema}: ${name}'>${schema}: ${response}
                          </div>`;
                $(`#${key} .wikibase-statementgroupview-property-label`).append(html);
             }
             }
         }
         }
    });
    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:*/
     function entityschema_add_to_statements(statements, schema, name) {
         for (var i = 0; i < x.length; i++) {
         for (let statement in statements) {
             x[i].classList.remove("autocomplete-active");
            let response = statements[statement].response;
            if (response != "not in schema") {
                let html = `<br class='entityschema-property'/>
                            <span class='entityschema-span entityschema-property entityschema-${response}'
                                  title='${schema}: ${name}'>${schema}: ${response}
                            </span>`;
                $(`div[id='${statement}'] .wikibase-toolbar-button-edit`).append(html);
            }
        }
    }
 
    function entityschema_add_general(general) {
         if (general.language) {
            entityschema_add_general_item("language", general.language);
        }
        if (general.lexicalCategory) {
            entityschema_add_general_item("lexical_category", general.lexicalCategory);
        }
    }
 
    function entityschema_add_general_item(general_type, response) {
        if (response != "not in schema") {
             html = `<span class='entityschema-property'/>
                    <span class='entityschema-span entityschema-property entityschema-${response}'>${response}
                    </span>`;
            $(`span[class='language-lexical-category-${general_type}']`).append(html);
         }
         }
     }
     }
     function closeAllLists(elmnt) {
 
         /*close all autocomplete lists in the document,
     function entityschema_combine_properties(property, schema) {
        except the one passed as an argument:*/
         let combined_properties = {};
        var x = document.getElementsByClassName("autocomplete-items");
         for (let i = 0; i < schema.length; i++ ) {
         for (var i = 0; i < x.length; i++) {
             for (let key in property[i]) {
             if (elmnt != x[i] && elmnt != inp) {
              if(!combined_properties.hasOwnProperty(key)){ combined_properties[key] = {}; }
                x[i].parentNode.removeChild(x[i]);
              combined_properties[key]["name"] = property[i][key]["name"];
              if(!combined_properties[key].hasOwnProperty("response")) { combined_properties[key]["response"] = {}; }
              combined_properties[key]["response"][schema[i]] = property[i][key]["response"];
              if(!combined_properties[key].hasOwnProperty("necessity")) { combined_properties[key]["necessity"] = {}; }
              combined_properties[key]["necessity"][schema[i]] = property[i][key]["necessity"];
             }
             }
         }
         }
        for (let key in combined_properties) {
            let necessity = "absent";
            let response = "";
            let response_key = combined_properties[key]["response"];
            let necessity_key = combined_properties[key]["necessity"];
            if (Object.values(necessity_key).includes("required")) {
                necessity = "required";
            } else if (Object.values(necessity_key).includes("optional")) {
                necessity = "optional";
            }
            if (Object.values(response_key).includes("incorrect")) {
                response = "incorrect";
            } else if (Object.values(response_key).includes("missing")) {
                response = "missing";
            } else if (Object.values(response_key).includes("too many statements")) {
                response = "too many statements";
            } else if (Object.values(response_key).includes("not enough statements")) {
                response = "not enough statements";
            } else if (Object.values(response_key).includes("correct")) {
                response = "correct";
            } else if (Object.values(response_key).includes("present")) {
                response = "present";
            } else if (Object.values(response_key).includes("allowed")) {
                response = "present";
            }
            combined_properties[key]["response"]["combined"] = response;
            combined_properties[key]["necessity"]["combined"] = necessity;
        }
        return combined_properties;
    }
    function entityschema_getStylesheet() {
        let stylesheet = "#entityschema-schemaSearchButton { background-position: center center; background-repeat: no-repeat; position: relative !important; top: 0; right: 0; overflow: hidden; height: 100%; background-color: #1E90FF !important; color: #FFFFFF !important; padding: 0.5em; text-indent: 0px !important; margin-left: 5px; width: 50px; margin-right: 5px;}";
        stylesheet += "#entityschema-entityToCheck { padding: 0.5em; margin: 0; width: 33%;}";
        stylesheet += ".response { padding: 2px;}";
        stylesheet += "a.is_entityschema-present { color: #008800; }";
        stylesheet += "a.is_entityschema-allowed { color: #008800; }";
        stylesheet += "a.is_entityschema-correct { color: #00CC00; }";
        stylesheet += "a.is_entityschema-missing { color: #FF5500; }";
        stylesheet += "a.is_entityschema-notinschema { color: #FF5500; }";
        stylesheet += "a.is_entityschema-wrong { color: #CC0000; }";
        stylesheet += "a.is_entityschema-wrong_amount { color: #CC0000; }";
        stylesheet += "a.is_entityschema-incorrect { color: #CC0000; }";
        stylesheet += ".entityschema_table {vertical-align: top; width: 33%;} ";
        stylesheet += ".entityschema-missing { background-color: #FF8C00; }";
        stylesheet += ".entityschema-notinschema { background-color: #FF8C00; }";
        stylesheet += ".entityschema-wrong { background-color: #CC0000; }";
        stylesheet += ".entityschema-incorrect { background-color: #CC0000; }";
        stylesheet += ".entityschema-wrong_amount { background-color: #CC0000; }";
        stylesheet += ".entityschema-excess { background-color: #CC0000; }";
        stylesheet += ".entityschema-deficit { background-color: #CC0000; }";
        stylesheet += ".entityschema-present { background-color: #008800; }";
        stylesheet += ".entityschema-allowed { background-color: #008800; }";
        stylesheet += ".entityschema-correct { background-color: #00CC00; }";
        stylesheet += ".required .entityschema-missing { background-color: #FF0000;}";
        stylesheet += ".required a.is_entityschema-missing { color: #FF0000;}";
        stylesheet += ".absent .entityschema-missing { display: none;}";
        stylesheet += ".absent a.is_entityschema-missing { display: none;}";
        stylesheet += ".entityschema-span { color: #ffffff; padding:2px; margin: 2px; font-size:75%; border-radius:2px; }";
        stylesheet += ".entityshape-spinner,.entityshape-spinner div,.entityshape-spinner div:after {box-sizing: border-box;}";
        stylesheet += ".entityshape-spinner { padding-top:5px; padding-bottom:5px; color: currentColor; display: inline-block; position: relative; width: 20px; height: 20px;}";
        stylesheet += ".entityshape-spinner div { transform-origin: 10px 10px; animation: entityshape-spinner 1.2s linear infinite;}";
        stylesheet += '.entityshape-spinner div:after { content: " "; display: block; position: absolute; top: 0.8px; left: 9.2px; width: 1.6px; height: 4.4px; border-radius: 20%; background: currentColor;}';
        stylesheet += ".entityshape-spinner div:nth-child(1) { transform: rotate(0deg); animation-delay: -1.1s;}";
        stylesheet += ".entityshape-spinner div:nth-child(2) { transform: rotate(30deg); animation-delay: -1s;}";
        stylesheet += ".entityshape-spinner div:nth-child(3) { transform: rotate(60deg); animation-delay: -0.9s;}";
        stylesheet += ".entityshape-spinner div:nth-child(4) { transform: rotate(90deg); animation-delay: -0.8s;}";
        stylesheet += ".entityshape-spinner div:nth-child(5) { transform: rotate(120deg); animation-delay: -0.7s;}";
        stylesheet += ".entityshape-spinner div:nth-child(6) { transform: rotate(150deg); animation-delay: -0.6s;}";
        stylesheet += ".entityshape-spinner div:nth-child(7) { transform: rotate(180deg); animation-delay: -0.5s;}";
        stylesheet += ".entityshape-spinner div:nth-child(8) { transform: rotate(210deg); animation-delay: -0.4s;}";
        stylesheet += ".entityshape-spinner div:nth-child(9) { transform: rotate(240deg); animation-delay: -0.3s;}";
        stylesheet += ".entityshape-spinner div:nth-child(10) { transform: rotate(270deg); animation-delay: -0.2s;}";
        stylesheet += ".entityshape-spinner div:nth-child(11) { transform: rotate(300deg); animation-delay: -0.1s;}";
        stylesheet += ".entityshape-spinner div:nth-child(12) { transform: rotate(330deg); animation-delay: 0s;}";
        stylesheet += "@keyframes entityshape-spinner { 0% { opacity: 1; } 100% { opacity: 0; }}";
        return stylesheet;
     }
     }
    /*execute a function when someone clicks in the document:*/
}());
    document.addEventListener("click", function (e) {
        closeAllLists(e.target);
    });
}

Versie van 7 okt 2024 22:45

/**
 * EntityShape.js adds an input box to a wikidata page wherein you can enter an entityschema
 * (such as E10).  When you click "Check", checks whether each statement and property conforms
 * to the schema.  It then displays a summary at the top of the item for each property indicating
 * whether they conform or not.  It also adds a badge to each statement and each property on the
 * page indicating whether they conform or not.
 **/

(function() {
    mw.util.addCSS(entityschema_getStylesheet());
    let entityschema_list = [];
    let value = window.localStorage.getItem("entityschema-auto");
    let schema = window.localStorage.getItem("entityschema");
    let property_list = [];
    let item_list = [];
    let entityID = mw.config.get( 'wbEntityId' );

    $(document).ready(function(){

        let entityschema_conditions = ["/wiki/Q", "/wiki/P", "/wiki/L"];
        if (entityschema_conditions.some(el => document.location.pathname.includes(el))) {
            let entityschema_entity_html = `<div><span id="entityschema-simpleSearch">
                                            <span>
                                            <input type="text" id="entityschema-entityToCheck"
                                                   placeholder="Enter schema to check against e.g. E234"
                                                   title="Enter 1 or more schemas to check against separated by commas e.g. E10, E236 or press Check to auto-determine schemas to check">
                                            <input type="submit" id="entityschema-schemaSearchButton"
                                                   class="searchButton" name="check" value="Check">
                                            <div class="entityshape-spinner" style="display:none"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div>
                                            </span>
                                            </span><input type="checkbox" id="entityschema-checkbox">
                                            <label for="entityschema-checkbox"><small>Automatically check schema</small></label>
                                            <span id="entityschemaResponse"></span></div>`;
            $(entityschema_entity_html).insertAfter("#siteSub");
            if (value == "true") {
                $("#entityschema-checkbox").prop('checked', true);
            } else {
                $("#entityschema-checkbox").prop('checked', false);
            }
            $("#entityschema-schemaSearchButton").click(function(){ entityschema_update(); });
            $("#entityschema-checkbox").click(function() { entityschema_checkbox(); });
        }
    });

    mw.hook( 'wikibase.entityPage.entityLoaded' ).add( function ( data ) {
        let claims = data["claims"];
        for (let claim in claims) {
            property_list.push(claim);
            let statements = claims[claim];
            for (let statement in statements) {
                let mainsnak = statements[statement]["mainsnak"];
                if (mainsnak["datavalue"] && mainsnak["datatype"] == "wikibase-item") {
                    if (!item_list.includes(mainsnak["datavalue"]["value"]["id"])) {
                        item_list.push(mainsnak["datavalue"]["value"]["id"]);
                    }
                }
            }
        }
        check_entity_for_schemas(item_list);
        check_entity_for_schemas(property_list);
    });

    mw.hook( 'wikibase.statement.saved' ).add( function ( data ) {
        if (value == "true") {
            entityschema_update();
        }
    });

    mw.hook( 'wikibase.statement.removed' ).add( function ( data ) {
        if (value == "true") {
            entityschema_update();
        }
    });

    function check_entity_for_schemas(entity_list) {
        for (let item in entity_list) {
            let url = "https://kg.kunsten.be/w/api.php?action=wbgetclaims&property=P12861&format=json&entity=" + entity_list[item];
            $.ajax({
                type: "GET",
                dataType: "json",
                url: url,
                success: function(data) {
                    if (data["claims"].hasOwnProperty("P12861")) {
                        for (let claim in data["claims"]["P12861"]) {
                            entityschema_list.push(data["claims"]["P12861"][claim]["mainsnak"]["datavalue"]["value"]["id"]);
                        }
                    }
                },
            });
        }
    }

    $(document).ajaxStop(function () {
      if (value == "true" && mw.config.get( 'wbEntityId' )) {
         entityschema_update();
      }
      $("#entityschema-entityToCheck:text").val(schema);
      $(this).unbind('ajaxStop');
    });

    $("#entityschema-entityToCheck").on("keyup", function(event){
        if (event.key === "Enter") {
            event.preventDefault();
            $("#entityschema-schemaSearchButton").click();
            return false;
        }
    });

    function entityschema_update() {
        let entityschema_entitySchema = $("#entityschema-entityToCheck")[0].value.toUpperCase();
        if (entityschema_entitySchema.length == 0) {
            entityschema_entitySchema = entityschema_list.join(", ");
            window.localStorage.setItem("entityschema", "");
        } else {
            window.localStorage.setItem("entityschema", entityschema_entitySchema);
        }
        if (entityschema_entitySchema.length == 0) {
            $("#entityschemaResponse").append( '<br/><span>No schemas entered and could not automatically determine schemas to check</span>' );
        } else {
            let entityschema_entityName = document.location.pathname.substring(6);
            let lang = mw.config.get( 'wgUserLanguage' );
            entityschema_checkEntity(entityschema_entityName, entityschema_entitySchema, lang);
        }
    }

    function entityschema_checkbox() {
        if ($('#entityschema-checkbox').is(":checked")) {
            window.localStorage.setItem("entityschema-auto", true);
        } else {
            window.localStorage.setItem("entityschema-auto", false);
        }
    }

    function entityschema_checkEntity(entity, entitySchema, language) {
        $("#entityschemaResponse").contents().remove();
        $(".entityschema-property").remove();
        $(".entityschema-span").remove();

        let url = "https://entityshape.toolforge.org/api/v2?entityschema=" + entitySchema + "&entity=" + entity + "&language=" + language;
        // let url = "http://127.0.0.1:5000/api/v2 ?entityschema=" + entitySchema + "&entity=" + entity + "&language=" + language;
        $.ajax({
            type: "GET",
            dataType: "json",
            url: url,
            beforeSend: function() {
                $(".entityshape-spinner").show();
            },
            success: function(data){
                let html = "";
                for (let i = 0; i < data.schema.length; i++ ) {
                    if (data.properties[i]) {
                        entityschema_add_to_properties(data.properties[i], data.schema[i], data.name[i]);
                    }
                    if (data.statements[i]) {
                        entityschema_add_to_statements(data.statements[i], data.schema[i], data.name[i]);
                    }
                    if (data.general[i]) {
                        entityschema_add_general(data.general[i]);
                    }
                }

                const combined_properties = entityschema_combine_properties(data.properties, data.schema);

                html += "<br/>Checking against <b>";
                for (let schema in data.schema) {
                    html += `<a href='https://kg.kunsten.be/wiki/EntitySchema:${data.schema[schema]}'>${data.schema[schema]}: ${data.name[schema]}</a> `;
                }
                html += `</b>:
                         <div style="overflow-y: scroll; max-height:200px;">
                         <table style="width:100%;">
                         <th class="entityschema_table" title="Properties in this item which must be present">Required properties</th>
                         <th class="entityschema_table" title="Properties in this item which can be present but do not have to be">Optional properties</th>
                         <th class="entityschema_table" title="Properties in this item which are not allowed to be present or not mentioned in the entityschema">Other properties</th>
                         <tr>`;

                html += entityschema_process_combined_properties(combined_properties);

                $("#entityschemaResponse" ).append( html );
                $(".entityshape-spinner").hide();
            },
            error: function(data) {
                $("#entityschemaResponse").append( '<br/><span>Unable to validate schema</span>' );
            }
        });
    }

    function entityschema_process_combined_properties(properties) {
        let required_html = '<td class="entityschema_table required"><ul style="list-style-type:none";>';
        let optional_html = '<td class="entityschema_table optional"><ul style="list-style-type:none";>';
        let absent_html = '<td class="entityschema_table absent"><ul style="list-style-type:none";>';
        let other_array = [];
        let other_array_names = [];
        for (let key in properties) {
            let shape_html = "";
            let response1 = properties[key].response.combined;
            let response_class  = "";
            switch (response1) {
                case "present":
                    response_class = "present";
                    break;
                case "allowed":
                    response_class = "present";
                    break;
                case "correct":
                    response_class = "correct";
                    break;
                case "missing":
                    response_class = "missing";
                    break;
                default:
                    response_class = "wrong";
                    break;
            }
            if (properties[key].necessity.combined == "absent") {
                if (response1 == "too many statements") {
                    response1 = "not allowed";
                }
            }
            if (!response1) {
                response1 = "Not in schema";
                response_class = "notinschema";
            }
            if (response1 == null) {
                response1 = "";
                shape_html += `<a href="https://kg.kunsten.be/wiki/Property:${key}">${key} - <small>${properties[key].name}</small></a><br/>`;
            } else if (response1 == "Not in schema") {
                other_array.push(key);
                other_array_names.push(properties[key].name);
            } else {
                shape_html += `<li class="is_entityschema-${response_class}">
                               <span class="entityschema-span entityschema-${response_class}">${response1}</span>
                               <a href="https://kg.kunsten.be/wiki/Property:${key}"
                                  class="is_entityschema-${response_class}">
                               ${key} - <small>${properties[key].name}</small></a></li>`
            }
            switch (properties[key].necessity.combined) {
                case "required":
                    required_html += shape_html;
                    break;
                case "optional":
                    optional_html += shape_html;
                    break;
                default:
                    absent_html += shape_html;
                    break;
            }
        }
        let other_html = `<details><summary>${other_array.length} properties not in any schema checked</summary>
                          <ul style='list-style-type:none';>`;
        for (let item in other_array) {
            other_html += `<li><span class="entityschema-span entityschema-notinschema">Not in schema</span>
                               <a href="https://kg.kunsten.be/wiki/Property:${other_array[item]}"
                                  class="is_entityschema-notinschema">${other_array[item]} - <small>${other_array_names[item]}</small></a></li>`;
        }
        other_html += "</ul></details>";
        absent_html += "</ul>" + other_html;
        required_html += "</ul></td>";
        optional_html += "</ul></td>";
        absent_html += "</td>";
        html = required_html + optional_html + absent_html + '</tr></table></div>';
        return html;
    }

    function entityschema_add_to_properties(properties, schema, name) {
        for (let key in properties) {
           let response = properties[key].response;
           let response_class = "";
            switch (response) {
                case "present":
                    response_class = "present";
                    break;
                case "correct":
                    response_class = "correct";
                    break;
                case "missing":
                    response_class = "missing";
                    break;
                default:
                    response_class = "wrong";
                    break;
            }
            if (properties[key].necessity == "absent") {
                if (response == "too many statements") {
                    response = "not allowed";
                }
            }
            if (!response) {
                response = "Not in schema";
                response_class = "notinschema";
            }
            if (response == null) {
                response = "Not in schema";
                response_class = "notinschema";
            }
            if (response != "Not in schema" && $("#" + key)[0]) {
                let html =`<br class='entityschema-property'/>
                           <div style='display:inline-block;'
                                class='entityschema-span entityschema-property entityschema-${response_class}'
                                title='${schema}: ${name}'>${schema}: ${response}
                           </div>`;
                $(`#${key} .wikibase-statementgroupview-property-label`).append(html);
            }
        }
    }

    function entityschema_add_to_statements(statements, schema, name) {
        for (let statement in statements) {
            let response = statements[statement].response;
            if (response != "not in schema") {
                let html = `<br class='entityschema-property'/>
                            <span class='entityschema-span entityschema-property entityschema-${response}'
                                  title='${schema}: ${name}'>${schema}: ${response}
                            </span>`;
                $(`div[id='${statement}'] .wikibase-toolbar-button-edit`).append(html);
            }
        }
    }

    function entityschema_add_general(general) {
        if (general.language) {
            entityschema_add_general_item("language", general.language);
        }
        if (general.lexicalCategory) {
            entityschema_add_general_item("lexical_category", general.lexicalCategory);
        }
    }

    function entityschema_add_general_item(general_type, response) {
        if (response != "not in schema") {
            html = `<span class='entityschema-property'/>
                    <span class='entityschema-span entityschema-property entityschema-${response}'>${response}
                    </span>`;
            $(`span[class='language-lexical-category-${general_type}']`).append(html);
        }
    }

    function entityschema_combine_properties(property, schema) {
        let combined_properties = {};
        for (let i = 0; i < schema.length; i++ ) {
            for (let key in property[i]) {
              if(!combined_properties.hasOwnProperty(key)){ combined_properties[key] = {}; }
              combined_properties[key]["name"] = property[i][key]["name"];
              if(!combined_properties[key].hasOwnProperty("response")) { combined_properties[key]["response"] = {}; }
              combined_properties[key]["response"][schema[i]] = property[i][key]["response"];
              if(!combined_properties[key].hasOwnProperty("necessity")) { combined_properties[key]["necessity"] = {}; }
              combined_properties[key]["necessity"][schema[i]] = property[i][key]["necessity"];
            }
        }
        for (let key in combined_properties) {
            let necessity = "absent";
            let response = "";
            let response_key = combined_properties[key]["response"];
            let necessity_key = combined_properties[key]["necessity"];

            if (Object.values(necessity_key).includes("required")) {
                necessity = "required";
            } else if (Object.values(necessity_key).includes("optional")) {
                necessity = "optional";
            }

            if (Object.values(response_key).includes("incorrect")) {
                 response = "incorrect";
            } else if (Object.values(response_key).includes("missing")) {
                 response = "missing";
            } else if (Object.values(response_key).includes("too many statements")) {
                 response = "too many statements";
            } else if (Object.values(response_key).includes("not enough statements")) {
                 response = "not enough statements";
            } else if (Object.values(response_key).includes("correct")) {
                 response = "correct";
            } else if (Object.values(response_key).includes("present")) {
                 response = "present";
            } else if (Object.values(response_key).includes("allowed")) {
                 response = "present";
            }
            combined_properties[key]["response"]["combined"] = response;
            combined_properties[key]["necessity"]["combined"] = necessity;
        }
        return combined_properties;
    }

    function entityschema_getStylesheet() {
        let stylesheet = "#entityschema-schemaSearchButton { background-position: center center; background-repeat: no-repeat; position: relative !important; top: 0; right: 0; overflow: hidden; height: 100%; background-color: #1E90FF !important; color: #FFFFFF !important; padding: 0.5em; text-indent: 0px !important; margin-left: 5px; width: 50px; margin-right: 5px;}";
        stylesheet += "#entityschema-entityToCheck { padding: 0.5em; margin: 0; width: 33%;}";
        stylesheet += ".response { padding: 2px;}";
        stylesheet += "a.is_entityschema-present { color: #008800; }";
        stylesheet += "a.is_entityschema-allowed { color: #008800; }";
        stylesheet += "a.is_entityschema-correct { color: #00CC00; }";
        stylesheet += "a.is_entityschema-missing { color: #FF5500; }";
        stylesheet += "a.is_entityschema-notinschema { color: #FF5500; }";
        stylesheet += "a.is_entityschema-wrong { color: #CC0000; }";
        stylesheet += "a.is_entityschema-wrong_amount { color: #CC0000; }";
        stylesheet += "a.is_entityschema-incorrect { color: #CC0000; }";
        stylesheet += ".entityschema_table {vertical-align: top; width: 33%;} ";
        stylesheet += ".entityschema-missing { background-color: #FF8C00; }";
        stylesheet += ".entityschema-notinschema { background-color: #FF8C00; }";
        stylesheet += ".entityschema-wrong { background-color: #CC0000; }";
        stylesheet += ".entityschema-incorrect { background-color: #CC0000; }";
        stylesheet += ".entityschema-wrong_amount { background-color: #CC0000; }";
        stylesheet += ".entityschema-excess { background-color: #CC0000; }";
        stylesheet += ".entityschema-deficit { background-color: #CC0000; }";
        stylesheet += ".entityschema-present { background-color: #008800; }";
        stylesheet += ".entityschema-allowed { background-color: #008800; }";
        stylesheet += ".entityschema-correct { background-color: #00CC00; }";
        stylesheet += ".required .entityschema-missing { background-color: #FF0000;}";
        stylesheet += ".required a.is_entityschema-missing { color: #FF0000;}";
        stylesheet += ".absent .entityschema-missing { display: none;}";
        stylesheet += ".absent a.is_entityschema-missing { display: none;}";
        stylesheet += ".entityschema-span { color: #ffffff; padding:2px; margin: 2px; font-size:75%; border-radius:2px; }";
        stylesheet += ".entityshape-spinner,.entityshape-spinner div,.entityshape-spinner div:after {box-sizing: border-box;}";
        stylesheet += ".entityshape-spinner { padding-top:5px; padding-bottom:5px; color: currentColor; display: inline-block; position: relative; width: 20px; height: 20px;}";
        stylesheet += ".entityshape-spinner div { transform-origin: 10px 10px; animation: entityshape-spinner 1.2s linear infinite;}";
        stylesheet += '.entityshape-spinner div:after { content: " "; display: block; position: absolute; top: 0.8px; left: 9.2px; width: 1.6px; height: 4.4px; border-radius: 20%; background: currentColor;}';
        stylesheet += ".entityshape-spinner div:nth-child(1) { transform: rotate(0deg); animation-delay: -1.1s;}";
        stylesheet += ".entityshape-spinner div:nth-child(2) { transform: rotate(30deg); animation-delay: -1s;}";
        stylesheet += ".entityshape-spinner div:nth-child(3) { transform: rotate(60deg); animation-delay: -0.9s;}";
        stylesheet += ".entityshape-spinner div:nth-child(4) { transform: rotate(90deg); animation-delay: -0.8s;}";
        stylesheet += ".entityshape-spinner div:nth-child(5) { transform: rotate(120deg); animation-delay: -0.7s;}";
        stylesheet += ".entityshape-spinner div:nth-child(6) { transform: rotate(150deg); animation-delay: -0.6s;}";
        stylesheet += ".entityshape-spinner div:nth-child(7) { transform: rotate(180deg); animation-delay: -0.5s;}";
        stylesheet += ".entityshape-spinner div:nth-child(8) { transform: rotate(210deg); animation-delay: -0.4s;}";
        stylesheet += ".entityshape-spinner div:nth-child(9) { transform: rotate(240deg); animation-delay: -0.3s;}";
        stylesheet += ".entityshape-spinner div:nth-child(10) { transform: rotate(270deg); animation-delay: -0.2s;}";
        stylesheet += ".entityshape-spinner div:nth-child(11) { transform: rotate(300deg); animation-delay: -0.1s;}";
        stylesheet += ".entityshape-spinner div:nth-child(12) { transform: rotate(330deg); animation-delay: 0s;}";
        stylesheet += "@keyframes entityshape-spinner { 0% { opacity: 1; } 100% { opacity: 0; }}";
        return stylesheet;
    }
}());