$(document).ready(function() {
$('label').popover();
});
/**
* Launches the parsing process by calling the parser with the data entered in the interface,
* and processing the results.
*/
function parse() {
var textToParse = $("#lojban-text-area").val();
$("#result-row").slideDown();
try {
var start = new Date().getTime();
var parse = camxes.parse(textToParse);
var end = new Date().getTime();
$("#time-label").html("(parsing took " + (end - start) + " ms)");
parse = remove_morphology(parse);
parse = remove_spaces(parse);
var simplified = simplifyTree(parse);
numberSumti(simplified);
if (parse) {
tokens = [];
findTokens(parse, tokens);
var $parseResultHighlighted = $("#parse-result-highlighted");
showHighlighting(simplified[0], tokens, $parseResultHighlighted);
var $parseResultRaw = $("#parse-result-raw");
showRawTree(parse, $parseResultRaw);
var $parseResultTree = $("#parse-result-tree");
showParseTree(parse, $parseResultTree);
var $parseResultSimplified = $("#parse-result-simplified");
showSimplifiedTree(simplified, $parseResultSimplified);
var $parseResultBoxes = $("#parse-result-boxes");
showBoxes(simplified, $parseResultBoxes);
var $parseResultGlossing = $("#parse-result-glossing");
showGlossing(tokens, $parseResultGlossing);
}
$("#parse-result-highlighted-tab").html("Highlighted");
$("#parse-result-tree-tab").html("Parse tree");
$("#parse-result-raw-tab").html("Raw tree");
$("#parse-result-simplified-tab").html("Simplified tree");
$("#parse-result-boxes-tab").html("Boxes");
$("#parse-result-glossing-tab").html("Glosses");
} catch (e) {
if (e.name && e.name === "SyntaxError") {
$("#parse-result-highlighted-tab").html("Highlighted");
showSyntaxError(e, textToParse, $("#parse-result-highlighted"));
$("#parse-result-raw-tab").html("Raw tree");
showSyntaxError(e, textToParse, $("#parse-result-raw"));
$("#parse-result-simplified-tab").html("Simplified tree");
showSyntaxError(e, textToParse, $("#parse-result-simplified"));
$("#parse-result-tree-tab").html("Parse tree");
showSyntaxError(e, textToParse, $("#parse-result-tree"));
$("#parse-result-boxes-tab").html("Boxes");
showSyntaxError(e, textToParse, $("#parse-result-boxes"));
$("#parse-result-glossing-tab").html("Glosses");
showSyntaxError(e, textToParse, $("#parse-result-glossing"));
} else {
throw e;
}
}
}
/**
* Finds all tokens in the resulting parse tree, and puts them in the tokens array.
*/
function findTokens(parse, tokens) {
if (parse instanceof Array) {
if (parse.length == 2 && isString(parse[0]) && isString(parse[1])) {
tokens.push(parse[1]);
} else {
for (child in parse) {
findTokens(parse[child], tokens);
}
}
}
}
/**
* Shows the parse result in the interface.
*/
function showRawTree(parse, $element) {
$element.html("
" + JSON.stringify(parse, undefined, 2) + "
");
}
/**
* Shows the parse result in the interface.
*/
function showParseTree(parse, $element) {
$element.html(constructParseTreeOutput(parse, 0));
}
function constructParseTreeOutput(parse, depth) {
// precaution against infinite recursion; this should not actually happen of course
if (depth > 50) {
return "too much recursion :-(";
}
// if we get null, just print that
if (parse === null) {
return "(none?)";
}
// if we get undefined, just print that
if (!parse) {
return "(undefined?)";
}
if (parse instanceof Array) {
if (parse.length == 0) {
return "(empty array?)";
}
var output = "";
// what is the type of parse[0]?
if (isString(parse[0])) {
// it is the type
output += parse[0] + ":";
if (isString(parse[1])) {
// a literal
output += " [" + getVlasiskuLink(parse[1]) + "]";
if (shortDescriptions[parse[1]]) {
output += " " + shortDescriptions[parse[1]] + "";
}
return output;
}
output += "";
for (var child in parse) {
if (child !== "0") {
output += "- " + constructParseTreeOutput(parse[child], depth + 1) + "
";
}
}
output += "
";
return output;
} else {
output += "a list:";
output += "";
for (var child in parse) {
output += "- " + constructParseTreeOutput(parse[child], depth + 1) + "
";
}
output += "
";
return output;
}
return "(huh 2?)";
}
return "(huh? " + parse + ")";
}
/**
* Shows the simplified parse tree in the interface.
*/
function showSimplifiedTree(simplified, $element) {
$element.html(constructSimplifiedTreeOutput(simplified[0], 0));
}
function constructSimplifiedTreeOutput(parse, depth) {
// precaution against infinite recursion; this should not actually happen of course
if (depth > 50) {
return "too much recursion :-(";
}
// if we get null, just print that
if (parse === null) {
return "(none?)";
}
// if we get undefined, just print that
if (!parse) {
return "(undefined?)";
}
var output = parse.type;
if (parse.sumtiPlace) {
output += parse.sumtiPlace;
}
if (parse.word) {
// we have a terminal
output += " [" + getVlasiskuLink(parse.word) + "]";
if (shortDescriptions[parse.word]) {
output += " " + shortDescriptions[parse.word] + "";
}
} else {
// we have a non-terminal
output += "";
for (var child in parse.children) {
output += "- ";
output += constructSimplifiedTreeOutput(parse.children[child], depth + 1);
output += "
";
}
output += "
";
}
return output;
}
/**
* Shows the boxes in the interface.
*/
function showBoxes(simplified, $element) {
var output = "";
output += constructBoxesOutput(simplified[0], 0);
/*output += "Legend: ";
var types = ["sentence", "prenex", "selbri", "sumti"];
for (var type in types) {
output += "
" + types[type] + "
";
}
output += "";*/
$element.html(output);
}
function constructBoxesOutput(parse, depth) {
// precaution against infinite recursion; this should not actually happen of course
if (depth > 50) {
return "too much recursion :-(";
}
// if we get null, just print that
if (parse === null) {
return "(none?)";
}
// if we get undefined, just print that
if (!parse) {
return "(undefined?)";
}
var output = "";
if (parse.word) {
output += "";
// we have a terminal
output += " " + getVlasiskuLink(parse.word) + "
";
output += " " + parse.type + "
";
if (shortDescriptions[parse.word]) {
output += " " + shortDescriptions[parse.word] + " ";
} else {
output += "...";
}
output += "
";
} else {
// we have a non-terminal
output += "";
for (var child in parse.children) {
output += constructBoxesOutput(parse.children[child], depth + 1);
}
if (boxClassForType(parse) !== "box box-not-shown") {
output += "
" + parse.type;
if (parse.sumtiPlace) {
output += parse.sumtiPlace;
}
}
output += "
";
}
return output;
}
function boxClassForType(parse) {
if (parse.type === "sentence") {
return "box box-sentence";
}
if (parse.type === "sumti x") {
if (parse.sumtiPlace > 5) {
return "box box-sumti6";
} else if (parse.sumtiPlace == "fai") {
return "box box-sumti-fai";
} else {
return "box box-sumti" + parse.sumtiPlace;
}
}
if (parse.type === "modal sumti") {
return "box box-modal";
}
if (parse.type === "sumti") {
return "box box-sumti";
}
if (parse.type === "selbri") {
return "box box-selbri";
}
if (parse.type === "prenex") {
return "box box-prenex";
}
return "box box-not-shown";
}
/**
* Shows a syntax error in the interface.
*/
function showSyntaxError(e, textToParse, $element) {
var output = "" +
"
Syntax error on line " +
e.line +
", at column " +
e.column +
": " +
e.message +
"
" +
"
" +
generateErrorPosition(e, textToParse) +
"
" +
generateFixes(e) +
"
";
$element.html(output);
}
/**
* Generates the text sample that shows the error position.
*/
function generateErrorPosition(e, textToParse) {
//"mi vau ▴ do cusku ..." +
var before = textToParse.substring(e.offset - 20, e.offset);
var after = textToParse.substring(e.offset + 0, e.offset + 20);
if (e.offset > 20) {
before = "..." + before;
}
if (e.offset < textToParse.length - 20) {
after = after + "...";
}
return before + "▴" + after;
}
function generateFixes(e) {
if (!e.fix) {
//return "No quick fixes available.
";
return "";
}
var fixes = "Quick fixes:
";
for (var f in e.fix) {
var fix = (e.fix)[f];
fixes += "- ";
if (fix.fixFunction) {
fixes += "";
fixes += fix.name;
fixes += "";
} else {
fixes += fix.name;
}
fixes += "
";
}
fixes += "
";
return fixes;
}
/**
* Shows the highlighting in the interface.
*/
function showHighlighting(simplified, tokens, $element) {
var output = "";
if ($("#latin-button").hasClass('active')) {
var mode = 1;
var classString = "latin-highlighting";
} else if ($("#cyrillic-button").hasClass('active')) {
var mode = 2;
var classString = "cyrillic-highlighting";
} else if ($("#tengwar-button").hasClass('active')) {
var mode = 3;
var classString = "tengwar-highlighting";
} else if ($("#hiragana-button").hasClass('active')) {
var mode = 4;
var classString = "hiragana-highlighting";
}
output += "";
output += markupHighlighting(simplified, mode);
output += "";
$element.html(output);
}
function markupHighlighting(simplified, mode) {
var output = "";
var beforeOutput = "";
var afterOutput = " ";
if (simplified.type === "selbri") {
beforeOutput += "";
afterOutput = " ";
} else if (simplified.type === "modal sumti") {
beforeOutput += "m";
afterOutput = " ";
} else if (simplified.type === "sumti x") {
if (simplified.sumtiPlace > 5) {
beforeOutput += "" + simplified.sumtiPlace + "";
afterOutput = " ";
} else {
beforeOutput += "" + simplified.sumtiPlace + "";
afterOutput = " ";
}
} else if (simplified.type === "prenex") {
beforeOutput += "p";
afterOutput = " ";
} else if (simplified.type === "free") {
beforeOutput += "v";
afterOutput = " ";
}
if (simplified.word) {
output += outputWord(simplified.word, mode);
} else {
if (beforeOutput === "") {
for (child in simplified.children) {
output += markupHighlighting(simplified.children[child], mode);
}
} else {
output += "" + enumerateTokens(simplified, mode) + "";
}
}
return beforeOutput + output + afterOutput;
}
function enumerateTokens(simplified, mode) {
var output = "";
if (simplified.word) {
output += outputWord(simplified.word, mode);
} else {
for (child in simplified.children) {
var textToAdd = enumerateTokens(simplified.children[child], mode);
if (textToAdd) {
output += textToAdd + " ";
}
}
}
if (endsWith(output, " ")) {
output = output.substring(0, output.length - 1);
}
return output;
}
function isModalSumti(sumti) {
var tag = sumti[1][0];
return tag === "tag"; // TODO It would be much nicer to make some methods for walking the parse tree, and using them
}
function markupError(error, before, after) { // TODO
before[error.position] = "" + before[error.position];
after[error.position] = after[error.position] + "";
}
/**
* Shows the glossing in the interface.
*/
function showGlossing(text, $element) {
var output = "";
for (var j = 0; j < text.length; j++) {
output += "- " + getVlasiskuLink(text[j]) + "
";
if (shortDescriptions[text[j]]) {
output += "- " + shortDescriptions[text[j]] + "
";
} else {
output += "- (?)
";
}
}
output += "
";
$element.html(output);
}
/**
* Shows the translation in the interface.
*/
function showTranslation(parse, text, $element) {
var output = "This translation feature tries to give an approximate translation of the Lojban text into English. However, it does only work for a few sentences as of now. (Try [mi gleki] or something simple like that...)
";
//var translation = translate(parse);
var translation = "Sorry! Translation is switched off at the moment, to prevent crashes in the other parts :-(";
output += "" + translation + "";
$element.html(output);
}
// Auxiliary
function isString(s) {
return typeof(s) === 'string' || s instanceof String;
}
function getVlasiskuLink(word) {
return "" + outputWord(word, getSelectedMode()) + "";
}
function outputWord(word, mode) {
if (mode === 1) { // Latin mode
return addDotsToWord(word);
} else if (mode === 2) { // Cyrillic mode
return wordToCyrillic(addDotsToWord(word));
} else if (mode === 3) { // Tengwar mode
return wordToTengwar(addDotsToWord(word));
} else if (mode === 4) { // Hiragana mode
return wordToHiragana(addDotsToWord(word));
}
}
function getSelectedMode() {
if ($("#latin-button").hasClass('active')) {
return 1;
} else if ($("#cyrillic-button").hasClass('active')) {
return 2;
} else if ($("#tengwar-button").hasClass('active')) {
return 3;
} else if ($("#hiragana-button").hasClass('active')) {
return 4;
}
}
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}