User:Mahir256/syndepgraph.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)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/* Used to make interesting SVG-based syntactic dependency graphs generated with {{Syndepgraph}} appear. Examples of such graphs are at [[Wikidata:Lexicographical data/Universal Dependencies]].

To use it, add

    importScript('User:Mahir256/syndepgraph.js');

to your common.js (or load it some other way).

As a more experimental piece of functionality, this can also be used on a lexeme page with 'combines' statements. Once on such a lexeme page, click

    "Show dependency graphs on lexeme page"

in the Tools portion of the sidebar, and the 'combines' statements at the top level of the lexeme will be formed into a graph.

This is currently a one-way transformation; edits to the statements after the graph is generated can only be accomplished after a refresh of the page.

Because this basically does what brat does (https://github.com/nlplab/brat), this script will also be released under the Expat license. */

( function ( mw, $ ) {

var node = mw.util.addPortletLink(
    "p-tb",
    "https://www.wikidata.org/wiki/Wikidata:Lexicographical_data/Universal_Dependencies",
    "Show dependency graphs on lexeme page"
);

var length_units = {
    "arrow_separation_distance": 18,
    "rel_start_y": 86,
    "starting_height": 110,
    "starting_width": 190,
    "text_arrow_gap": 4,
    "text_end_x_offset": 24,
    "text_height": 24,
    "text_start_x": 24,
    "text_start_y": 64,
    "text_width": 144,
    "y_upper_limit": 30
};

function SVGelem(name){
    return document.createElementNS("http://www.w3.org/2000/svg", name);
}

function buildCSS(){
    var svgstyle = new SVGelem("style");
    var svg_style_text = `
    .text-block-selected { fill:yellow }
    .rel-hidden { visibility:hidden }
    .arc-selected { stroke-width:3px }
    `;
    $(svgstyle).attr({type:"text/css"});
    $(svgstyle).html("<![CDATA[" + svg_style_text + "]]>");
    return svgstyle;
}

function buildEndmarker(){
    var endmarker = new SVGelem("marker");
    var endmarkershape = new SVGelem("polyline");
    $(endmarker).attr({
        fill:"black",
        id:"end_arrow",
        markerHeight:20,
        markerUnits:"userSpaceOnUse",
        markerWidth:20,
        orient:"auto",
        refX:10,
        refY:10
    });
    $(endmarkershape).attr({
        points:"0,0 10,10 0,20"
    });
    $(endmarker).append(endmarkershape);
    return endmarker;
}

function buildDefinitions(){
    var definitions = new SVGelem("defs");
    $(definitions).append(buildEndmarker());
    $(definitions).append(buildCSS());
    return definitions;
}

function hideHighlight(toplevel, class1, class2){
    return toggleHighlight(toplevel, class1, class2);
}

function toggleHighlight(toplevel, class1, class2){
    return function(){
        $(".textlayer rect."+class1, toplevel).toggleClass("text-block-selected");
        $(".textlayer rect."+class2, toplevel).toggleClass("text-block-selected");
        $(".arclayer path."+class1+"."+class2, toplevel).toggleClass("arc-selected");
        $(".rellayer text."+class1+"."+class2, toplevel).toggleClass("rel-hidden");
    };
}

function showHighlight(toplevel, class1, class2){
    return function(){
        hideAllHighlight(toplevel)();
        toggleHighlight(toplevel, class1, class2)();
    };
}

function hideAllHighlight(toplevel){
    return function(){
        $(".textlayer rect", toplevel).removeClass("text-block-selected");
        $(".arclayer path", toplevel).removeClass("arc-selected");
        $(".rellayer text", toplevel).addClass("rel-hidden");
    };
}

function buildTitlemark(title, dims){
    var titlemark = new SVGelem("text");
    $(titlemark).attr({
        "class":"word",
        "dominant-baseline":"middle",
        "text-anchor":"right",
        x:dims.text_start_x,
        y:dims.y_upper_limit-dims.text_arrow_gap
    });
    $(titlemark).html(title);
    return titlemark;
}

function buildTextRect(current_svg, word_class, target_class, index, dims){
    var current_textrect = new SVGelem("rect");
    $(current_textrect).attr({
        class:word_class,
        fill:"white",
        height:dims.text_height,
        stroke:"black",
        width:dims.text_width,
        x:dims.text_start_x+index*dims.text_width,
        y:dims.text_start_y
    });
    $(current_textrect).mouseenter(showHighlight(current_svg, word_class, target_class))
                       .mouseleave(hideHighlight(current_svg, word_class, target_class));
    return current_textrect;
}

function buildTextElem(current_svg, word_class, target_class, index, lemmaid, lemmatext, dims){
    var current_textelem = new SVGelem("text");
    $(current_textelem).attr({
        class:word_class,
        "dominant-baseline":"middle",
        "text-anchor":"middle",
        x:dims.text_start_x+(index+0.5)*dims.text_width,
        y:dims.text_start_y+0.5*dims.text_height
    });
    $(current_textelem).html("<a href=\"https://www.wikidata.org/entity/"+lemmaid+"\">"+lemmatext+"</a>");
    $(current_textelem).mouseenter(showHighlight(current_svg, word_class, target_class))
                       .mouseleave(hideHighlight(current_svg, word_class, target_class));
    return current_textelem;
}

function shiftXY(current_svg, textlayer, arclayer, rellayer, xshift_if_any, yshift_if_any, dims){
    $(current_svg).attr({
        height:dims.starting_height,
        width:dims.starting_width
    });
    if(yshift_if_any <= dims.y_upper_limit){
        $(current_svg).attr("height",$(current_svg).attr("height")-yshift_if_any+dims.y_upper_limit);
        $(textlayer).attr("transform","translate(0,"+(dims.y_upper_limit-yshift_if_any)+")");
        $(arclayer).attr("transform","translate(0,"+(dims.y_upper_limit-yshift_if_any)+")");
        $(rellayer).attr("transform","translate(0,"+(dims.y_upper_limit-yshift_if_any)+")");
    }
    if(xshift_if_any >= dims.starting_width - dims.text_end_x_offset){
        $(current_svg).attr("width",xshift_if_any + dims.text_end_x_offset);
    }
}

function buildPath(arrow_height, arrow_origin_y, startpt, endpt, combined_class, dims){
    var newpath = new SVGelem("path");
    $(newpath).attr({
        class:combined_class,
        "d":"M"+(startpt+dims.text_arrow_gap)+","+arrow_origin_y+"v-"+arrow_height+"h"+(endpt-startpt)+"v"+arrow_height,
        "marker-end":"url(#end_arrow)"
    }).css({
        fill:"none",
        stroke:"black"
    });
    return newpath;
}

function buildRelText(startpt, endpt, combined_class, relationship_text, dims){
    var reltext = new SVGelem("text");
    $(reltext).attr({
        class:combined_class,
        "dominant-baseline":"middle",
        "text-anchor":"middle",
        x:startpt+(endpt-startpt)/2,
        y:dims.rel_start_y+0.5*dims.text_height
    });
    $(reltext).html(relationship_text);
    $(reltext).addClass("rel-hidden");
    return reltext;
}

function buildSVG(elements, title, dimensions){
    var dims = dimensions ?? length_units;

    var titlemark = buildTitlemark(title, dims);

    var textlayer = new SVGelem("g");
    var arclayer = new SVGelem("g");
    var rellayer = new SVGelem("g");

    var yshift_if_any = dims.text_start_y - dims.arrow_separation_distance*(elements.length+2);
    var xshift_if_any = dims.text_start_x + (elements.length)*dims.text_width;

    var current_svg = new SVGelem("svg");
    $(current_svg).append(buildDefinitions());
    $(current_svg).append(titlemark);

    elements.forEach(function(item, index, current_array){
        var source_index = item[0];
        var lemmaid = item[1];
        var lemmatext = item[2];
        var target_index = item[3];
        var relationship_text = item[4];
        var word_class = item[5];
        var target_class = item[6];
        var combined_class = word_class+" "+target_class;

        var arrow_origin_x = dims.text_start_x+0.5*dims.text_width;
        var startpt = arrow_origin_x+(index)*dims.text_width;
        var endpt = arrow_origin_x+(target_index-1)*dims.text_width;
        var arrow_origin_y = dims.text_start_y;
        var arrow_height = dims.arrow_separation_distance*(index+1);

        if(target_index === 0){
            return;
        }

        $(textlayer).append(
            buildTextRect(current_svg, word_class, target_class, index, dims)
        );
        $(textlayer).append(
            buildTextElem(current_svg, word_class, target_class, index, lemmaid, lemmatext, dims)
        );

        if(target_index === "0"){
            $(rellayer).append(
                buildRelText(startpt, startpt, combined_class, relationship_text, dims)
            );
            return;
        }

        if(index > target_index){
            endpt += dims.text_width/4.0;
        }
        else{
            endpt -= dims.text_width/4.0;
        }

        $(arclayer).append(
            buildPath(arrow_height, arrow_origin_y, startpt, endpt, combined_class, dims)
        );

        $(rellayer).append(
            buildRelText(startpt, endpt, combined_class, relationship_text, dims)
        );
    });

    shiftXY(current_svg, textlayer, arclayer, rellayer, xshift_if_any, yshift_if_any, dims);

    $(textlayer).attr({
        class:"textlayer",
        x:0,
        y:0
    });
    $(current_svg).append(textlayer);
    $(arclayer).attr("class","arclayer");
    $(rellayer).attr("class","rellayer");
    $(current_svg).append(arclayer);
    $(current_svg).append(rellayer);
    return current_svg;
}

$(".syndepgraph").each(function(){
    var other_class = $(this).attr("class").split(" ")[1];
    var emphasized_rel = $(this).attr("data-emphasis");
    var elements = [];
    $(this).children(":not(:first)").each(function(){
        var word_class = $(this).attr("class");
        var target_class = $(this).attr("data-target");
        var sourceindex = word_class.replace(/^word/, "");
        var lemmaid = $("a", this).attr("href");
        var lemmatext = $(this).html();
        var targetindex = target_class.replace(/^word/, "");
        var relationship = "<a href=\"http://www.wikidata.org/entity/"+lemmaid+"\">"+$(this).attr("data-relname")+"</a>";
        elements.push([sourceindex, lemmaid, lemmatext, targetindex, relationship, word_class, target_class]);
    });
    elements.sort(function(a,b){
        return a[0] - b[0];
    });

    $(this).html(
        buildSVG(elements, $(this).children(":first").html())
    );
});

$( node ).on( "click", function ( e ) {
    $(".wikibase-entityview-main>.wikibase-statementgrouplistview .wikibase-statementgroupview#P5238").each(function(index0, stmtgroup){
        var elements = [];
        var full_lemma = "";
        if($("svg", stmtgroup).length > 0){
            return;
        }
        // collect and arrange information
        $(".wikibase-statementview.listview-item", stmtgroup).each(function(index1, stmt){
            var lemmaid = "";
            var lemmatext = $(".wikibase-statementview-mainsnak .wikibase-snakview-value span", stmt).text();
            var sourceindex = 0;
            var targetindex = 0;
            var relationship = "";

            $(".wikibase-statementview-mainsnak .wikibase-snakview-value a", stmt).each(function(index2,elem){
                lemmaid = $(elem).attr("href").replace("/wiki/Lexeme:","");
            });
            if($("div.wikibase-snakview-variation-somevaluesnak", stmt).length > 0){
                lemmatext = $("div.wikibase-snakview-variation-somevaluesnak", stmt).text();
            }
            $(".wikibase-snaklistview", stmt).each(function(index2, elem){
                if($(".wikibase-snakview-property a[href=\"/wiki/Property:P1932\"]", elem).length > 0){
                    lemmatext = $(".wikibase-snakview-value", elem).html();
                }
                else if($(".wikibase-snakview-property a[href=\"/wiki/Property:P5548\"]", elem).length > 0){
                    lemmatext = $(".wikibase-snakview-value", elem).text();
                }
                else if($(".wikibase-snakview-property a[href=\"/wiki/Property:P1545\"]", elem).length > 0){
                    sourceindex = $(".wikibase-snakview-value", elem).text();
                }
                else if($(".wikibase-snakview-property a[href=\"/wiki/Property:P9764\"]", elem).length > 0){
                    targetindex = $(".wikibase-snakview-value", elem).text();
                }
                else if($(".wikibase-snakview-property a[href=\"/wiki/Property:P9763\"]", elem).length > 0){
                    relationship = $(".wikibase-snakview-value", elem).html();
                }
            });
            elements.push([
                sourceindex, lemmaid, lemmatext, targetindex,
                relationship, "word"+sourceindex, "word"+targetindex
            ]);
        });
        elements.sort(function(a,b){
            return a[0] - b[0];
        });
        $(".lemma-widget_lemma-value").each(function(index2, current_lemma){
            full_lemma += "/"+$(current_lemma).text();
        });
        $(stmtgroup).html(
            buildSVG(elements, full_lemma.substr(1))
        );
    });
    e.preventDefault();
} );

} ( mediaWiki, jQuery ) );