(function () {
const MANIFEST_FILE = "manifest.jsonld";
const PROGRESS_CHUNK_COUNT = 100;
const OCTICON_USE = " "; // doesn't render when composed in pieces.
if (location.search.substr(1) === "toy") { // some examples from validation/manifest.jsonld
const url = new URL(location.search.substr(1) + '/' +'toy', location).href;
renderManifest(aFewTests(), "validation/", url);
} else {
$.ajaxSetup({ mimeType: "text/plain" }); // for persistent FF bug.
const url = new URL(location.search.substr(1) + '/' + MANIFEST_FILE, location).href;
$.getJSON(url).then(data => {
renderManifest(data["@graph"][0].entries, location.search.substr(1) + "/", url);
}).fail(e => {
$("table thead").append(
$("
").append(
$(" ").text("directory"),
$(" ").text("description")
));
$("table tbody").append(
[
{directory: "schemas",
description: "ShExC, JSON and Turtle versions of all schemas in the test suite."},
{directory: "validation",
description: "Positive and negative ShEx validation tests."},
{directory: "negativeStructure",
description: "Constructs that fail structural constraints ."},
{directory: "negativeSyntax",
description: "Constructs that fail the ShExC grammar ."},
{directory: "toy",
description: "small, static emulation of some validation tests for easy debugging."},
].map(d => {
return $(" ").append(
$(" ").append($(" ", {href: location.href+"?"+d.directory}).text(d.directory)),
$(" ").html(d.description)
);
}));
});
}
let droparea = $("#droparea");
let type = droparea.find("input");
let data = droparea.find("textarea");
droparea.on("dragover", function (evt) {
droparea.addClass("droppable");
evt.preventDefault();
}).on("dragleave", () => {
droparea.removeClass("droppable");
}).on("drop", evt => {
droparea.removeClass("droppable");
evt.preventDefault();
let xfer = evt.originalEvent.dataTransfer;
const prefOrder = [
"files", "application/json", "text/uri-list", "text/plain"
];
if (prefOrder.find(l => {
if (l.indexOf("/") === -1) {
if (xfer[l].length > 0) {
type.val(l);
data.val(JSON.stringify(xfer[l]));
readfiles(xfer[l], data);
return true;
}
} else {
if (xfer.getData(l)) {
type.val(l);
data.val(xfer.getData(l));
return true;
}
}
return false;
}) === undefined) {
type.val(xfer.types[0]);
data.val(xfer.getData(xfer.types[0]));
}
}).on("dragstart", (evt) => {
const t = type.val()
let v = data.val()
if (t === "text/uri-list")
v = v.split(/\n/).filter(s => !!s).map(s => s + "\r\n").join("");
console.log(v)
evt.originalEvent.dataTransfer.setData(t, v);
});
type.on('mousedown', function(e) {
e.stopPropagation();
droparea.attr('draggable', false);
}).on('mouseup', function(e) {
droparea.attr('draggable', true);
});
/* progressively render the tests, adjusting relative URLs by relPrepend.
*/
function renderManifest (tests, relPrepend, manifestUrl) {
let toAdd = [];
let startTime = new Date();
let testNo = 0;
let chunkSize = Math.max(Math.floor(tests.length / PROGRESS_CHUNK_COUNT), 1);
$("#tests").colResizable({ disable: true });
// assumes at least one test entry
let progressbar = $( "#progressbar" ),
progressLabel = $( ".progress-label" );
progressbar.progressbar({
value: false,
max: tests.length,
change: function() {
progressLabel.text( progressbar.progressbar( "value" ) + "/" + tests.length );
}
});
queue();
function queue () {
renderTest(tests[testNo]);
if (++testNo < tests.length) {
if (testNo % chunkSize === 0) {
progressbar.progressbar( "value", testNo+1 );
}
setTimeout(queue, 0);
} else {
// done loading tests
progressbar.progressbar( "value", testNo+1 );
progressLabel.empty().append(
"Loaded " + tests.length + " tests from ",
$("", {href: relPrepend + MANIFEST_FILE}).text(relPrepend + MANIFEST_FILE),
" in ",
(new Date() - startTime)/1000,
" seconds."
);
setTimeout(() => {
$("table tbody").append(toAdd);
var h = new URL(location).hash;
if (h)
highlight(h);
$("#tests").colResizable({
fixed:false,
liveDrag:true,
gripInnerHtml:"
"
});
}, 0);
}
}
function highlight (h) {
let [top, bottom] = h.substr(1).split(/--/);
let topElt = document.getElementById(top);
if (topElt) {
topElt.scrollIntoView({
behavior: "smooth",
block: "start"
});
let range = $(topElt);
if (bottom) {
let botElt = document.getElementById(bottom);
if (botElt) {
range = range.add(range.nextUntil(botElt)).add(botElt);
}
}
range.addClass("highlight");
}
}
function renderTest (test) {
const structures = {
"sht:ValidationTest": {
str: "passes", chr: "✓", offset: ["action"],
fields: [
{name:"schema", f: link},
{name:"data", f: link},
{name:"shape map", f:makeShapeMap}
]
},
"sht:ValidationFailure": {
str: "fails" , chr: "✗", offset: ["action"],
fields: [
{name:"schema", f: link},
{name:"data", f: link},
{name:"shape map", f:makeShapeMap}
]
},
"sht:RepresentationTest": {
str: "" , chr: "", offset: [],
fields: [
{name:"shex", f: link},
{name:"json", f: link},
{name:"ttl", f:link}
]
},
"sht:NegativeStructure": {
str: "" , chr: "", offset: [],
fields: [
{name:"shex", f: link}
]
},
"sht:NegativeSyntax": {
str: "" , chr: "", offset: [],
fields: [
{name:"shex", f: link}
]
}
};
let structure = structures[test["@type"]];
if (testNo === 0) {
// Table heading with column titles.
$("table thead").append(
drag("tr", { }, x => JSON.stringify(tests, null, 2), "application/json").append(
$(" "),
$(" ").text("name"),
structure.fields.map(h => {
return $(" ").text(h.name);
})
));
}
let titleText = "#" + (testNo+1) + " " + structure.str;
let id = test["@id"].replace(/#/, '_');
test["@id"] = new URL(id, manifestUrl).href;
let status = drag("td", { title: titleText, class: structure.str }, showTest, "application/json").text(structure.chr);
let attrs = structure.offset.reduce((acc, o) => { return acc[o]; }, test);
let octicon = $("", { href: '#' + id }).append(OCTICON_USE).on("click", function (evt) {
evt.preventDefault();
$(".highlight").removeClass("highlight");
let fragment = $(this).attr("href").substr(1);
if (!evt.shiftKey) {
location.hash = fragment;
} else {
location.hash = location.hash + "--" + fragment;
}
highlight(location.hash);
});
let name = drag("td", { title: test.comment }, showTest, "application/json").append(
octicon,
" ",
id.substr(1)
);
toAdd = toAdd.concat(
$(" ", {id: id}).append(
status, name,
// $(" ").append(shexc), $(" ").append(data), shapemap
structure.fields.map(h => {
return h.f(attrs, h.name);
})
));
if (testNo === tests.length-1) {
// Table footer with column titles.
toAdd = toAdd.concat(
drag("tr", { }, x => JSON.stringify(tests, null, 2), "application/json").append(
$(" "),
$(" ").text(tests.length + " tests"),
structure.fields.map(h => {
return $(" ").text(h.name);
})
)
);
}
function showTest (elt) {
return JSON.stringify(test, null, 2);
}
function ttl (ld) {
return typeof ld === "object" ? lit(ld) :
ld.startsWith("_:") ? ld :
"<" + ld + ">";
function lit (o) {
let ret = "\""+o["@value"]+"\"";
if ("@type" in o)
ret += "^^<" + o["@type"] + ">";
if ("language" in o)
ret += "@" + o["language"];
return ret;
}
}
function drag (name, attrs, val, type) {
return $("<"+name+"/>", attrs).attr("draggable", true).
on("dragstart", (evt) => {
evt.originalEvent.dataTransfer.setData(type, val(evt.target));
return true;
});
}
function link (attrs, name) {
let val = attrs[name];
let a = $(" ", { href: relPrepend + val }).text(val);
attrs[name] = a.prop("href");
return title(drag("td", {}, elt => {
return a.get(0).href;
}, "text/uri-list").append(a), a.get(0).href);
}
function title (target, url) {
// localStorage shaves ~1.5s off the load time.
if (typeof(Storage) !== "undefined" && url in localStorage) {
target.attr("title", localStorage[url].length > 0 ? localStorage[url] : "-- empty file --");
} else {
$.ajax({
url: url,
dataType: 'text',
type: 'GET',
async: true
}).then(function (data) {
if (typeof(Storage) !== "undefined") {
localStorage[url] = data;
}
target.attr("title", data.length > 0 ? data : "-- empty file --");
}).fail(function (jqXHR, status, errorThrown) {
target.addClass("error");
target.attr("title", url + " " + status + ": " + errorThrown);
});
}
return target;
}
function makeShapeMap (attrs, val) {
if ("map" in attrs) {
var a = $(" ", { href: relPrepend + attrs.map }).text(attrs.map);
title(a, a.get(0).href)
attrs["map"] = a.prop("href");
return drag(" ", { }, elt => {
return a.get(0).href;
}, "text/uri-list").append(a);
} else {
return drag("td", { }, elt => {
return elt.innerText;
}, "text/plain").text(ttl(attrs.focus) + "@" + ("shape" in attrs ? ttl(attrs.shape) : "START"))
}
}
};
}
function readfiles(files, target) {
var formData = new FormData();
for (var i = 0; i < files.length; i++) {
var file = files[i], name = file.name;
formData.append("file", file);
var reader = new FileReader();
reader.onload = (function (target) {
return function (event) {
target.text(event.target.result);
};
})(target);
reader.readAsText(file);
}
}
function aFewTests () {
return [
{
"@id": "#0_otherbnode",
"@type": "sht:ValidationTest",
"action": {
"schema": "../schemas/0.shex",
"shape": "http://a.example/S1",
"data": "Babcd_Ip1_Io1.ttl",
"focus": "_:abcd"
},
"extensionResults": [],
"name": "0_otherbnode",
"trait": [
"ToldBNode",
"Empty"
],
"comment": " { } on { _:abcd }",
"status": "mf:proposed"
},
{
"@id": "#3Eachdot3Extra_pass-iri1",
"@type": "sht:ValidationTest",
"action": {
"schema": "../schemas/3Eachdot3Extra.shex",
"shape": "http://a.example/S1",
"data": "Is_Ipn_IonX3.ttl",
"focus": "http://a.example/s"
},
"extensionResults": [],
"name": "3Eachdot3Extra_pass-iri1",
"trait": [
"Extra",
"IriEquivalence",
"EachOf"
],
"comment": " EXTRA EXTRA EXTRA { [], [], [] } on { ; ; }",
"status": "mf:proposed"
},
{
"@id": "#3circRefS1-IS2-IS3-IS3",
"@type": "sht:ValidationTest",
"action": {
"schema": "../schemas/3circRefS1-IS2-IS3-IS3.shex",
"shape": "http://a.example/S1",
"data": "3circRefPlus1_pass-open.ttl",
"focus": "http://a.example/n1"
},
"extensionResults": [],
"name": "3circRefS1-IS2-IS3-IS3",
"trait": [
"Import"
],
"comment": "I2 I3 { ., @? } | I3 { @ } | { @ } on { \"X\" ; . . . \"X\" }",
"status": "mf:proposed",
"result": "3circRefPlus1_pass-open.val"
},
{
"@id": "#focusdatatype_pass",
"@type": "sht:ValidationTest",
"action": {
"schema": "../schemas/focusdatatype.shex",
"shape": "http://a.example/S1",
"data": "Is1_Ip1_LabDTbloodType.ttl",
"focus": {
"@value": "ab",
"@type": "http://a.example/bloodType"
}
},
"extensionResults": [],
"name": "focusdatatype_pass",
"trait": [
"FocusConstraint"
],
"comment": " on { 'ab'^^my:bloodType }",
"status": "mf:proposed"
},
{
"@id": "#dependent_shape",
"@type": "sht:ValidationTest",
"action": {
"schema": "../schemas/dependent_shape.shex",
"data": "dependent_shape.ttl",
"map": "dependent_shape_map.json"
},
"extensionResults": [],
"name": "dependent_shape",
"trait": [
"ShapeMap"
],
"comment": " { @} { []} on { . .}",
"status": "mf:proposed",
"result": "dependent_shape_results.json"
},
];
}
})();