1
0
forked from GitHub/gf-core

move gf.cabal and all compiler dependent files into src/compiler

This commit is contained in:
Krasimir Angelov
2022-06-18 20:42:31 +02:00
parent 96c8218564
commit a8ad145aeb
264 changed files with 71 additions and 71 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -0,0 +1,95 @@
/* Copyright © Elnaz Abolahrar and Thomas Hallgren, 2011 */
body {
background: #eee url("brushed-metal.png");
}
/* th, td { vertical-align: baseline; text-align: left; } */
div#surface {
min-height: 3ex;
margin: 5px;
padding: 5px;
border: 3px dashed #e0e0e0;
}
div#words {
min-height: 3ex;
margin: 5px;
padding: 6px;
border: 3px solid #e0e0e0;
}
div.word, span.word, div#words input[type=button] {
display: inline-block;
font-family: sans-serif;
font-size: 100%;
background-color: white;
border: 1px solid black;
padding: 3px;
margin: 3px;
}
/* added for quiz */
textarea.quest {
width: 452px;
height: 35px;
font-size:16px;
}
textarea.explain {
width: 452px;
height: 70px;
font-size:16px;
}
input.answer1 {
width: 442px;
height: 20px;
font-size:16px;
}
div.hint {
font-family: Verdana;
font-weight: bold;
font-size:16px;
}
div.bold {
font-weight: bold;
font-size:16px;
}
input.number {
text-align:center;
font-family: Verdana;
font-weight: bold;
font-size:16px;
}
div.shift_left {
padding-left: 320px;
}
/* default for all links */
a:link {color: #2E8B57; font-size:16px; font-weight: bold; text-decoration: underline; }
a:active {color: #2E8B57; font-size:16px; font-weight: bold; text-decoration: underline; }
a:visited {color: #2E8B57; font-size:16px; font-weight: bold; text-decoration: underline; }
a:hover {color: #CD853F; font-size:16px; font-weight: bold; text-decoration: none; }
.invalid { color: red; }
div.modtime { float: right; }
.modtime { color: #444; white-space: nowrap; }
ul.space>li { margin-top: 0.5ex; }
div#saldospel input[type=button] { font-size: 100%; }
div#saldospel input.correct { color: green; }
div#saldospel input.incorrect { color: red; }
#surface input[type=text] { width: 5em; }
span.field { background-color: #eee; }

View File

@@ -0,0 +1,640 @@
// Copyright © Elnaz Abolahrar and Thomas Hallgren, 2011
// minibar.js, assumes that support.js has also been loaded
var tree_icon="tree-btn.png";
/*
// This is essentially what happens when you call start_minibar:
if(server.grammar_list) grammars=server.grammar_list;
else grammars=server.get_grammarlist();
show_grammarlist(grammars)
select_grammar(grammars[0])
grammar_info=server.get_languages()
show_languages(grammar_info)
new_language()
complete_output=get_completions()
show_completions(complete_output)
*/
function Minibar(server,opts,target) {
// Typically called when the HTML document is loaded
/* --- Configuration ---------------------------------------------------- */
// default values for options:
this.options={
show_abstract: false,
show_trees: false,
show_grouped_translations: true,
delete_button_text: "⌫",
default_source_language: null,
//modified for quiz
try_google: false,
feedback_url: null,
//modified for quiz
random_button: false,
help_url: null
}
// Apply supplied options
if(opts) for(var o in opts) this.options[o]=opts[o];
/* --- Creating user interface elements --------------------------------- */
this.surface=div_id("surface");
this.extra=div_id("extra");
this.menubar=div_id("menubar");
this.quizbar=div_id("quizbar");
this.words=div_id("words");
this.translations=div_id("translations");
this.minibar=element(target || "minibar");
this.minibar_contin=element("minibar_contin");
this.minibar_buttons=element("minibar_buttons");
this.minibar.innerHTML="";
//modified for quiz
with(this) {
appendChildren(minibar,[menubar, quizbar]);
appendChildren(minibar_contin,[surface,words]);
append_extra_buttons(extra,options);
}
// Filled in and added to minibar later:
this.grammar_menu=empty_id("select","grammar_menu");
this.from_menu=empty_id("select","from_menu");
this.to_menu=empty_id("select","to_menu");
/* --- Minibar client state initialisation ------------------------------ */
this.grammar=null;
//modified for quiz
this.current={from: null, to:null, input: ""};
this.previous=null;
this.server=server;
/* --- Main program, this gets things going ----------------------------- */
with(this) {
if(server.grammar_list) show_grammarlist(server.grammar_list);
else server.get_grammarlist(bind(show_grammarlist,this));
}
}
/* --- Auxiliary functions ---------------------------------------------- */
Minibar.prototype.show_grammarlist=function(grammars) {
debug(this)
with(this) {
//debug("show_grammarlist ")
menubar.innerHTML="";
if(grammars.length>0) {
function opt(g) { return option(g,g); }
appendChildren(grammar_menu,map(opt,grammars));
grammar_menu.onchange=
bind(function() { select_grammar(grammar_menu.value); },this);
appendChildren(menubar,[text("Grammar: "),grammar_menu]);
}
//modified for quiz
appendChildren(menubar,
[text(" From: "), from_menu,
text(" To: "), to_menu]);
//modified for quiz
appendChildren(minibar_buttons,
[button(options.delete_button_text,bind(delete_last,this),"H"),
button("Clear",bind(clear_all,this),"L")]);
if(options.random_button)
menubar.appendChild(button("Random",bind(generate_random,this),"R"));
if(options.help_url)
menubar.appendChild(button("Help",bind(open_help,this)));
select_grammar(grammars[0]);
}
}
Minibar.prototype.select_grammar=function(grammar_name) {
var t=this;
//debug("select_grammar ");
function get_languages() {
t.server.get_languages(bind(t.show_languages,t));
}
t.server.switch_grammar(grammar_name,get_languages);
}
Minibar.prototype.show_languages=function(grammar_info) {
var t=this;
with(t) {
//debug("show_languages ");
grammar=grammar_info;
var new_language=function () {
current.from=from_menu.value;
//modified for quiz
//clear_all();
}
//added for quiz
var change_tolang=function () {
current.to=to_menu.value;
//var langname = element("to_menu").value;
//to_menu.current={to: langname, input: ""};
//clear_all();
}
from_menu.onchange=bind(new_language,t);
update_language_menu(from_menu,grammar);
set_initial_language(options,from_menu,grammar);
//modified and added for quiz
//to_menu.onchange=bind(get_translations,t);
to_menu.onchange=bind(change_tolang,t);
update_language_menu(to_menu,grammar);
//modified for quiz
//insertFirst(to_menu,option("All","All"));
//to_menu.value="All";
new_language();
//added for quiz
change_tolang();
}
}
Minibar.prototype.clear_all1=function() {
with(this) {
remove_typed_input();
current.input="";
previous=null;
surface.innerHTML="";
translations.innerHTML="";
}
//added for quiz
document.answer.answer_text.value = "";
document.explanation.explanation_text.value= "";
document.getElementById("hint_txt").innerHTML = "";
}
Minibar.prototype.clear_all=function() {
with(this) {
clear_all1();
get_completions();
}
}
Minibar.prototype.get_completions=function() {
with(this) {
//debug("get_completions ");
words.innerHTML="...";
//modified for quiz
server.complete(current.to,current.input,bind(show_completions,this));
}
}
Minibar.prototype.show_completions=function(complete_output) {
with(this) {
//debug("show_completions ");
var completions=complete_output[0].completions;
var emptycnt=add_completions(completions)
if(true/*emptycnt>0*/) get_translations();
else translations.innerHTML="";
if(surface.typed && emptycnt==completions.length) {
if(surface.typed.value=="") remove_typed_input();
}
else add_typed_input();
}
//added for quiz :updates the hint and prevents the check_notEmpty alert in show_hint when all words are deleted
if (!(document.answer.answer_text.value == null || document.answer.answer_text.value ==""))
{ if (hint_times > 0 && hint_times < max_hint_times)
show_hint();
}
else
document.getElementById("hint_txt").innerHTML = "";
}
Minibar.prototype.add_completions=function(completions) {
with(this) {
if(words.timeout) clearTimeout(words.timeout),words.timeout=null;
words.innerHTML="";
words.completions=completions;
words.word=[];
var t=surface.typed ? surface.typed.value : "";
var emptycnt=0;
for(var i=0;i<completions.length;i++) {
var s=completions[i];
if(s.length>0) {
var w=word(s);
words.appendChild(w);
words.word[i]=w;
}
else emptycnt++;
}
filter_completions(t,true);
return emptycnt;
}
}
Minibar.prototype.filter_completions=function(t,dim) {
with(this) {
if(words.timeout) clearTimeout(words.timeout),words.timeout=null;
words.filtered=t;
//if(dim) debug('filter "'+t+'"');
var w=words.word;
words.count=0;
var dimmed=0;
var prefix=""; // longest common prefix, for completion
for(var i=0;i<w.length;i++) {
var s=words.completions[i];
var keep=hasPrefix(s,t);
if(keep) {
if(words.count==0) prefix=s;
else prefix=(commonPrefix(prefix,s));
words.count++;
}
if(dim) {
w[i].style.opacity= keep ? "1" : "0.5";
if(keep) w[i].style.display="inline";
else dimmed++;
}
else
w[i].style.display=keep ? "inline" : "none";
}
words.theword=prefix;
if(dimmed>0)
words.timeout=setTimeout(function(){ filter_completions(t,false)},1000);
}
}
Minibar.prototype.get_translations=function() {
with(this) {
var c=current;
if(options.show_grouped_translations)
server.translategroup(c.from,c.input,bind(show_groupedtranslations,this));
else
server.translate(c.from,c.input,bind(show_translations,this));
}
}
Minibar.prototype.target_lang=function() {
with(this) return langpart(to_menu.value,grammar.name);
}
Minibar.prototype.add_typed_input=function() {
with(this) {
var inp;
if(surface.typed) inp=surface.typed;
else {
inp=empty("input","type","text");
inp.value="";
inp.setAttribute("accesskey","t");
inp.style.width="10em";
inp.onkeyup=bind(complete_typed,this);
surface.appendChild(inp);
surface.typed=inp;
}
inp.focus();
}
}
Minibar.prototype.remove_typed_input=function() {
with(this) {
if(surface.typed) {
surface.typed.parentNode.removeChild(surface.typed);
surface.typed=null;
}
}
}
Minibar.prototype.complete_typed=function(event) {
with(this) {
//element("debug").innerHTML=show_props(event,"event");
var inp=surface.typed;
//debug('"'+inp.value+'"');
var s=inp.value;
var ws=s.split(" ");
if(ws.length>1 || event.keyCode==13) {
if(ws[0]!=words.filtered) filter_completions(ws[0],true);
if(words.count==1) add_word(words.theword);
else if(elem(ws[0],words.completions)) add_word(ws[0]);
else if(words.theword.length>ws[0].length) inp.value=words.theword;
}
else if(s!=words.filtered) filter_completions(s,true)
}
}
Minibar.prototype.generate_random=function() {
var t=this;
function show_random(random) {
t.clear_all1();
t.add_words(random[0].text);
}
function lin_random(abs) {
t.server.linearize(abs[0].tree,t.current.from,show_random);
}
t.server.get_random(lin_random);
}
Minibar.prototype.add_words=function(s) {
with(this) {
var ws=s.split(" ");
for(var i=0;i<ws.length;i++)
add_word1(ws[i]+" ");
get_completions();
}
//added for quiz
document.answer.answer_text.value += s+" ";
}
Minibar.prototype.word=function(s) {
var t=this;
function click_word() {
if(t.surface.typed) t.surface.typed.value="";
t.add_word(s);
}
return button(s,click_word);
}
Minibar.prototype.add_word=function(s) {
with(this) {
add_word1(s+" ");
if(surface.typed) {
var s2;
if(hasPrefix(s2=surface.typed.value,s)) {
s2=s2.substr(s.length);
while(s2.length>0 && s2[0]==" ") s2=s2.substr(1);
surface.typed.value=s2;
}
else surface.typed.value="";
}
get_completions();
}
//added for quiz
document.answer.answer_text.value += s+" ";
}
Minibar.prototype.add_word1=function(s) {
with(this) {
previous={ input: current.input, previous: previous };
current.input+=s;
var w=span_class("word",text(s));
if(surface.typed) surface.insertBefore(w,surface.typed);
else surface.appendChild(w);
}
}
Minibar.prototype.delete_last=function() {
with(this) {
if(surface.typed && surface.typed.value!="")
surface.typed.value="";
else if(previous) {
current.input=previous.input;
previous=previous.previous;
if(surface.typed) {
surface.removeChild(surface.typed.previousSibling);
surface.typed.focus();
}
else surface.removeChild(surface.lastChild);
translations.innerHTML="";
//added for quiz (to update the user answer area)
var last_answer= document.answer.answer_text.value ;
document.answer.answer_text.value = remove_last(last_answer);
get_completions();
}
}
}
//added for quiz
function remove_last(txt){
var str = remove_unwanted_characters(txt);
var ls= str.lastIndexOf(" ");
if (ls > -1)
return str.substring(0,ls ) + " ";
else
return "";
}
Minibar.prototype.tdt=function(tree_btn,txt) {
with(this) {
return options.show_trees ? tda([tree_btn,txt]) : td(txt);
}
}
Minibar.prototype.show_translations=function(translationResults) {
with(this) {
var trans=translations;
//var to=target_lang(); // wrong
var to=to_menu.value;
var cnt=translationResults.length;
//trans.translations=translations;
trans.single_translation=[];
trans.innerHTML="";
/*
trans.appendChild(wrap("h3",text(cnt<1 ? "No translations?" :
cnt>1 ? ""+cnt+" translations:":
"One translation:")));
*/
for(p=0;p<cnt;p++) {
var tra=translationResults[p];
if (tra.translations != null) {
for (q = 0; q < tra.translations.length; q++) {
var t = tra.translations[q];
var lin=t.linearizations;
var tbody=empty("tbody");
if(options.show_abstract && t.tree)
tbody.appendChild(tr([th(text("Abstract: ")),
tdt(abstree_button(t.tree),text(" "+t.tree))]));
for(var i=0;i<lin.length;i++)
if(to=="All" || lin[i].to==to)
tbody.appendChild(tr([th(text(langpart(lin[i].to,grammar.name)+": ")),
tdt(parsetree_button(t.tree,lin[i].to),
text(lin[i].text))]));
trans.appendChild(wrap("table",tbody));
}
}
else if(tra.typeErrors) {
var errs=tra.typeErrors;
for(var i=0;i<errs.length;i++)
trans.appendChild(wrap("pre",text(errs[i].msg)))
}
}
}
}
Minibar.prototype.show_groupedtranslations=function(translationsResult) {
with(this) {
var trans=translations;
var to=target_lang();
//var to=to_menu.value // wrong
var cnt=translationsResult.length;
//trans.translations=translationsResult;
trans.single_translation=[];
trans.innerHTML="";
for(p=0;p<cnt;p++) {
var t=translationsResult[p];
if(to=="All" || t.to==to) {
var lin=t.linearizations;
var tbody=empty("tbody");
if(to=="All") tbody.appendChild(tr([th(text(t.to+":"))]));
for(var i=0;i<lin.length;i++) {
if(to!="All") trans.single_translation[i]=lin[i].text;
tbody.appendChild(tr([td(text(lin[i].text))]));
if (lin.length > 1) tbody.appendChild(tr([td(text(lin[i].tree))]));
}
trans.appendChild(wrap("table",tbody));
}
}
}
}
Minibar.prototype.append_extra_buttons=function(extra,options) {
with(this) {
if(options.try_google)
extra.appendChild(button("Try Google Translate",bind(try_google,this)));
if(options.feedback_url)
appendChildren(extra,[text(" "),button("Feedback",bind(open_feedback,this))]);
}
}
Minibar.prototype.try_google=function() {
with(this) {
var to=target_lang();
var s=current.input;
if(surface.typed) s+=surface.typed.value;
var url="http://translate.google.com/?sl="
+langpart(current.from,grammar.name);
if(to!="All") url+="&tl="+to;
url+="&q="+encodeURIComponent(s);
window.open(url);
}
}
Minibar.prototype.open_help=function() {
with(this) open_popup(options.help_url,"help");
}
Minibar.prototype.open_feedback=function() {
with(this) {
// make the minibar state easily accessible from the feedback page:
minibar.state={grammar:grammar,current:current,to:to_menu.value,
translations:translations};
open_popup(options.feedback_url,'feedback');
}
}
function update_language_menu(menu,grammar) {
// Replace the options in the menu with the languages in the grammar
var lang=grammar.languages;
menu.innerHTML="";
for(var i=0; i<lang.length; i++) {
var ln=lang[i].name;
if(!hasPrefix(ln,"Disamb")) {
var lp=langpart(ln,grammar.name);
menu.appendChild(option(lp,ln));
}
}
}
function set_initial_language(options,menu,grammar) {
if(grammar.userLanguage) menu.value=grammar.userLanguage;
else if(options.default_source_language) {
for(var i=0;i<menu.options.length;i++) {
var o=menu.options[i].value;
var l=langpart(o,grammar.name);
if(l==options.default_source_language) menu.value=o;
}
}
}
function langpart(conc,abs) { // langpart("FoodsEng","Foods") == "Eng"
return hasPrefix(conc,abs) ? conc.substr(abs.length) : conc;
}
function abstree_button(abs) {
var i=img(tree_icon);
i.setAttribute("onclick","toggle_img(this)");
i.other=server.current_grammar_url+"?command=abstrtree&tree="+encodeURIComponent(abs);
return i;
}
function parsetree_button(abs,lang) {
var i=img(tree_icon);
i.setAttribute("onclick","toggle_img(this)");
i.other=server.current_grammar_url
+"?command=parsetree&from="+lang+"&tree="+encodeURIComponent(abs);
return i;
}
function toggle_img(i) {
var tmp=i.src;
i.src=i.other;
i.other=tmp;
}
function open_popup(url,target) {
var w=window.open(url,target,'toolbar=no,location=no,status=no,menubar=no');
w.focus();
}
function setField(form,name,value) {
form[name].value=value;
var el=element(name);
if(el) el.innerHTML=value;
}
function opener_element(id) { with(window.opener) return element(id); }
// This function is called from feedback.html
function prefill_feedback_form() {
var state=opener_element("minibar").state;
var trans=state.translations;
var gn=state.grammar.name
var to=langpart(state.to,gn);
var form=document.forms.namedItem("feedback");
setField(form,"grammar",gn);
setField(form,"from",langpart(state.current.from,gn));
setField(form,"input",state.current.input);
setField(form,"to",to);
if(to=="All") element("translation_box").style.display="none";
else setField(form,"translation",trans.single_translation.join(" / "));
// Browser info:
form["inner_size"].value=window.innerWidth+"×"+window.innerHeight;
form["outer_size"].value=window.outerWidth+"×"+window.outerHeight;
form["screen_size"].value=screen.width+"×"+screen.height;
form["available_screen_size"].value=screen.availWidth+"×"+screen.availHeight;
form["color_depth"].value=screen.colorDepth;
form["pixel_depth"].value=screen.pixelDepth;
window.focus();
}
/*
se.chalmers.cs.gf.gwt.TranslateApp/align-btn.png
GET /grammars/Foods.pgf?&command=abstrtree&tree=Pred+(This+Fish)+(Very+Fresh)
GET /grammars/Foods.pgf?&command=parsetree&tree=Pred+(This+Fish)+Expensive&from=FoodsAfr
GET /grammars/Foods.pgf?&command=alignment&tree=Pred+(This+Fish)+Expensive
*/

View File

@@ -0,0 +1,56 @@
/* --- Grammar access object ------------------------------------------------ */
function pgf_online(options) {
var server = {
// State variables (private):
grammars_url: "/grammars/",
grammar_list: null,
current_grammar_url: null,
// Methods:
switch_grammar: function(grammar_url,cont) {
this.current_grammar_url=this.grammars_url+grammar_url;
if(cont) cont();
},
get_grammarlist: function(cont) {
http_get_json(this.grammars_url+"grammars.cgi",cont);
},
pgf_call: function(cmd,args,cont) {
var url=this.current_grammar_url+"?command="+cmd;
for(var arg in args) url+="&"+arg+"="+encodeURIComponent(args[arg]);
http_get_json(url,cont);
},
get_languages: function(cont) {
this.pgf_call("grammar",{},cont);
},
get_random: function(cont) {
this.pgf_call("random",{random:Math.random()},cont);
},
linearize: function(tree,to,cont) {
this.pgf_call("linearize",{tree:tree,to:to},cont);
},
linearizeAll: function(tree,to,cont) {
this.pgf_call("linearizeAll",{tree:tree,to:to},cont);
},
complete: function(from,input,cont) {
this.pgf_call("complete",{from:from,input:input},cont);
},
parse: function(from,input,cont) {
this.pgf_call("parse",{from:from,input:input},cont);
},
translate: function(from,input,cont) {
this.pgf_call("translate",{from:from,input:input},cont);
},
translategroup: function(from,input,cont) {
this.pgf_call("translategroup",{from:from,input:input},cont);
}
};
for(var o in options) server[o]=options[o];
if(server.grammar_list && server.grammar_list.length>0)
server.switch_grammar(server.grammar_list[0]);
return server;
}

View File

@@ -0,0 +1,50 @@
<!-- Copyright © Elnaz Abolahrar, 2011 -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Quiz About</title>
<link rel=stylesheet type="text/css" href="minibar_quiz.css">
<meta name = "viewport" content = "width = device-width">
<meta http-equiv="Content-type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
</head>
<body>
<h3>About GF Translation Quiz </h3>
<p style="font-size:18px;">
This project is actually part of my master thesis work at <a href="http://chalmers.se/en/Pages/default.aspx" >Chalmers University of Technology</a>
, in the Language Technology group under supervision of Professor <a href="http://www.cse.chalmers.se/~aarne/">Aarne Ranta.</a> The application
is an exercise generator to be used in training the lexical, morphological and syntactic aspects of human languages.
One important aspect of this application is that it is intended to be as general as possible and specially not
to be limited to any specific human language. For this purpose GFs (The <a href="http://www.grammaticalframework.org/" >Grammatical Framework </a>)
Resource Grammar Libraries have been applied as the constructive core of the project. The application also uses the
<a href="http://code.google.com/p/grammatical-framework/wiki/GFWebServiceAPI" >GF web services API </a>
which has made it possible to apply the PGF API as Web Service, to be more precise the Quiz uses the additional API for JavaScript defined in pgf_online.js provided by the
<a href="http://www.grammaticalframework.org:41296/minibar/minibar.html"> Minibar </a> application.
The Quiz also relies on the Minibar,
in the "Easy Study Mode" of the Quiz, where word magnets from the Minibar are available to help the user construct his/her answer.
</p>
<p style="font-size:18px;"> The generated exercises are in the preliminary form of sentences
in a certain language - selected by the user - which need to be translated to another one - which is also the user's choice.
The user may choose the grammar he/she wants to train with as well. More information about how the quiz works can be found from the
<a href="quiz_help.html" >link </a> in the Quiz main page.
</p>
<br/>
Author: <i><b> Elnaz Abolahrar </b></i> (elnaz dot abolahrar at gmail dot com)
<br/>
Date: December, 2010 - Göteborg, Sweden
<br/>
<br/>
<br/>
<div class="shift_left"><a href="javascript: window.close()">Close page</a></div>
</body>
</html>

View File

@@ -0,0 +1,46 @@
<!-- Copyright © Elnaz Abolahrar, 2011 -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Quiz Help</title>
<link rel=stylesheet type="text/css" href="minibar_quiz.css">
<meta name = "viewport" content = "width = device-width">
<meta http-equiv="Content-type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
</head>
<body>
<a href="quiz_about.html" >About GF Translation Quiz </a>
<h3>&#x095 How the Quiz works? </h3>
<p style="font-size:18px;">
<b>Hint:</b> You will find the Hint button very handy in all quiz modes. In fact it works very much similar to the famous <a href="http://en.wikipedia.org/wiki/Mastermind_(board_game)">Mastermind</a> game. Words in green stand for correct words in their correct place in the sentence, and yellow words mean these words are correct words and are part of the right answer but they are misplaced, while red words stand for words which do not exist in the right answer and are either misspelled or totally wrong lexically or grammatically.
<br/><br/>
<b>Check Answer:</b> You can check whether your answer is right by either clicking on this button or pressing the Enter/Return key of your keyboard. (Exception: In the "Easy Study Mode" you cannot use the Enter/Return key for checking your answer, because it has another functionality which is adding typed words to your answer.) Please note that by checking your answer you also submit it, and for each question you have only one submission chance to increase your score - further submissions don't have any impact on your score. Use the Hint if you are not sure and of course if it is available. Also note that Check Answer moves you automatically to the next question if your answer is right.
<br/><br/>
<b>History:</b> At the end of the quiz, either if you end it manually by pressing the "End" button or when the quiz ends itself e.g. when you pass the quiz or at the end of an exam, you will get a "Show Quiz History" button by which you may review and print the whole questions together with your answers and the programs' feedback.
</p>
<hr>
<h3>&#x095 How different are different Quiz Modes? </h3>
<p style="font-size:18px;"> <b>Easy Study Mode: </b>
In this mode word magnets are available to help you; you can type and/or click on them freely to construct your answer. Use "&#x232B" (Delete last) <!--img border="0" src="Delete_button.jpg" alt="&#x232B" width="30" height="17" /--> and "Clear" buttons to delete the last word or all of your answer in case required. Theses words come from the grammar you have chosen, and therefore it keeps you from mistyping words and even making grammatically wrong sentences. Also you have unlimited Hint which is automatically updated as you modify your answer. You can also go to one previous question at a time and of course check your answer's correctness.
<br/><br/><b>Medium Study Mode: </b>
In this mode you can use the Hint button for a maximum of 3 times for each question, and you have the possibility of going to one previous question at a time as well as checking your answer's correctness.
<br/><br/><b>Hard Study Mode: </b>
In this mode you can use the Hint button only once for each question, and you may not use the Previous question button however you may check your answer's correctness.
<br/><br/><b>Exam Mode: </b>
In this mode you cannot use the Hint, Previous question or Check answer buttons, and you will not see your score until the end of the exam.
</p>
<br/>
<div class="shift_left"><a href="javascript: window.close()">Close page</a></div>
</body>
</html>

View File

@@ -0,0 +1,64 @@
// Copyright © Elnaz Abolahrar, 2011
// pre_start: runs on load time before evrything else to prepare the quiz
function pre_start()
{
hide_element('history_part');
hide_element('toggle_show');
default_values();
var quiz_mode=empty_id("select","quiz_mode");
//adds the "Quiz Mode" and "Restart Quiz" and "End Quiz" and "Start Quiz"
appendChildren(minibar.quizbar,
[text(" Quiz Mode: "), quiz_mode,
button("Restart Quiz","restart_quiz()","R"),
button("End Quiz","end_quiz(true)","E"),
button("Start Quiz","start_quiz()","S")]);
//adds the "Next Question" and "Previous Question" and "Hint" and "Check Answer" buttons
var buttons_bar=element("buttons_bar");
appendChildren(buttons_bar,
[button("< Previous Question","previous_question_quiz()","B", "previous_question"),
button("Next Question >","generate_question()","N", "next_question"),
button("Hint","show_hint()","H", "hint"),
submit_button("Check Answer", "check_answer")]);
disable_all();
//hide the minibar word magnets
hide_element('minibar_contin');
//hide the delete and clear buttons
hide_element('minibar_buttons');
mode_options(quiz_mode);
}
function mode_options(quiz_mode)
{
var opt=empty("option");
opt.setAttribute("value","Easy Study Mode");
opt.innerHTML="Easy Study Mode";
quiz_mode.appendChild(opt);
var opt=empty("option");
opt.setAttribute("value","Medium Study Mode");
opt.innerHTML="Medium Study Mode";
quiz_mode.appendChild(opt);
var opt=empty("option");
opt.setAttribute("value","Hard Study Mode");
opt.innerHTML="Hard Study Mode";
quiz_mode.appendChild(opt);
var opt=empty("option");
opt.setAttribute("value","Exam Mode");
opt.innerHTML="Exam Mode";
quiz_mode.appendChild(opt);
}

View File

@@ -0,0 +1,248 @@
// Copyright © Elnaz Abolahrar and Thomas Hallgren, 2011
function hide_element(elem_id)
{
document.getElementById(elem_id).style.display="none";
}
function show_element(elem_id)
{
document.getElementById(elem_id).style.display="";
}
function toggle_info()
{
if ( info_hidden == true )
{
show_element("info");
hide_element("toggle_show");
show_element("toggle_hide");
info_hidden = false;
}
else
{
hide_element("info");
show_element("toggle_show");
hide_element("toggle_hide");
info_hidden = true;
}
}
function show_word_magnets()
{
if ( words_hidden == true )
{
show_element("words");
words_hidden= false;
var buttons_bar=element("buttons_bar");
buttons_bar.removeChild(buttons_bar.lastChild);
}
}
function hide_word_magnets()
{
if ( words_hidden == false )
{
hide_element("words");
words_hidden= true;
//add "Show magnets" button
var buttons_bar=element("buttons_bar");
appendChildren(buttons_bar,
[ button("Show Magnets","show_word_magnets()","M", "show_magnets")]);
}
}
function popUp(newPage, pageName)
{
window.open(newPage, pageName, "dependent = 1, scrollbars=1, location=1, statusbar=1, width=540, height=650, left = 10, top = 20");
}
function disable_all()
{
//disables the "Hint", "Check Answer", "Next Question" and "Previous Question" buttons + user answer area
document.getElementById('check_answer').disabled = true;
document.getElementById('next_question').disabled = true;
document.getElementById('previous_question').disabled = true;
document.getElementById('hint').disabled = true;
document.getElementById('user_answer').disabled = true;
}
function set_mode()
{
//disable the grammar - To - From languages, and mode menubar
document.getElementById('grammar_menu').disabled = true;
document.getElementById('from_menu').disabled = true;
document.getElementById('to_menu').disabled = true;
document.getElementById('quiz_mode').disabled = true;
selected_mode = element("quiz_mode").value;
//sets the Quiz mode displayed
document.getElementById('mode').value = selected_mode;
/*-------------------------------------- Modes Settings --------------------------------------*/
switch (selected_mode)
{
case "Easy Study Mode":
have_minibar = true;
have_prevQuestion = true;
have_checkAns = true;
max_hint_times = 100;
break;
case "Medium Study Mode":
have_minibar = false;
have_prevQuestion = true;
have_checkAns = true;
max_hint_times = 3;
break;
case "Hard Study Mode":
have_minibar = false;
have_prevQuestion = false;
have_checkAns = true;
max_hint_times = 1;
break;
case "Exam Mode":
have_minibar = false;
have_prevQuestion = false;
have_checkAns = false;
max_hint_times = 0;
break;
}
}
function reset_mode()
{
//enable the grammar - To - From languages, and mode menubar
document.getElementById('grammar_menu').disabled = false;
document.getElementById('from_menu').disabled = false;
document.getElementById('to_menu').disabled = false;
document.getElementById('quiz_mode').disabled = false;
//clears the Quiz mode displayed
document.getElementById('mode').value = "";
}
function remove_minibar()
{
if (have_minibar && is_ended == false )
{
//hide the minibar word magnets
hide_element("minibar_contin");
//hide the delete and clear buttons
hide_element("minibar_buttons");
}
}
function remove_unwanted_characters(txt)
{
//removes digits, special characters and extra spaces from user's answer
txt = txt.replace(/[\u0021-\u0026 \u0028-\u0040 \u005b-\u0060 \u007b-\u007e]+/g,' ').replace(/^\s+|\s+$/g,'').replace(/\s+/g,' ');
//changes the first character to lowercase
txt= txt.replace(txt.charAt(0),txt.charAt(0).toLowerCase());
return txt;
}
function split_to_words(str)
{
if (!(str == "" || str == null))
str = str.split(" ");
else
str = "";
return str;
}
function string_matching(serv_answer,use_answer)
{
var result = new Array();
//for empty answers
if ( use_answer== "" || use_answer== null)
result= "";
else
{
var min_length = Math.min(serv_answer.length, use_answer.length);
var i=0;
for (i= 0; i < min_length; i++)
{
if (serv_answer[i] == use_answer[i])
result[i] = 1;
else
result[i] = 0;
}
//for answers with extra words (more than the number of words in the right answer)
while ( i < use_answer.length)
{
result[i] = 0;
i++;
}
}
return result;
}
function sum_all(arr) {
var s = 0;
for (var i = 0; i < arr.length; i++)
{
s += arr[i];
}
return s;
}
function find_closest(all_ans)
{
var best_match = new Array();
var comp = new Array();
var server_answer2 = new Array();
var max=0;
var k = 0;
for (k= 0; k < all_ans.length; k++)
{
server_answer = remove_unwanted_characters(all_ans[k]);
server_answer2 = split_to_words(server_answer);
comp = string_matching(server_answer2, user_answer_splited);
var sum = sum_all(comp);
if (sum >= max)
{
best_match = server_answer2;
max= sum;
}
}
return best_match;
}
function clearing()
{
//clears the question, answer and the explanation and hint display areas
document.question.question_text.value= "...";
document.answer.answer_text.value = "";
document.explanation.explanation_text.value= "";
document.getElementById("hint_txt").innerHTML = "";
}
//checks that the answer field is not empty
function check_notEmpty()
{
if (document.answer.answer_text.value == null || document.answer.answer_text.value =="")
{
alert(" You have to write something first!");
//sets the focus on the answer area
document.answer.answer_text.focus();
return false;
}
else
return true;
}

View File

@@ -0,0 +1,285 @@
/* --- Accessing document elements ------------------------------------------ */
function element(id) {
return document.getElementById(id);
}
/* --- JavaScript tricks ---------------------------------------------------- */
// To be able to object methods that refer to "this" as callbacks
// See section 3.3 of https://github.com/spencertipping/js-in-ten-minutes/raw/master/js-in-ten-minutes.pdf
function bind(f, this_value) {
return function () {return f.apply (this_value, arguments)};
};
/* --- JSONP ---------------------------------------------------------------- */
// Inspired by the function jsonp from
// http://www.west-wind.com/Weblog/posts/107136.aspx
// See also http://niryariv.wordpress.com/2009/05/05/jsonp-quickly/
// http://en.wikipedia.org/wiki/JSON#JSONP
function jsonp(url,callback)
{
if (url.indexOf("?") > -1)
url += "&jsonp="
else
url += "?jsonp="
url += callback;
//url += "&" + new Date().getTime().toString(); // prevent caching
var script = empty("script");
script.setAttribute("src",url);
script.setAttribute("type","text/javascript");
document.body.appendChild(script);
}
var json = {next:0};
// Like jsonp, but instead of passing the name of the ballback function, you
// pass the callback function directly, making it possible to use anonymous
// functions.
function jsonpf(url,callback)
{
var name="callback"+(json.next++);
json[name]=function(x) { delete json[name]; callback(x); }
jsonp(url,"json."+name);
}
/* --- AJAX ----------------------------------------------------------------- */
function GetXmlHttpObject(handler)
{
var objXMLHttp=null
if (window.XMLHttpRequest)
{
objXMLHttp=new XMLHttpRequest()
}
else if (window.ActiveXObject)
{
objXMLHttp=new ActiveXObject("Microsoft.XMLHTTP")
}
return objXMLHttp
}
function ajax_http_get(url,callback) {
var http=GetXmlHttpObject()
if (http==null) {
alert ("Browser does not support HTTP Request")
return
}
var statechange=function() {
if (http.readyState==4 || http.readyState=="complete") {
if(http.status==200) callback(http.responseText);
else alert("Request for "+url+" failed: "
+http.status+" "+http.statusText);
}
}
http.onreadystatechange=statechange;
http.open("GET",url,true)
http.send(null)
//dump("http get "+url+"\n")
return http
}
// JSON via AJAX
function ajax_http_get_json(url,cont) {
ajax_http_get(url,function(txt) { cont(eval("("+txt+")")); });
}
function sameOrigin(url) {
return hasPrefix(url,location.protocol+"//"+location.host+"/");
}
// Use AJAX when possible, fallback to JSONP
function http_get_json(url,cont) {
if(sameOrigin(url)) ajax_http_get_json(url,cont);
else jsonpf(url,cont);
}
/* --- HTML construction ---------------------------------------------------- */
function text(s) { return document.createTextNode(s); }
function node(tag,as,ds) {
var n=document.createElement(tag);
for(var a in as) n.setAttribute(a,as[a]);
for(var i in ds) n.appendChild(ds[i]);
return n;
}
function empty(tag,name,value) {
var el=node(tag,{},[])
if(name && value) el.setAttribute(name,value);
return el;
}
function empty_id(tag,id) { return empty(tag,"id",id); }
function empty_class(tag,cls) { return empty(tag,"class",cls); }
function div_id(id) { return empty_id("div",id); }
function span_id(id) { return empty_id("span",id); }
function wrap(tag,contents) { return node(tag,{},[contents]); }
function wrap_class(tag,cls,contents) {
var el=empty_class(tag,cls);
if(contents) el.appendChild(contents);
return el;
}
function span_class(cls,contents) { return wrap_class("span",cls,contents); }
function div_class(cls,contents) { return wrap_class("div",cls,contents); }
function p(contents) { return wrap("p",contents); }
function dt(contents) { return wrap("dt",contents); }
function li(contents) { return wrap("li",contents); }
function th(contents) { return wrap("th",contents); }
function td(contents) { return wrap("td",contents); }
function tr(cells) { return node("tr",{},cells); }
//modified for quiz (id added)
function button(label,action,key ,id) {
var el=node("input",{"type":"button","value":label},[]);
if(typeof action=="string") el.setAttribute("onclick",action);
else el.onclick=action;
if(key) el.setAttribute("accesskey",key);
if(id) el.setAttribute("id",id);
return el;
}
//added for quiz
function submit_button(label, id) {
var el=empty("input","type","submit");
el.setAttribute("value",label);
if(id) el.setAttribute("id", id);
return el;
}
function option(label,value) {
return node("option",{"value":value},[text(label)]);
}
function appendChildren(el,ds) {
for(var i in ds) el.appendChild(ds[i]);
return el;
}
function insertFirst(parent,child) {
parent.insertBefore(child,parent.firstChild);
}
function tda(cs) { return node("td",{},cs); }
function img(src) { return empty("img","src",src); }
/* --- Debug ---------------------------------------------------------------- */
function debug(s) {
var d=element("debug");
if(d) d.appendChild(text(s+"\n"))
}
function show_props(obj, objName) {
var result = "";
for (var i in obj) {
result += objName + "." + i + " = " + obj[i] + "<br>";
}
return result;
}
function field_names(obj) {
var result = "";
for (var i in obj) {
result += " " + i;
}
return result;
}
/* --- Data manipulation ---------------------------------------------------- */
function swap(a,i,j) { // Note: this doesn't work on strings.
var tmp=a[i];
a[i]=a[j];
a[j]=tmp;
return a;
}
function sort(a) {
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/sort
return a.sort();
/* // Note: this doesn't work on strings.
for(var i=0;i<a.length-1;i++) {
var min=i;
for(var j=i+1;j<a.length;j++)
if(a[j]<a[min]) min=j;
if(min!=i) swap(a,i,min);
}
return a;
*/
}
function filter(p,xs) {
var ys=[];
for(var i=0;i<xs.length;i++)
if(p(xs[i])) ys[ys.length]=xs[i];
return ys;
}
function implode(cs) { // array of strings to string
/*
var s="";
for(var i=0;i<cs.length;i++)
s+=cs[i];
return s;
*/
return cs.join("");
}
function hasPrefix(s,pre) { return s.substr(0,pre.length)==pre; }
function commonPrefix(s1,s2) {
for(var i=0;i<s1.length && i<s2.length && s1[i]==s2[i];i++);
return s1.substr(0,i);
}
/*
function all(p,xs) {
for(var i=0;i<xs.length;i++)
if(!p(xs[i])) return false;
return true;
}
*/
function map(f,xs) {
var ys=[];
for(var i=0;i<xs.length;i++) ys[i]=f(xs[i]);
return ys;
}
// map in continuation passing style
function mapc(f,xs,cont) { mapc_from(f,xs,0,[],cont); }
function mapc_from(f,xs,i,ys,cont) {
if(i<xs.length)
f(xs[i],function(y){ys[i]=y;mapc_from(f,xs,i+1,ys,cont)});
else
cont(ys);
}
function overlaps(as,bs) {
for(var i=0;i<as.length;i++)
if(elem(as[i],bs)) return true;
return false;
}
function elem(a,as) {
for(var i=0;i<as.length;i++)
if(a==as[i]) return true;
return false;
}
function shuffle(a) {
for(i=0;i<a.length;i++) swap(a,i,Math.floor(Math.random()*a.length))
return a;
}

View File

@@ -0,0 +1,165 @@
<!-- Copyright © Elnaz Abolahrar, 2011 -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>GF Translation Quiz</title>
<link rel=stylesheet type="text/css" href="minibar_quiz.css">
<meta name = "viewport" content = "width = device-width">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta charset="UTF-8">
<script type="text/JavaScript" src="support.js"></script>
<script type="text/JavaScript" src="minibar_quiz.js"></script>
<script type="text/JavaScript" src="pgf_online.js"></script>
<script type="text/JavaScript" src="quiz_support.js"></script>
<script type="text/javascript" src="translation_quiz.js"></script>
<script type="text/JavaScript" src="quiz_pre_start.js"></script>
</head>
<body>
<div id = "info" >
<a href="javascript:popUp('quiz_about.html', 'about')">About GF Translation Quiz</a>
<h2 style=" padding-left: 35px;"><i> Welcome to GF Translation Quiz </i></h2>
<p style="font-size:20px;">
To start the quiz choose a grammar, From and To languages
<br/> as well as a quiz mode, then press the "Start Quiz" button.
<br/> <b>Note:</b> You may not change these settings in the middle of
<br/> a quiz.
The quiz is over when you have done at least
<span style="color:#2E8B57;"><b><script type="text/javascript"> document.write(min_no_questions); </script></b></span>
<br/>examples with a minimum success of
<span style="color:#2E8B57;"><b><script type="text/javascript"> document.write(pass_percentage * 100); </script> % </b></span>
. However <br/> you can restart or quit the quiz
at any time by clicking <br/>on the "Restart Quiz" or "End Quiz" buttons.
</p>
<a style="font-size:18px;" href="javascript:popUp('quiz_help.html', 'help')">Click here to learn more about how the Quiz works.</a>
<br/>
</div>
<div id = "toggle_hide"; class="shift_left"; ><a href="javascript: toggle_info()">Hide Information</a></div>
<div id = "toggle_show"; class="shift_left"; ><a href="javascript: toggle_info()">Show Information</a></div>
<hr noshade size=0 />
<div id = "minibar1" class="bold">
</div>
<hr noshade size=0 />
<table frame="void" cellpadding="5" >
<tr>
<td><b>Quiz Question:</b></td>
</tr>
<tr>
<td>
<form name="question">
<textarea class="quest" name="question_text" wrap="virtual" readonly="true">
</textarea>
</form>
</td>
</tr>
</table>
<form name="answer" onsubmit="check_answer_quiz(); return false;">
<fieldset>
<legend><b>Your Answer: </b></legend>
<div id = "minibar_buttons" class="shift_left";></div>
<div id = "minibar_contin"></div>
<table cellpadding="2">
<tr>
<td>
<input class="answer1" type="text" id="user_answer" name="answer_text" value="" />
</td>
</tr>
<tr>
<td>
<div id="hint_txt" class="hint">
</td>
</tr>
<tr>
<td>
<div id = "buttons_bar"></div>
</td>
</tr>
<tr>
<td>
<div id = "history_bar"></div>
</td>
</tr>
</table>
</fieldset>
</form>
<table frame="void" cellpadding="5" >
<tr>
<td><b>Explanation:</b></td>
</tr>
<tr>
<td>
<form name="explanation">
<textarea class="explain" name="explanation_text" wrap="virtual" readonly="true">
</textarea>
</form>
</td>
</tr>
</table>
<table cellpadding="1" cellspacing="10">
<tr>
<td><b>Current Quiz Mode:</b></td>
<td><b>Answered Questions</b></td>
<td><b>Your Score</b></td>
</tr>
<tr>
<td><input type="text" id="mode" style="border: 0; text-align:left; color:#CD5C5C; background-color:#ccc;" class="number" size="18" readonly="true"/></td>
<td><input type="text" id="counter_display" class="number" size="4" readonly="true" value="0" /></td>
<td><input type="text" id="score_display" style="color:#2E8B57" class="number" size="3" readonly="true" value="0" /></td>
</tr>
</table>
<div id="history_part">
</div>
<script type="text/javascript">
var online_options={
//grammars_url: "http://www.grammaticalframework.org/grammars/"
//grammars_url: "http://tournesol.cs.chalmers.se:41296/grammars",
//grammars_url: "http://localhost:41296/grammars",
//grammar_list: ["Foods.pgf"], // leave undefined to get list from server
}
if(/^\?\/tmp\//.test(location.search)) {
online_options.grammars_url=location.search.substr(1);
}
var server=pgf_online(online_options);
var minibar_options= {
show_abstract: false,
show_trees: false,
show_grouped_translations: false,
default_source_language: "Eng",
try_google: false
}
var minibar=new Minibar(server,minibar_options,"minibar1");
pre_start();
</script>
</body>
</html>

View File

@@ -0,0 +1,766 @@
// Copyright © Elnaz Abolahrar, 2011
// translation_quiz.js, assumes that quiz_support.js has also been loaded
/*-------------------------------------- Configuration Variables --------------------------------------*/
var pass_percentage = 0.75;
var min_no_questions = 10;
var exam_quesNo = 10; // sets the number of questions appearing in an exam
var max_answer_times = 1; // the max limit for the user to increase his/her score
/*-------------------------------------- other Variables --------------------------------------*/
var grammar;
var question_lang;
var answer_lang;
//saves the random tree generated by the server in generate_question function to be used by the check_answer
var rand_tree;
var counter;
var score;
var confirmed_check = new Boolean(true);
var prev_pressed = new Boolean();
var prev_question;
var prev_explanation;
var prev_answer;
var prev_hint;
var current_question;
var current_answer;
var current_explanation= "";
var current_hint;
//for history
var quiz_history = [];
var history_shown= new Boolean();
var index =0;
//for check answer
var answer_was_right= new Boolean();
var raw_user_answer;
var user_answer;
var user_answer_splited;
var server_answer;
var all_answers = [];
var parsed_trees = 0;
var words_hidden = new Boolean();
var answer_times ;
var hint_times ;
var hint_pressed = new Boolean();
var restart_active = new Boolean();
var is_ended = new Boolean();
var info_hidden = new Boolean();
//modes variables
var have_minibar = new Boolean();
var have_prevQuestion = new Boolean();
var have_checkAns = new Boolean();
var max_hint_times;
var selected_mode;
/*-------------------------------------- Functions --------------------------------------*/
function default_values()
{
//resets the question, answer and the explanation and hint display areas
document.question.question_text.value= "Quiz questions will be displayed here.";
document.answer.answer_text.value = " ";
document.explanation.explanation_text.value= "Explanations are displayed here.";
document.getElementById("hint_txt").innerHTML = "";
//resets some flags
prev_pressed = false;
is_ended = false; //refering to the End button or end o quiz by score or end of exam
restart_active = false; //refering to the Restart button
hint_pressed = false;
answer_was_right= false;
history_shown= false;
//resets the counter and score displays and some variables
answer_times = 1;
counter = 0;
score = 0;
hint_times =0;
document.getElementById('counter_display').value = counter;
document.getElementById('score_display').value = score;
//resets the variables for keeping the history
quiz_history = [];
index =0;
var history=element("history_part");
history.innerHTML ="";
//resets the variables for keeping the parsing and linearization altenatives
parsed_trees = 0;
all_answers = [];
//shows the normal user answer area
show_element("user_answer");
}
function start_quiz()
{
//sets the grammar and From and To languages
grammar= minibar.grammar_menu.value;
question_lang= minibar.from_menu.value;
answer_lang= minibar.to_menu.value;
//removes the start button
minibar.quizbar.removeChild(minibar.quizbar.lastChild);
set_mode();
//shows the minibar area and buttons
var buttons_bar=element("buttons_bar");
if (have_minibar)
{
//show the minibar word magnets
show_element("minibar_contin");
//show the delete and clear buttons
show_element("minibar_buttons");
//hides the normal user answer area
hide_element("user_answer");
//changes the "Check Answer" button to a none_submit one
buttons_bar.removeChild(buttons_bar.lastChild);
appendChildren(buttons_bar,
[ button("Check Answer","check_answer_quiz()","C", "check_answer")]);
}
else
{
//enable the user answer area
document.getElementById('user_answer').disabled = false;
//hide the minibar word magnets
hide_element("minibar_contin");
//hide the delete and clear buttons
hide_element("minibar_buttons");
//changes the "Check Answer" button to a submit one
buttons_bar.removeChild(buttons_bar.lastChild);
appendChildren(buttons_bar,
[ submit_button("Check Answer", "check_answer")]);
}
//conditionally enbles the "Hint", "Check Answer", "Next Question" and "Previous Question" buttons, and the minibar
if (have_checkAns)
document.getElementById('check_answer').disabled = false;
else
document.getElementById('check_answer').disabled = true;
if (have_prevQuestion)
document.getElementById('previous_question').disabled = false;
if ( max_hint_times > 0 )
document.getElementById('hint').disabled = false;
document.getElementById('next_question').disabled = false;
//generates the first quiz question
generate_question();
//resets the restart_active
restart_active= true ;
//hides the information
if ( info_hidden == false )
toggle_info();
}
function restart_quiz()
{
//javascript:location.reload(true);
if (restart_active == true)
{
var end_confirmed= new Boolean(true);
if (!is_ended )
end_confirmed = confirm(" Are you sure you want to quit this quiz!");
if (end_confirmed)
{
end_quiz(false);
reset_mode();
//add the start button
appendChildren(minibar.quizbar,[ button("Start Quiz","start_quiz()","S")]);
//removes the History button
var history_bar=element("history_bar");
history_bar.removeChild(history_bar.lastChild);
default_values();
}
}
}
function generate_question()
{
//for the exam mode
if (selected_mode == "Exam Mode")
{
if (check_notEmpty())
{
if (counter > 0)
{
document.getElementById('score_display').value = "?";
//save the current user answer for history
current_answer = document.answer.answer_text.value;
quiz_history[index][1] = current_answer;
make_all_answers();
}
else
exam_continue();
}
}
else
{
if (prev_pressed == true )
{
//changes the question, answer and the explanation back to the current one
document.question.question_text.value= current_question;
document.answer.answer_text.value = current_answer;
document.explanation.explanation_text.value= current_explanation;
document.getElementById("hint_txt").innerHTML = current_hint;
prev_pressed = false; // to go back to normal
//enables the "Previous Question","Hint" and "Check Answer" buttons
document.getElementById('previous_question').disabled = false;
document.getElementById('check_answer').disabled = false;
document.getElementById('hint').disabled = false;
}
else
{
// if the user clicks the "Next Question" without cheking his/her previous answer,
// asks for user's confirmation before moving to the next question
if ( answer_times == 0 )
{
confirmed_check = confirm("Are you sure you don't want to check your answer's correctness!");
if ( hint_times == 0 )
{
//saves the current answer and the explanation which is null(the answer was not checked or hinted)
current_answer = document.answer.answer_text.value;
current_explanation = "You did not check your answer's correctness!";
current_hint = "";
}
//save the current user answer for history
quiz_history[index][1] = document.answer.answer_text.value;
//save the current explanation for history
quiz_history[index][2] = "You did not check your answer's correctness!";
}
if (confirmed_check == true )
{
//save the current question for the "previous question" button and history
prev_question = current_question;
//save the current answer for the "previous question" button
prev_answer = current_answer;
//save the current explanation and hint for the "previous question" button
prev_explanation = current_explanation;
prev_hint= current_hint;
//clears the question, answer and the explanation and hint display areas
clearing();
if (have_minibar)
{
minibar.clear_all();
//unhides the words area and removes the "Show Magnets" button
show_word_magnets();
}
if (answer_was_right) //here it still contains info about the previous question
document.explanation.explanation_text.value= current_explanation;
//resets the times user has answered to the current question
answer_times = 0;
//resets the times user has pressed hint for the current question
hint_times = 0;
document.getElementById('counter_display').value = counter;
//increments the counter
counter= ++ counter;
//sends a question retrival request to the server
server.get_random(generate_question2);
}
}
}
//sets the focus on the answer area
document.answer.answer_text.focus();
}
function generate_question2(random_trees)
{
//we now have a random abstract syntax tree that we need to linearize
server.pgf_call("linearizeAll",{tree:random_trees[0].tree,
to:question_lang}, generate_question3);
rand_tree= random_trees[0].tree;
}
function generate_question3(random_texts)
{
var no_of_lins = random_texts[0].texts.length;
//generates a random number, to determine which linearization should be used as the question
var which= Math.floor(Math.random()* no_of_lins );
if (which >= no_of_lins)
which = which - 1;
//display the new quiz quetion sent by server
document.question.question_text.value= counter +". " + random_texts[0].texts[which];
//save the current question for the "previous question" button and history
current_question = document.question.question_text.value;
index = quiz_history.length;
quiz_history[index] = [];
quiz_history[index][0]= current_question;
}
function check_answer_quiz()
{
if (check_notEmpty())
{
//resets the user confirmation for moving to next question without checking the answer
confirmed_check = true;
//clears the explanation and Hint display area
document.explanation.explanation_text.value = "";
document.getElementById("hint_txt").innerHTML = "";
//save the current user answer for the "peivious question" button and history
current_answer = document.answer.answer_text.value;
quiz_history[index][1] = current_answer;
//increments the times the user has answered to the current question
answer_times ++;
//resets the hint_pressed flag
hint_pressed = false;
if ((answer_times + hint_times) <= 1)
make_all_answers();
else
continue_checking();
}
}
function make_all_answers()
{
//resets the variables for keeping the parsing and linearization altenatives
parsed_trees = 0;
all_answers = [];
//we now need to linearize the random abstract syntax tree in order to
//find all possible correct answers then we check user's answer against these answers.
server.pgf_call("linearize",{tree:rand_tree, to:question_lang}, parse_answer);
}
function parse_answer(right_answer)
{
server.parse(question_lang,right_answer[0].text, parse_answer2);
}
var trees_to_go;
function parse_answer2(parsed_answer)
{
trees_to_go = parsed_trees = parsed_answer[0].trees.length;
var j =0;
for (j= 0; j < parsed_trees ; j++)
{
server.linearizeAll(parsed_answer[0].trees[j], answer_lang, collect_answers);
}
}
function collect_answers(lin_answer)
{
var next= 0;
var i=0;
for (i= 0; i < lin_answer[0].texts.length ; i++)
{
next = all_answers.length;
all_answers[next]= lin_answer[0].texts[i];
}
trees_to_go--;
if(trees_to_go == 0)
continue_checking();
}
function continue_checking()
{
if (selected_mode == "Exam Mode")
{
check_answer_exam2();
exam_continue();
}
else
if (hint_pressed)
show_hint2();
else
check_answer2();
}
function check_answer2()
{
if (have_minibar)
hide_word_magnets();
raw_user_answer= document.answer.answer_text.value;
user_answer = remove_unwanted_characters(raw_user_answer);
answer_was_right= false;
var k = 0 ;
for (k= 0; k < all_answers.length; k++)
{
server_answer = remove_unwanted_characters(all_answers[k]);
if ( user_answer == server_answer )
{
if (answer_times <= max_answer_times)
{
//increments the score
score ++;
document.getElementById('score_display').value = score;
}
document.explanation.explanation_text.value = "Yes, that was the correct answer.";
answer_was_right= true;
break;
}
}
if (k >= all_answers.length)
{
document.explanation.explanation_text.value= "No, the correct answer(s) is(are): \n "
+ all_answers;
document.getElementById('counter_display').value = counter;
}
//save the current explanation for the "previous question" button and history
current_explanation = document.explanation.explanation_text.value;
quiz_history[index][2] = current_explanation;
if ((counter >= min_no_questions) && ((score/counter) >= pass_percentage))
{
if (answer_was_right)
document.explanation.explanation_text.value += "\nAlso, ";
else
document.explanation.explanation_text.value += "\nHowever, ";
document.explanation.explanation_text.value += "Congratulations!!! You passed the quiz. Click \"Restart Quiz\" for a new one.";
document.getElementById('counter_display').value = counter;
end_quiz(false);
}
else
if (answer_was_right)
{
//goes to the next question automaticly after getting the correct answer
generate_question();
}
//save the current hint for the "previous question" button
current_hint = document.getElementById("hint_txt").innerHTML ;
}
function check_answer_exam2()
{
raw_user_answer= document.answer.answer_text.value;
user_answer = remove_unwanted_characters(raw_user_answer);
var k = 0 ;
for (k= 0; k < all_answers.length; k++)
{
server_answer = remove_unwanted_characters(all_answers[k]);
if ( user_answer == server_answer )
{
//increments the score
score ++;
//save the current explanation for history
current_explanation = "Yes, that was the correct answer.";
quiz_history[index][2] = current_explanation;
break;
}
}
if (k >= all_answers.length)
{
//save the current explanation for history
current_explanation = "No, the correct answer(s) is(are): \n " + all_answers;
quiz_history[index][2] = current_explanation;
}
}
function exam_continue()
{
if (counter >= exam_quesNo)
exam_result();
else
{
//clears the question and answer and the explanation and hint display areas
clearing();
document.getElementById('counter_display').value = counter + "/" + exam_quesNo;
//increments the counter
counter= ++ counter;
//sends a question retrival request to the server
server.get_random(generate_question2);
}
}
function exam_result()
{
document.getElementById('score_display').value = score;
document.getElementById('counter_display').value = counter + "/" + exam_quesNo;
document.explanation.explanation_text.value= "That's the end of exam.";
end_quiz(false);
}
function show_hint()
{
if (hint_times < max_hint_times)
{
if (check_notEmpty())
{
//clears the explanation and Hint area
document.explanation.explanation_text.value = "";
document.getElementById("hint_txt").innerHTML = "";
//increments the times the user has pressed hint button
hint_times= ++ hint_times;
//resets the user confirmation for moving to next question without checking the answer
confirmed_check = true;
//save the current user answer for the "peivious question" button
current_answer = document.answer.answer_text.value;
//sets the hint_pressed flag
hint_pressed = true;
raw_user_answer= document.answer.answer_text.value;
user_answer = remove_unwanted_characters(raw_user_answer);
user_answer_splited = split_to_words(user_answer);
if ((answer_times + hint_times) <= 1)
make_all_answers();
else
continue_checking();
}
}
else
{
//Error message (max_hint_times is passed)
document.explanation.explanation_text.value = "Sorry, you have already used up your allowed number of hints for this question.";
}
}
function show_hint2()
{
var best_answer = new Array();
best_answer= find_closest(all_answers);
var compared = new Array();
compared = string_matching(best_answer, user_answer_splited);
//preparing the Hint spans
var hint = element("hint_txt");
hint.innerHTML = "<b>Hint: </b>";
var max_length = Math.max(best_answer.length, user_answer_splited.length);
for (k= 0; k < max_length; k++)
{
var id = "word" + k.toString();
var word_span= span_id(id);
hint.appendChild(word_span);
}
var k=0;
var visited = new Array();
for (k= 0; k < best_answer.length; k++)
{
visited[k] = 0;
}
var i= 0; //i is used for the user_answer
var myid = "";
while ( i < compared.length)
{
myid = "word" + i.toString();
if (compared[i] == 0)
{
var j =0; //j is used for the server_answer
while (j < best_answer.length)
{
if (visited[j] == 0 && user_answer_splited[i] == best_answer[j] && (j >= compared.length || compared[j] == 0 ))
{
//yellow for the right word in wrong place
document.getElementById(myid).style.color="#FFFF00";
visited[j] = 1;
break;
}
j++;
}
if ( j >= best_answer.length)
{
//red for the totaly wrong word
document.getElementById(myid).style.color="#FF0000";
}
}
else
{ //green for the right word in right place
document.getElementById(myid).style.color="#339933";
}
document.getElementById(myid).innerHTML = user_answer_splited[i] + " ";
i++;
}
while ( i < best_answer.length)
{
myid = "word" + i.toString();
document.getElementById(myid).style.color="#FF0000";
document.getElementById(myid).innerHTML = "____ ";
i++;
}
//save the current explanation and hint for the "previous question" button
current_explanation = document.explanation.explanation_text.value;
current_hint = document.getElementById("hint_txt").innerHTML ;
}
function previous_question_quiz()
{
if ( counter > 1)
{
if ( answer_times == 0 && hint_times == 0 )
{
//sets the current answer and the explanation and hint
current_answer = document.answer.answer_text.value;
current_explanation = "";
current_hint = "";
}
//disables the "Previous Question","Hint" and "Check Answer" buttons
document.getElementById('previous_question').disabled = true;
document.getElementById('check_answer').disabled = true;
document.getElementById('hint').disabled = true;
//changes the question, answer and the explanation
document.question.question_text.value= prev_question;
document.answer.answer_text.value = prev_answer;
document.explanation.explanation_text.value= prev_explanation;
document.getElementById("hint_txt").innerHTML = prev_hint;
prev_pressed = true; // to remember the current question, answer and explanation
}
}
function end_quiz(confirm_needed)
{
if (restart_active == true && is_ended == false)
{
var end= new Boolean(true);
if (confirm_needed)
end = confirm(" Are you sure you want to quit this quiz!");
if (end)
{
if (have_minibar)
{
//removes the "Show Magnets" button if exists
show_word_magnets();
//remove the minibar word magnets if there was any
remove_minibar();
//shows the normal user answer area
show_element("user_answer");
}
disable_all();
is_ended = true;
//adds a Show Quiz History button
var history_bar=element("history_bar");
appendChildren(history_bar,
[ button("Show Quiz History","show_history()","H", "quiz_history")]);
}
}
}
function show_history()
{
var current_grammar = minibar.grammar.name; // minibar.grammar.name gives the grammar name without .pgf
var to_lang= langpart(minibar.to_menu.value,current_grammar);
var from_lang= langpart(minibar.from_menu.value,current_grammar);
var history=element("history_part");
if (history_shown == false)
{
var i=0;
for (i= 0; i < quiz_history.length ; i++)
{
var question = text("Question" + " "+quiz_history[i][0] + "\n");
history.appendChild(empty("br"));
history.appendChild(question);
history.appendChild(empty("br"));
var answer = text("Your Answer" + ": "+ quiz_history[i][1] + "\n");
history.appendChild(answer);
history.appendChild(empty("br"));
var explan = text("Explanation" + ": "+ quiz_history[i][2] + "\n");
history.appendChild(explan);
history.appendChild(empty("br"));
history.appendChild(empty("br"));
}
history_shown = true;
}
var history_content = history.innerHTML;
var history_window = window.open('', 'historyPopup', "dependent = 1, scrollbars=1, location=1, statusbar=1, width=540, height=650, left = 10, top = 20");
history_window.document.writeln('<html><head><title>Quiz History</title></head><body>');
history_window.document.writeln('<h3> Your Quiz History </h3>');
history_window.document.write('<b> Quiz Mode: </b>');
history_window.document.write(selected_mode+ ", ");
history_window.document.write('<b> Grammar: </b> ');
history_window.document.write(" "+ current_grammar +", ");
history_window.document.write('<b> From: </b>');
history_window.document.write(" "+ from_lang + ", ");
history_window.document.write('<b> To: </b>');
history_window.document.writeln(" "+ to_lang);
history_window.document.writeln('<div style="font-size:18px;">');
history_window.document.writeln(history_content);
history_window.document.writeln('</div>');
history_window.document.writeln('<div style="color: #2E8B57; font-size:16px; font-weight: bold;"><a href="javascript: window.print()">Print History</a></div>');
history_window.document.writeln('<div style="padding-left: 320px; color: #2E8B57; font-size:16px; font-weight: bold;"><a href="javascript: window.close()">Close page</a></div>');
history_window.document.writeln('</body></html>');
history_window.document.close();
}

View File

@@ -0,0 +1,20 @@
body { color: black; background: #eee; }
h1 { font-size: 175%; }
h1,h2,h3,h4,small { font-family: sans-serif;}
h1,h2,h3,h4 { color: #303030; text-shadow: rgba(0,0,0,0.25) 3px 3px 5px; }
h1:first-child, h2:first-child { margin-top: 0; margin-bottom: 1ex; }
h1 img { float: right; border: 0; max-width: 50%; }
h1 img.nofloat { float: none; }
img.cloud, img.right, div.right, div.modtime, div.version { float: right; }
li { margin-top: 0.5ex; margin-bottom: 0.5ex; }
div.modtime >small { visibility: hidden; }
div.modtime:hover >small { visibility: visible; }
.modtime,.version { color: #999; white-space: nowrap; }
table.loaded_grammars th { text-align: left; font-family: sans-serif; }
table.loaded_grammars td { padding-right: 1em; font-family: monospace; }
table.loaded_grammars tr td:nth-child(2) { color: #555; }

View File

@@ -0,0 +1,160 @@
<!DOCTYPE html>
<html> <head>
<title>GF Cloud Service API (preliminary)</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="gfse/editor.css" title="Cloud">
<meta name = "viewport" content = "width = device-width">
<style type="text/css">
dl.api>dt, .request { background-color: #cef; }
.response { background-color: #ffc; }
dd { margin-top: 0.5ex; margin-bottom: 0.5ex; }
em { color: #36f; }
</style>
</head>
<body>
<h1><a href="./"><img src="P/gf-cloud.png" alt=""></a> GF Cloud Service API (preliminary)</h1>
The GF cloud service API provides the following functionality:
<ul>
<li>the functionality available in via the <strong>PGF service API</strong>,
<li>the functionality provided by the commands in the
<strong>GF shell</strong>,
<li>some additional services for
grammar compilation and persistent storage of files in the cloud.
</ul>
<p>
<strong>This is preliminary and subject to change!</strong>
<h2>Availability and protocol</h2>
<p>
The service is available from
<a href="http://cloud.grammaticalframework.org/"><code>http://cloud.grammaticalframework.org/</code></a>.
Users that have GF installed on their own computer can also
run the service locally by starting GF with the command <code>gf -server</code>.
<p>
Requests are made via HTTP with the GET or POST method. (The examples below
show GET requests, but POST is preferred for requests that change the state
on the server.)
<p>
Data in requests is in the <code>application/x-www-form-urlencoded</code> format
(the format used by default by web browsers when submitting form data).
<p>
Data in responses is usually in JSON format.
The HTTP response code is usually 200, but can also be
204 (after file upload),
404 (file to download or remove was not found),
400 (for unrecognized commands or missing/unacceptable parameters in requests)
or
501 (for unsupported HTTP request methods).
Unrecognized parameters in requests are silently ignored.
<h2>Requests</h2>
<h3>PGF Service</h3>
The GF Cloud Service supports the same set of PGF service requests as the
already available PGF web service. For example, a request like
<blockquote class=request>
<code>http://cloud.grammaticalframework.org/grammars/Foods.pgf?command=random</code>
</blockquote>
might return a result like
<blockquote class=response>
<code>[{"tree":"Pred (That Pizza) (Very Boring)"}]</code>
</blockquote>
<p>
For more info, see:
<ul>
<li><a href="gf-web-api-examples.html">PGF web service API examples</a>.
<li><a href="http://code.google.com/p/grammatical-framework/wiki/GFWebServiceAPI">PGF web service API documentation</a>.
</ul>
<h3>GF Shell Service</h3>
This service lets you execute arbitrary GF shell commands. Before you can do
this, you need to use the <code class=request>/new</code> command to obtain a
working directory (which also serves as a session identifier) on the server,
see below.
<dl class=api>
<dt><code>/gfshell?dir=</code>...<code>&amp;command=i+Foods.pgf</code>
<dd>&nbsp;
<dt><code>/gfshell?dir=</code>...<code>&amp;command=gr</code>
<dd class=response><code>Pred (That Pizza) (Very Boring)</code>
<dt><code>/gfshell?dir=</code>...<code>&amp;command=ps+-lextext+%22That+pizza+is+very+boring.%22</code>
<dd class=response><code>that pizza is very boring .</code>
</dl>
For documentation of GF shell commands, see:
<ul>
<li><a href="http://www.grammaticalframework.org/doc/gf-shell-reference.html">GF Shell Reference</a>
</ul>
<h3>Additional cloud service</h3>
<dl class=api>
<dt><code>/new</code>
<dd>This generates a new working directory on the server, e.g.
<code class=response>/tmp/gfse.123456</code>.
Most of the cloud service commands require that a working directory
is specified in the <code class=request>dir</code> parameter.
The working directory is persistent, so clients are expected
to remember and reuse it. Access to previously
uploaded files requires that the same working directory is used.
<dt><code>/parse?</code><var>path</var><code>=</code><var>source</var>
<dd>This command can be used to check GF source code for syntax errors.
It also converts GF source code to the JSON representation used in
GFSE (the cloud-based GF grammar editor).
<dt><code>/cloud?dir=</code>...<code>&amp;command=<strong>upload</strong>&amp;</code><var>path<sub>1</sub></var><code>=</code><var>source<sub>1</sub></var><code>&amp;</code><var>path<sub>2</sub></var><code>=</code><var>source<sub>2</sub></var><code>&amp;</code>...
<dd>Upload files to be stored in the cloud.
The response code is 204 if the upload was successful.
<dt><code>/cloud?dir=</code>...<code>&amp;command=<strong>make</strong>&amp;</code><var>path<sub>1</sub></var><code>=</code><var>source<sub>1</sub></var><code>&amp;</code><var>path<sub>2</sub></var><code>=</code><var>source<sub>2</sub></var><code>&amp;</code>...
<dd>Upload grammar files and compile them into a PGF file. Example response:
<blockquote class=response><code>
{ "errorcode":"OK", <em>// "OK" or "Error"</em>
<br>  "command":"gf -s -make FoodsEng.gf FoodsSwe.gf FoodsChi.gf",
<br>  "output":"\n\n" <em>// Warnings and errors from GF</em>
<br>}</code></blockquote>
<dt><code>/cloud?dir=</code>...<code>&amp;command=<strong>remake</strong>&amp;</code><var>path<sub>1</sub></var><code>=</code><var>source<sub>1</sub></var><code>&amp;</code><var>path<sub>2</sub></var><code>=</code><var>source<sub>2</sub></var><code>&amp;</code>...
<dd>Like <code>command=<strong>make</strong></code>, except you can leave
the <var>source<sub>i</sub></var> parts empty to reuse previously uploaded
files.
<dt><code>/cloud?dir=</code>...<code>&amp;command=<strong>download</strong>&amp;file=</code><var>path</var>
<dd>Download the specified file.
<dt><code>/cloud?dir=</code>...<code>&amp;command=<strong>ls</strong>&amp;ext=.pgf</code>
<dd>List files with the specified extension, e.g.
<code class=response>["Foods.pgf","Letter.pgf"]</code>.
<dt><code>/cloud?dir=</code>...<code>&amp;command=<strong>rm</strong>&amp;file=</code><var>path</var>
<dd>Remove the specified file.
<dt><code>/cloud?dir=</code>...<code>&amp;command=<strong>link_directories</strong>&newdir=</code>...
<dd>Combine server directores. This is used by GFSE to share grammars
between multiple devices.
</dl>
<hr>
<address></address>
<div class=modtime><small>
<!-- hhmts start -->Last modified: Tue Jun 17 10:42:21 CEST 2014 <!-- hhmts end -->
</small></div>
<address>
<a href="http://www.cse.chalmers.se/~hallgren/">TH</a>
<img src="http://www.altocumulus.org/~hallgren/online.cgi?icon" alt="">
</address>
</body>
</html>

View File

@@ -0,0 +1,183 @@
<!DOCTYPE html>
<html>
<head>
<title>GF web services API examples</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="gfse/editor.css" title="Cloud">
<meta name = "viewport" content = "width = device-width">
<style type="text/css">
dt { background: #cef; }
dt.js { background: white; margin-bottom: 1ex; }
dt.js em { color: #36f; }
dd { background: #ffc; margin-top: 1ex; margin-bottom: 1ex; }
dt, dd { padding: 0.3ex; }
dl.apiexamples>dt, dl.apiexamples>dd { font-family: monospace; }
dl.apiexamples>dd { white-space: pre; }
@media projection {
div.intro { display: none; }
body {
font-size: 150%;
}
h2 { page-break-before: always; }
dl.apiexamples dd {
page-break-after: always;
/*border-style: none;*/
}
}
</style>
<body>
<h1><a href="../"><img src="../P/gf-cloud.png" alt=""></a>
GF web services API examples</h1>
GF can be used interactively from the GF Shell. Some of the functionality
availiable in the GF shell is also available via the GF web services API.
<p>
The
<a href="gf-web-api.html">GF
Web Service API page</a> describes the calls supported by the GF web service
API. Below, we illustrate these calls by examples, and also show
how to make these calls from JavaScript using the API defined in
<a href="js/pgf_online.js"><code>pgf_online.js</code></a>.
<p>
<strong>Note</strong> that <code>pgf_online.js</code> was initially developed
with one particular web application in mind (the minibar), so the server API was
incomplete. It was simplified and generalized in August 2011 to support the
full API.
<dl>
<dt class=js>These boxes show what the calls look like in the JavaScript
API defined in <code>pgf_online.js</code>.
<dt>These boxes show the corresponding URLs sent to the PGF server.
<dd>These boxes show the JSON (JavaScript data structures) returned by the PGF
server. This will be passed to the callback function supplied in the
call.
</dl>
<h2>Initialization</h2>
<dl class=apiexamples>
<dt class=js>
<em>// Select which server and grammars to use:</em>
<br>var server_options = {
<br>&nbsp;&nbsp;grammars_url: "http://www.grammaticalframework.org/grammars/",
<br>&nbsp;&nbsp;grammar_list: ["Foods.pgf"] <em>// It's ok to skip this</em>
<br>}
<br>var server = pgf_online(server_options);
</dl>
<h2>Examples</h2>
<dl class=apiexamples>
<dt class=js> <em>// Get the list of available grammars</em>
<br>server.get_grammarlist(callback)
<dt>http://localhost:41296/grammars/grammars.cgi
<dd>["Foods.pgf","Phrasebook.pgf"]
<dt class=js> <em>// Select which grammar to use</em>
<br>server.switch_grammar("Foods.pgf")
<dt class=js><em>// Get list of concrete languages and other grammar info</em>
<br>server.grammar_info(callback)
<dt>http://localhost:41296/grammars/Foods.pgf
<dd>{"name":"Foods",
"userLanguage":"FoodsEng",
"startcat":"Comment",
"categories":["Comment","Float","Int","Item","Kind","Quality","String"],
"functions":["Boring","Cheese","Delicious","Expensive","Fish","Fresh",
"Italian","Mod","Pizza","Pred","That","These","This","Those","Very",
"Warm","Wine"],
"languages":[{"name":"FoodsBul","languageCode":""},
{"name":"FoodsEng","languageCode":"en-US"},
{"name":"FoodsFin","languageCode":""},
{"name":"FoodsSwe","languageCode":"sv-SE"},
...]
}
<dt class=js><em>// Get a random syntax tree</em>
<br>server.get_random({},callback)
<dt>http://localhost:41296/grammars/Foods.pgf?command=random
<dd>[{"tree":"Pred (That Pizza) (Very Boring)"}]
<dt class=js><em>// Linearize a syntax tree</em>
<br>server.linearize({tree:"Pred (That Pizza) (Very Boring)",to:"FoodsEng"},callback)
<dt>http://localhost:41296/grammars/Foods.pgf?command=linearize&amp;tree=Pred+(That+Pizza)+(Very+Boring)&amp;to=FoodsEng
<dd>[{"to":"FoodsEng","text":"that pizza is very boring"}]
<dt class=js>server.linearize({tree:"Pred (That Pizza) (Very Boring)"},callback)
<dt>http://localhost:41296/grammars/Foods.pgf?command=linearize&amp;tree=Pred+(That+Pizza)+(Very+Boring)
<dd>[{"to":"FoodsBul","text":"онази пица е много еднообразна"},
{"to":"FoodsEng","text":"that pizza is very boring"},
{"to":"FoodsFin","text":"tuo pizza on erittäin tylsä"},
{"to":"FoodsSwe","text":"den där pizzan är mycket tråkig"},
...
]
<dt class=js><em>// Parse a string</em>
<br>server.parse({from:"FoodsEng",input:"that pizza is very boring"},callback)
<dt>http://localhost:41296/grammars/Foods.pgf?command=parse&amp;input=that+pizza+is+very+boring&amp;from=FoodsEng
<dd>[{"from":"FoodsEng",
"brackets":{"cat":"Comment","fid":10,"index":0,"children":[{"cat":"Item","fid":7,"index":0,"children":[{"token":"that"},{"cat":"Kind","fid":6,"index":0,"children":[{"token":"pizza"}]}]},{"token":"is"},{"cat":"Quality","fid":9,"index":0,"children":[{"token":"very"},{"cat":"Quality","fid":8,"index":0,"children":[{"token":"boring"}]}]}]},
"trees":["Pred (That Pizza) (Very Boring)"]}]
<dt class=js><em>// Translate to all available languages</em>
<br>server.translate({from:"FoodsEng",input:"that pizza is very boring"},callback)
<dd>...
<dt class=js><em>// Translate to one language</em>
<br>server.translate({input:"that pizza is very boring", from:"FoodsEng", to:"FoodsSwe"}, callback)
<dt>http://localhost:41296/grammars/Foods.pgf?command=translate&amp;input=that+pizza+is+very+boring&amp;from=FoodsEng&amp;to=FoodsSwe
<dd>[{"from":"FoodsEng",
"brackets":{"cat":"Comment","fid":10,"index":0,"children":[{"cat":"Item","fid":7,"index":0,"children":[{"token":"that"},{"cat":"Kind","fid":6,"index":0,"children":[{"token":"pizza"}]}]},{"token":"is"},{"cat":"Quality","fid":9,"index":0,"children":[{"token":"very"},{"cat":"Quality","fid":8,"index":0,"children":[{"token":"boring"}]}]}]},
"translations":
[{"tree":"Pred (That Pizza) (Very Boring)",
"linearizations":
[{"to":"FoodsSwe",
"text":"den där pizzan är mycket tråkig"}]}]}]
<dt class=js><em>// Get completions (what words could come next)</em>
<br>server.complete({from:"FoodsEng",input:"that pizza is very "},callback)
<dt>http://localhost:41296/grammars/Foods.pgf?command=complete&amp;input=that+pizza+is+very+&amp;from=FoodsEng
<dd>[{"from":"FoodsEng",
"brackets":{"cat":"_","fid":0,"index":0,"children":[{"cat":"Item","fid":7,"index":0,"children":[{"token":"that"},{"cat":"Kind","fid":6,"index":0,"children":[{"token":"pizza"}]}]},{"token":"is"},{"token":"very"}]},
"completions":["boring","delicious","expensive","fresh","Italian","very","warm"],
"text":""}]
<dt class=js><em>// Get info about a category in the abstract syntax</em>
<br>server.browse({id:"Kind"},callback)
<dt>http://localhost:41296/grammars/Foods.pgf?command=browse&amp;id=Kind&amp;format=json
<dd>{"def":"cat Kind",
"producers":["Cheese","Fish","Mod","Pizza","Wine"],
"consumers":["Mod","That","These","This","Those"]}
<dt class=js><em>// Get info about a function in the abstract syntax</em>
<br>server.browse({id:"This"},callback)
<dt>http://localhost:41296/grammars/Foods.pgf?command=browse&amp;id=This&amp;format=json
<dd>{"def":"fun This : Kind -> Item","producers":[],"consumers":[]}
<dt class=js><em>// Get info about all categories and functions in the abstract syntax</em>
<br>server.browse({},callback)
<dt>http://localhost:41296/grammars/Foods.pgf?command=browse&amp;format=json
<dd>{"cats":{"Kind":{"def":"cat Kind",
"producers":["Cheese","Fish","Mod","Pizza","Wine"],
"consumers":["Mod","That","These","This","Those"]},
...},
"funs":{"This":{"def":"fun This : Kind -> Item","producers":[],"consumers":[]},
...}
}
<dt class=js><em>// Convert an abstract syntax tree to JSON</em>
<br>server.pgf_call("abstrjson",{tree:"Pred (That Pizza) (Very Boring)"},callback)
<dt>http://localhost:41296/grammars/Foods.pgf?command=abstrjson&amp;tree=Pred+(That+Pizza)+(Very+Boring)
<dd>{"fun":"Pred","fid":4,
"children":[{"fun":"That","fid":1,
"children":[{"fun":"Pizza","fid":0}]},
{"fun":"Very","fid":3,
"children":[{"fun":"Boring","fid":2}]}]}
<dt class=js><em>// Lookup the morphological analysis of a word</em>
<br>server.pgf_call("lookupmorpho",{input:"fish",from:"FoodsEng"},callback)
<dt>http://localhost:41296/grammars/Foods.pgf?command=lookupmorpho&amp;input=fish&amp;from=FoodsEng
<dd>[{"lemma":"Fish","analysis":"s Pl"},{"lemma":"Fish","analysis":"s Sg"}]
</dl>
<hr>
<div class=modtime><small>
<!-- hhmts start -->Last modified: Thu Jun 16 17:08:04 CEST 2016 <!-- hhmts end -->
</small></div>
<address><a href="http://www.cse.chalmers.se/~hallgren/">TH</a></address>

View File

@@ -0,0 +1,358 @@
GF Web Service API
June 2016
%!style:cloud.css
%!options(html): --toc
%!options(html): --toc-level=3
%!postproc(html): <TITLE> <meta charset="UTF-8"><meta name = "viewport" content = "width = device-width"> <TITLE>
%!postproc(html): <H1> <H1><a href="http://www.grammaticalframework.org/"><IMG src="Logos/gf0.png"></a>
==Introduction==
The PGF API is available as Web Service through the built-in HTTP server in
the main GF executable. It is activated by starting GF with the ``-server``
flag:
```
$ gf -server
This is GF version 3.8.
Document root = /usr/share/gf-3.8/www
Starting HTTP server, open http://localhost:41296/ in your web browser.
```
A compiled GF grammar (a ``.pgf`` file) can be used in web applications
%in the same way as JSP, ASP or PHP pages are used.
by placing it somewhere under the document root.
%, usually in the ``grammars/`` subdirectory.
When there is a request for access to a ``.pgf`` file, the GF web server
will load and cache the grammar and interpret any parameters included in the URL
(in the //url-encoded-query// format). The response you get back is usually
a data structure in [JSON http://www.json.org/] format,
but it could also be an image or plain text.
For example, if ``my_grammar.pgf`` is a grammar placed directly under the
document root, then the grammar could be accessed using this URL:
```
http://localhost:41296/my_grammar.pgf
```
The default when no parameters are included in the URL is a response with
some general information about the grammar, encoded in JSON format.
To perform specific command you have to tell what command you want to perform.
The command is encoded in the parameter ``command``, i.e.:
``http://localhost/my_grammar.pgf?command=``//cmd//
where //cmd// is the name of the command. Most commands require
additional arguments, which are encoded as parameters as well.
The supported commands and their arguments are described below.
==Commands==
--------------------------------------------------------------------------------
===Grammar===
This command provides some general information about the grammar. This command is also executed if no `command` parameter is given.
=====Input=====
|| Parameter | Description | Default |
| command | should be ``grammar`` | - |
=====Output=====
A JSON object including the following fields:
|| Field | Description |
| ``name`` | the name of the abstract syntax in the grammar |
| ``userLanguage`` | the concrete language in the grammar which best matches the default language set in the user's browser |
| ``categories`` | list of all abstract syntax categories defined in the grammar |
| ``functions`` | list of all abstract syntax functions defined in the grammar |
| ``languages`` | list of concrete languages available in the grammar |
Every language is described with object having this two fields:
|| Field | Description |
| ``name`` | the name of the concrete syntax for the language |
| ``languageCode`` | the two-character language code according to the [ISO standard http://www.loc.gov/standards/iso639-2/php/code_list.php] i.e. ``en`` for English, ``bg`` for Bulgarian, etc. |
The language codes need to be specified in the grammar with
``flags language=...``. The web service receives the code of the language
set in the browser and compares it with the codes
defined in the grammar. If there is a match then the service returns
the corresponding concrete syntax name. If no match is found then the
first language in alphabetical order is returned.
--------------------------------------------------------------------------------
===Parsing===
This command parses a string and returns a list of abstract syntax trees.
====Input====
|| Parameter | Description | Default |
| ``command`` | should be ``parse`` | - |
| ``cat`` | the start category for the parser | the default start category for the grammar |
| ``input`` | the string to be parsed | empty string |
| ``from`` | the name of the concrete syntax to use for parsing | all languages in the grammar will be tried |
| ``limit`` | limit how many trees are returned (gf>3.3.3) | no limit is applied |
====Output====
List of objects where every object represents the analyzes for every input language. The objects have three fields:
|| Field | Description |
| ``from`` | the concrete language used in the parsing |
| ``brackets`` | the bracketed string from the parser |
| ``trees`` | list of abstract syntax trees |
| ``typeErrors`` | list of errors from the type checker |
The abstract syntax trees are sent as plain strings. The type errors are objects with two fields:
|| Field | Description |
| ``fid`` | forest id which points to a bracket in the bracketed string where the error occurs |
| ``msg`` | the text message for the error |
The current implementation either returns a list of abstract syntax trees or a list of type errors. By checking whether the field trees is not null we check whether the type checking was successful.
--------------------------------------------------------------------------------
===Linearization===
The command takes an abstract syntax tree and produces string in the specified language(s).
====Input====
|| Parameter | Description | Default |
| ``command`` | should be ``linearize`` | - |
| ``tree`` | the abstract syntax tree to linearize | - |
| ``to`` | the name of the concrete syntax to use in the linearization | linearizations for all languages in the grammar will be generated |
====Output====
|| Field | Description |
| ``to`` | the concrete language used for the linearization |
| ``tree`` | the output text |
--------------------------------------------------------------------------------
===Translation===
The translation is a two step process. First the input sentence is parsed with the source language and after that the output sentence(s) are produced via linearization with the target language(s). For that reason the input and the output for this command is the union of the input/output of the commands for parsing and the one for linearization.
====Input====
|| Parameter | Description | Default |
| ``command`` | should be ``translate`` | - |
| ``cat`` | the start category for the parser | the default start category for the grammar |
| ``input`` | the input string to be translated | empty string |
| ``from`` | the source language | all languages in the grammar will be tried |
| ``to`` | the target language | linearizations for all languages in the grammar will be generated |
| ``limit`` | limit how many parse trees are used (gf>3.3.3) | no limit is applied |
====Output====
The output is a list of objects with these fields:
|| Field | Description |
| ``from`` | the concrete language used in the parsing |
| ``brackets`` | the bracketed string from the parser |
| ``translations`` | list of translations |
| ``typeErrors`` | list of errors from the type checker |
Every translation is an object with two fields:
| ``tree`` | abstract syntax tree |
| ``linearizations`` | list of linearizations |
Every linearization is an object with two fields:
| Field | Description |
| ``to`` | the concrete language used in the linearization |
| ``text`` | the sentence produced |
The type errors are objects with two fields:
|| Field | Description |
| ``fid`` | forest id which points to a bracket in the bracketed string where the error occurs |
| ``msg`` | the text message for the error |
The current implementation either returns a list of translations or a list of type errors. By checking whether the field translations is not null we check whether the type checking was successful.
--------------------------------------------------------------------------------
===Random Generation===
This command generates random abstract syntax tree where the top-level function will be of the specified category. The categories for the sub-trees will be determined by the type signatures of the parent function.
====Input====
|| Parameter | Description | Default |
| ``command`` | should be ``random`` | - |
| ``cat`` | the start category for the generator | the default start category for the grammar |
| ``limit`` | maximal number of trees generated | 1 |
====Output====
The output is a list of objects with only one field:
|| Field | Description |
| ``tree`` | the generated abstract syntax tree |
The length of the list is limited by the limit parameter.
--------------------------------------------------------------------------------
===Word Completion===
Word completion is a special case of parsing. If there is an incomplete sentence then it is first parsed and after that the state of the parse chart is used to predict the set of words that could follow in a grammatically correct sentence.
====Input====
|| Parameter | Description | Default |
| ``command`` | should be ``complete`` | - |
| ``cat`` | the start category for the parser | the default start category for the grammar |
| ``input`` | the string to the left of the cursor that is already typed | empty string |
| ``from`` | the name of the concrete syntax to use for parsing | all languages in the grammar will be tried |
| ``limit`` | maximal number of trees generated | all words will be returned |
====Output====
The output is a list of objects with two fields which describe the completions.
|| Field | Description |
| ``from`` | the concrete syntax for this word |
| ``text`` | the word itself |
--------------------------------------------------------------------------------
===Abstract Syntax Tree Visualization===
This command renders an abstract syntax tree into an image.
Several image formats are supported.
====Input====
|| Parameter | Description | Default |
| ``command`` | should be ``abstrtree`` | - |
| ``tree`` | the abstract syntax tree to render | - |
| ``format`` | output format (gf>3.3.3) | ``png`` |
====Output====
By default, the output is an image in PNG format.
The Content-Type is set to ``image/png``, so the easiest way to visualize the
generated image is to add HTML element ``<img/>`` which points to URL
for the visualization command i.e.:
```
<img src="http://localhost/my_grammar.pgf?command=abstrtree&tree=..."/>
```
The ``format`` parameter can also be ``gif``, ``svg`` or ``gv``, for
GIF (``image/gif``), SVG (``image/svg+xml``)
or graphviz (``text/plain``) format, respectively.
--------------------------------------------------------------------------------
===Parse Tree Visualization===
This command renders the parse tree that corresponds to a specific abstract syntax tree. The generated image is in PNG format.
====Input====
|| Parameter | Description | Default |
| ``command`` | should be ``parsetree`` | - |
| ``tree`` | the abstract syntax tree to render | - |
| ``from`` | the name of the concrete syntax to use in the rendering | - |
| ``format`` | output format (gf>3.3.3) | ``png`` |
| //options// | additional rendering options (gf>3.4) | - |
The additioal rendering options are: ``noleaves``, ``nofun`` and ``nocat`` (booleans, false by default);
``nodefont``, ``leaffont``, ``nodecolor``, ``leafcolor``, ``nodeedgestyle`` and ``leafedgestyle``
(strings, have builtin defaults).
====Output====
By default, the output is an image in PNG format. The Content-Type is set to ``image/png``, so the easiest way to visualize the generated image is to add HTML element ``<img/>`` which points to URL for the visualization command i.e.:
```
<img src="http://localhost/my_grammar.pgf?command=parsetree&tree=..."/>
```
The ``format`` parameter can also be ``gif``, ``svg`` or ``gv``, for
GIF (``image/gif``), SVG (``image/svg+xml``)
or graphviz (``text/plain``) format, respectively.
--------------------------------------------------------------------------------
===Word Alignment Diagrams===
This command renders the word alignment diagram for some sentence and all languages in the grammar. The sentence is generated from a given abstract syntax tree.
====Input====
|| Parameter | Description | Default |
| ``command`` | should be ``alignment`` | - |
| ``tree`` | the abstract syntax tree to render | - |
| ``format`` | output format (gf>3.3.3) | ``png`` |
| ``to`` | list of languages to include in the diagram (gf>3.4) | all languages supported by the grammar |
====Output====
By default, the output is an image in PNG format. The Content-Ttype is set to
``image/png``, so the easiest way to visualize the generated image is to add HTML element ``<img/>`` which points to URL for the visualization command i.e.:
```
<img src="http://localhost/my_grammar.pgf?command=alignment&tree=..."/>
```
The ``format`` parameter can also be ``gif``, ``svg`` or ``gv``, for
GIF (``image/gif``), SVG (``image/svg+xml``)
or graphviz (``text/plain``) format, respectively.
--------------------------------------------------------------------------------
===Word Dependency Diagrams===
This command (available in GF>=3.8) outputs word dependency diagrams
in various format.
====Input====
|| Parameter | Description | Default |
| ``command`` | should be ``deptree`` | - |
| ``tree`` | the abstract syntax tree to render | - |
| ``format`` | output format, see below | ``dot`` |
| ``to`` | name of the concrete syntax to use in the diagram | - |
The ``format`` is one of the following:
- ``png``, ``gif``, ``gv``: rendered with graphviz,
- ``svg``, ``latex``: [universal dependency http://universaldependencies.org/]
diagrams, in SVG format for use in web pages or
as LaTeX Picture code for use in LaTeX documents,
- ``conll``, ``malt_tab`` and ``malt_input``: text formats
--------------------------------------------------------------------------------
===Undocumented commands===
There a few additional commands that lack proper documentation:
- ``abstrjson``, ``browse``, ``download``, ``generate``, ``linearizeAll``,
``linearizeTable``, ``lookupmorpho``, ``translategroup``.
See the source code for details.
--------------------------------------------------------------------------------
==Commands that use the C run-time system==
GF includes two implementations of the PGF API: the traditional
Haskell implementation and the newer C implementation. The commands documented
above all use the Haskell implementation.
The following commands use the C implementation instead:
- ``c-parse``, ``c-linearize``, ``c-linearizeAll``, ``c-translate``,
``c-lookupmorpho``, ``c-flush``, ``c-grammar``, ``c-abstrtree``,
``c-parsetree``, ``c-wordforword``.
They implement the same functionality as the corresponding commands
without the ``c-`` prefix, although there are some restrictions in what
parameters they support, and some differences in the JSON
data structures they output.
When using these commands, the grammar will be loaded and cached
by the C run-time system. If you use commands from both the Haskell and C
implementations with the same grammar, the grammar will be loaded twice.
--------------------------------------------------------------------------------
[www.grammaticalframework.org http://www.grammaticalframework.org]

View File

@@ -0,0 +1,112 @@
import Network.HTTP.Base
import Codec.Binary.UTF8.String
import Data.Char
import Data.List
import System
main = do
xs <- getArgs
let xxoo = lexArgs (unwords xs)
case pArgs xxoo of
Just (oo,xx) -> do
morpho oo xx
_ -> do
putStrLn $ "cannot read " ++ unwords xs ++ "."
putStrLn "<p>"
putStrLn usage
usage = "usage: gfmorpho LANG POS FORMS OPT*"
noParse xx = length xx < 3 ----
lexArgs = map (decodeString . urlDecode) . words . map unspec . drop 1 . dropWhile (/='=') where
unspec c = case c of
'=' -> ' '
'+' -> ' '
_ -> c
pArgs xxoo = do
let (oo,xx) = partition isOption xxoo
if length xx < 3 then Nothing else return (oo,xx)
morpho :: [String] -> [String] -> IO ()
morpho oo xx = do
writeFile tmpCommand (script xx)
system $ command xx
s <- readFile tmpFile
putStrLn $ mkFile $ response oo s
script ("!":lang:rest) = "cc -table -unqual " ++ unwords rest
script (lang: pos: forms) = "cc -table -unqual " ++ fun pos ++ quotes forms
where
fun pos = "mk" ++ pos
command ("!":args) = command args
command (lang: pos: forms) =
"/usr/local/bin/gf -run -retain -path=alltenses alltenses/Paradigms" ++ lang ++ ".gfo"
++ " < " ++ tmpCommand
++ " > " ++ tmpFile
quotes = unwords . map quote where
quote s = case s of
'_':tag -> tag
_ -> "\"" ++ s ++ "\""
-- html response
response oo =
tag "table border=1" . unlines . map (tag "tr" . unwords) . map cleanTable . grep oo . map words . lines
cleanTable ws = [tag "td" (unwords param), tag "td" (tag "i" (unwords form))] where
(param,form) = getOne (map cleant ws)
cleant w = case w of
"s" -> ""
"." -> ""
_ -> cleanw w
cleanw = filter (flip notElem "()")
getOne ws = let ww = filter (/= "=>") ws in (init ww, [last ww]) -- excludes multiwords
responsePlain oo =
unlines . map unwords . grep oo . map cleanTablePlain . map words . lines
cleanTablePlain = map clean where
clean w = case w of
"=>" -> "\t"
"s" -> ""
"." -> ""
_ -> cleanw w
cleanw = filter (flip notElem "()")
grep oo wss = filter (\ws -> all (flip matchIn ws) oo) wss
matchIn p ws = quant (matchPol pol patt) ws where
quant = if pol then any else all
(pol,patt) = (head p == '-', tail p)
matchPol True p w = match p w
matchPol False p w = not (match p w)
match p w = case (p,w) of
('*':ps,_ ) -> any (match ps) [drop i w | i <- [0..length w]] ---
(c:ps, d:ws) -> c == d && match ps ws
_ -> p == w
tmpFile = "_gfmorpho.tmp"
tmpCommand = "_gfcommand.tmp"
isOption = (flip elem "-~") . head
tag t s = "<" ++ t ++ ">" ++ s ++ "</" ++ t ++ ">"
-- html file with UTF8
mkFile s = unlines $ [
"<HTML>",
"<HEAD>",
"<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=utf-8\">",
"<TITLE>GF Smart Paradigm Output</TITLE>",
"</HEAD>",
"<BODY>",
s,
"</BODY>",
"</HTML>"
]

View File

@@ -0,0 +1,23 @@
A service for using smart paradigms on the web.
Works with a cgi script running a Haskell program that calls GF to interprete a query string as a "cc" command on a specified Paradigms file. For instance, if the
user submits the query
Eng N baby
the program executes the command
cc -table -unqual ParadigmsEng.mkN "baby"
The resulting output is converted into an HTML table.
The file gfmorpho.html gives some more information. Open issues in addition to those mentioned there are:
- GFMorpho.hs creates the temporary files _gfcommand.tmp and _gfmorpho.tmp which need to be world-writable; they should be created more properly and removed after use
- gfmorpho.cgi defines the variable GF_LIB_PATH to reside in /home/aarne, and must be edited for other environments
- to work for all languages mentioned, one has to compile some incomplete GF grammars not standardly compiled:
GF/lib/src$ runghc Make alltenses lang langs=Amh,Ara,Lat,Mlt,Tur
(c) Aarne Ranta 2012 under LGPL/BSD.

View File

@@ -0,0 +1,7 @@
#!/bin/bash
echo "Content-type: text/html";
echo ""
export LANG=en_US.UTF-8
export GF_LIB_PATH=/home/aarne/GF/lib/
/usr/local/bin/runghc GFMorpho "$QUERY_STRING"

View File

@@ -0,0 +1,112 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html> <head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<title>Use GF Smart Paradigms</title>
</head>
<body>
<h1>Word inflection with smart paradigms</h1>
Give language, part of speech, and one or more word forms, to obtain
the inflection table.
<p>
<form method=get action="cgi-bin/gfmorpho.cgi">
<input name=args>
<INPUT TYPE=SUBMIT VALUE="Submit">
</form>
Examples:
<pre>
Eng N baby
Fin V odottaa odotti
Fre V manger
Ger N Soldat Soldaten _masculine
Hin N बच्छा
Jpn V 答える _Gr2
Lat A vetus veteris
</pre>
Thus notice that strings are given without quotes, but features
are prefixed with an underscore <tt>_</tt>.
<h2>Languages and part of speech tags</h2>
The available languages are:
<pre>
Afr Amh Cat Dan Dut Eng Fin Fre Ger Hin Ina Ita Jpn Lat
Lav Nep Nor Pes Pnb Ron Rus Snd Spa Swe Tha Tur Urd
</pre>
In addition, the library has the languages <tt>Ara Bul Pol</tt>, but they
are not yet available in this way; you can however use the full form of
paradigm applications prefixed by "!" as described below.
<p>
The parts of speech are: N (= noun), A (= adjective), V (= verb).
<p>
The way this works is that the program constructs the most probable
inflection table from the forms given. For a vast majority of words in
all languages, it is enough to give just one form. But sometimes more
forms are needed to get the inflection table right.
<p>
This is a front end to the Paradigms modules in the GF Resource Grammar.
See <a href=http://grammaticalframework.org/lib/doc/synopsis.html>RGL
Synopsis</a> for more information on available languages and paradigms.
<h2>Filtering with patterns</h2>
You may not want to see the whole table.
Then you can filter it with patterns, each of which works like
"grep", using <tt>*</tt> to match any substring, either in the
features or in the forms:
<pre>
Eng N baby -Gen
Eng V die -dy*
</pre>
If several pattern are given, they are applied in conjunction.
Patterns prefixed with a tilde <tt>~</tt> rather than a hyphen
<tt>-</tt> are checked for <i>not</i> matching. Thus the search
<pre>
Eng N baby -Gen ~Pl
</pre>
selects the lines that contain <tt>Gen</tt> but not <tt>Pl</tt>.
<h2>Using custom paradigms</h2>
(For GF experts.) If you want to use other paradigms than the smart
<tt>mk</tt> paradigms, you can prefix your input with <tt>!</tt> and
use the normal expression syntax of GF. For example:
<pre>
! Ara brkN "طير" "فَعل" "فُعُول" Masc NoHum
! Bul mkN041 "птица"
! Pol mkRegAdj "duży" "większy" "dużo" "więcej"
</pre>
This also allows you to use structured terms:
<pre>
! Ger prefixV "auf" (mkV "fassen")
</pre>
<h2>To do</h2>
<ul>
<li> nicer input helped by menus
<li> error handling and reporting when some language doesn't follow
the format assumed here
<li> better documentation of the paradigms
</ul>
<p>
Powered by <a href=http://grammaticalframework.org>GF</a>. Aarne Ranta 2012.
<hr>
<address></address>
<!-- hhmts start --> Last modified: Wed Sep 12 14:24:51 CEST 2012 <!-- hhmts end -->
</body> </html>

View File

@@ -0,0 +1,107 @@
<!DOCTYPE html>
<html> <head>
<title>Smart paradigms</title>
<meta charset="UTF-8">
<meta name = "viewport" content = "width = device-width">
<link rel=stylesheet href="../cloud.css">
<style>
.morpho_output {
background: white;
padding: 1ex;
margin: 1ex;
box-shadow: 5px 5px 5px rgba(0,0,0,0.3);
}
th,td { vertical-align: baseline; }
</style>
</head>
<body>
<header>
<h1><a href="/"><img class=nofloat src="../P/gf-cloud.png" alt=""></a> Word inflection with smart paradigms</h1>
</header>
<main>
Give language, part of speech, and one or more word forms, to obtain
the inflection table.
<p>
<table>
<tr><th>Examples:
<td>
<input onclick="submit_example(this)" type=button value="Eng N baby">
<input onclick="submit_example(this)" type=button value="Fin V odottaa odotti">
<input onclick="submit_example(this)" type=button value="Fre V manger">
<input onclick="submit_example(this)" type=button value="Ger N Soldat Soldaten _masculine">
<input onclick="submit_example(this)" type=button value="Hin N बच्छा">
<input onclick="submit_example(this)" type=button value="Jpn V 答える _Gr2">
<input onclick="submit_example(this)" type=button value="Lat A vetus veteris">
</table>
<p>
Thus notice that word forms are given without quotes. In addition
to word forms, in some languages it might be necessary to give
an inherent feature (e.g. gender) and these
are prefixed with an underscore <tt>_</tt>.
<p>
<form name=morpho method=get action="gfmorpho.cgi" onsubmit="submitmorpho();return false" onreset="resetmorpho()">
Create your own example:
<!--
<select name=language><option value=Eng>English</option></select>
<select name=category>
<option value=N>Noun</option>
<option value=A>Adjective</option>
<option value=V>Verb</option>
</select>
-->
<input size=40 name=args placeholder="Lang Cat word_forms">
<input type=submit value="Submit">
<input type=reset value="Clear">
</form>
<pre id=morpho_output></pre>
<h2>Language and part of speech</h2>
The available languages are:
<blockquote><tt>
Afr Amh Ara Bul Cat Chi Dan Dut Eng Est Eus Fin Fre Ger
Grc Gre Heb Hin Ice Ina Ita Jpn Lat Lav Mlt Mon Nep Nno
Nor Pes Pnb Pol Por Ron Rus Slv Snd Spa Swe Tha Tur Urd
</tt></blockquote>
<!--
In addition, the library has the languages <tt>Ara Bul Pol</tt>, but they
are not yet available in this way; you can however use the full form of
paradigm applications prefixed by "!" as described below.
-->
<p>
The parts of speech are: N (= noun), A (= adjective), V (= verb).
<p>
The way this works is that the program constructs the most probable
inflection table from the forms given. For a vast majority of words in
all languages, it is enough to give just one form. But sometimes more
forms are needed to get the inflection table right.
<p>
This is a front-end to the Paradigms modules in the GF Resource Grammar.
See <a href=http://www.grammaticalframework.org/lib/doc/synopsis.html>RGL
Synopsis</a> for more information on available languages and paradigms.
</main>
<footer>
<hr>
<div class=modtime><small>
<!-- hhmts start -->Last modified: Tue Dec 3 17:06:25 CET 2019 <!-- hhmts end -->
</small></div>
<address>Thomas H</address>
Based on <a href="gfmorpho.html">GFMorpho</a> by Aarne Ranta 2012.
</body>
<script src="../js/support.js"></script>
<script src="../js/localstorage.js"></script>
<script src="../gfse/cloud2.js"></script>
<script src="morpho.js"></script>
</html>

View File

@@ -0,0 +1,42 @@
var local=appLocalStorage("gf.morpho.")
function quote(s) {
return s[0]=="_" ? s.substr(1) : '"'+s+'"'
}
function show_output(output) {
morpho_output.className=output ? "morpho_output" : ""
replaceChildren(morpho_output,text(output))
}
function submitmorpho() {
clear(morpho_output)
var args=morpho.args.value.split(/ +/)
var lang=args[0]
var cat=args[1]
var wordforms=args.slice(2).map(quote).join(" ")
//console.log("submitmorpho",lang,cat,wordforms)
switch("") {
case lang: show_output("No language"); break;
case cat: show_output("No category"); break;
case wordforms: show_output("No word forms"); break;
default:
gfshell("e",function() {
gfshell("i -retain alltenses/Paradigms"+lang+".gfo",function() {
gfshell("cc -table -unqual mk"+cat+wordforms,show_output)
})
})
}
return false;
}
function resetmorpho() {
show_output("")
}
function submit_example(b) {
//console.log("submit_example",b.value)
morpho.args.value=b.value
submitmorpho()
}

View File

@@ -0,0 +1,7 @@
save: save.hs
ghc --make save.hs
install::
@make save
rsync -avz --exclude .DS_Store P *.html *.css *.js *.cgi *.manifest save www.grammaticalframework.org:/usr/local/www/GF/demos/gfse

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,44 @@
+ Safety question before deleting a grammar
+ Check identifier syntax
+ Allow lincat for deleted cat to be deleted
+ Allow lin for deleted fun to be deleted
+ Apply category alpha conversion in concrete syntax
+ Remove a concrete syntax
+ Apply function alpha conversion in concrete syntax
+ Change lhs of lins when function type is changed
+ Allow languages other than the ones in the given list to be added
+ Export as plain text
+ Allow definitions to be reordered
+ 1. possibility to compile the grammar set, returning a URL to a translator app
+ 2a. possibility to import modules - resource libraries
- 2b. possibility to import modules - user's own auxiliaries
- 3. possibility to upload own modules
+ 4. access to the created files in an on-line shell (making testing possible)
- 5. rule-to-rule type checking and guidance (e.g. with library oper
suggestions)
+ Try grammars in the Translation Quiz
+ Show lincat and lin before params and opers below
+ Create a new concrete syntax by copying an existing one.
- Easy access to compute_concrete from the editor
- Instead of starting with an empty grammar, start a new grammar by copying
an example.
+ Cloning grammars
- Allow grammars to contain a resoure module. Create the resource module by
factoring out common parts of the concrete syntaxes.
- Integrate example-based concrete syntax construction (using Ramona's tool)
- Open lexicon modules? They are not installed by default!
DictEng, MorphalouFre, DictSwe, DictTur, DictBul, DictFun, DictLav
+ compile only the uploaded grammar even if other grammars are present
+ 'flags startcat' is needed for grammars with only one category (since the
default startcat is S, even if it doesn't exist)
- Bug! After adding a 2nd def of a fun with a different type and then deleting
the old fun, the corresponding lin will have the wrong lhs.
+ Bug! The startcat menu shows the first category by default, but the startcat
flag is actually not set until a selection is made from the menu.
+ Always open a resizable text box when editing concrete syntax.

View File

@@ -0,0 +1,309 @@
<!DOCTYPE HTML>
<html>
<head>
<title>About: GF online editor for simple multilingual grammars</title>
<link rel="stylesheet" type="text/css" href="editor.css" title="Cloud">
<link rel="alternate stylesheet" type="text/css" href="molto.css" title="MOLTO">
<link rel=author href="http://www.cse.chalmers.se/~hallgren/" title="Thomas Hallgren">
<meta name = "viewport" content = "width = device-width">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta charset="UTF-8">
<script type="text/javascript" src="slideshow.js"></script>
</head>
<body>
<h1><a href="../"><img src="../P/gf-cloud.png" alt="" title="GF Cloud Service"></a>GF online editor for simple multilingual grammars</h1>
<div class=right>
<div class=slideshow>
<img onload="start_slideshow(this,{delay:5,fade:0.3})" src="P/w1s.jpg" alt="[GF online editor screen shoot]">
<img class=hidden src="P/w2s.jpg" alt="[GF online editor screen shoot]">
<img class=hidden src="P/w3s.jpg" alt="[GF online editor screen shoot]">
<img class=hidden src="P/w4s.jpg" alt="[GF online editor screen shoot]">
</div>
</div>
<h2>Introduction</h2>
Traditionally, <a href="http://www.grammaticalframework.org/">GF</a>
grammars are created in a text editor and tested in the
GF shell. Text editors know very little (if anything) about the syntax of
GF grammars, and thus provide little guidance for novice GF users. Also, the
grammar author has to download and install the GF software on his/her own
computer.
<p>
In contrast, the
<a href="."><em>GF online editor for simple multilingual grammars</em></a>
is available online, making it easier to get started. All that
is needed is a reasonably modern web browser. Even Android and iOS devices
can be used.
<p>
The editor
also guides the grammar author by showing a skeleton grammar file and
hinting how the parts should be filled in. When a new part is added to the
grammar, it is immediately checked for errors.
<p>
Editing operations are accessed by clicking on editing symbols embedded
in the grammar display:
<span class=more>+</span>=Add an item,
<span class=delete>×</span>=Delete an item,
<span class=edit>%</span>=Edit an item.
These are revealed when hovering over items. On touch devices, hovering is
in some cases simulated by tapping, but there is also a button at the bottom
of the display to "Enable editing on touch devices" that reveals all editing
symbols.
<p>
In spite of its name, the core of the editor runs in the web
browser, so once you have opened the web page, you can
<strong>continue editing</strong> grammars even while you are
<strong>offline</strong>. Grammar compilation &amp; testing and some
error checking is done by the GF server and is not available while offline.
<h3>Current status</h3>
<p>
At the moment, the editor supports only a small subset of the GF grammar
notation.
Proper error checking is done for abstract syntax, but not (yet) for concrete
syntax.
<p>
The grammars created with this editor always consists of one file for the
abstract syntax, and one file for each concrete syntax.
<h4>Abstract syntax</h4>
The supported abstract syntax corresponds to context-free grammars
(no dependent types). The definition of an abstract syntax is limited to
<ul>
<li>a list of inherited abstract syntaxes,
<li>a list of <em>category names</em>,
<var>Cat<sub>1</sub></var> ; ... ; <var>Cat<sub>n</sub></var>,
<li>a list of <em>functions</em> of the form
<var>Fun</var> : <var>Cat<sub>1</sub></var> -> ... ->
<var>Cat<sub>n</sub></var>,
<li>and a <em>start category</em>.
</ul>
Available editing operations:
<ul>
<li>Inherited abstract syntaxes can be added and removed.
<li>Categories can be added, removed and renamed. When renaming a category,
occurences of it in function types will be updated accordingly.
<li>Functions can be added, removed and edited. Concrete syntaxes are updated
to reflect changes.
<li>Functions can be reordered using drag-and-drop.
</ul>
Error checks:
<ul>
<li>Syntactically incorrect function definitions are refused.
<li>Semantic problem such as duplicated definitions or references to
undefined categories, are highlighted.
</ul>
<h4>Concrete syntax</h4>
At the moment, the concrete syntax for a language <var>L</var> is limited to
<ul>
<li>inheriting the concrete syntax <var>G<sub>i</sub>L</var>
for each <var>G<sub>i</sub></var> inherited by the abstract syntax.
<li>opening a selection of the Resource Grammar Library modules
<code>Syntax</code><var>L</var>, <code>Paradigms</code><var>L</var>,
<code>Lexicon</code><var>L</var>, <code>Symbolic</code><var>L</var>
and <code>Extra</code><var>L</var>,
<li><em>linearization types</em> for the categories in the abstract syntax,
<li><em>linearizations</em> for the functions in the abstract syntax,
<li><em>parameter type definitions</em>,
<var>P</var> = <var>C<sub>1</sub></var> | ... |<var>C<sub>n</sub></var>,
<li>and <em>operation definitions</em>, <var>op</var> = <var>expr</var>,
<var>op</var> : <var>type</var> = <var>expr</var>,
</ul>
Available editing operations:
<ul>
<li>Modules can be added and removed from the list of opened Resource Grammar
Library modules.
<li>The LHSs of the linearization types and linearizations are determined by
the abstract syntax and do not need to be entered manually.
The RHSs can
be edited.
<li>Parameter types can be added, removed and edited.
<li>Operation definitons can be added, removed and edited.
<li>Definitions can be reordered (using drag-and-drop).
</ul>
Also,
<ul>
<li>When a new concrete syntax is added to the grammar, a copy of the
currently open concrete syntax is created, since copying and modifying
is usually easier than creating something new from scratch.
(If the abstract syntax is currently open, the new conrete syntax will
start out empty.)
<li>When adding a new concrete syntax, you normally pick one of the supported
languages from a list. The language code and the file name is determined
automatically. But you can also pick <em>Other</em> from the list and change
the language code afterwards to add a concrete syntax for a language
that is not in the list.
</ul>
Error checks:
<ul>
<li>The RHSs in the concrete syntax are checked
for syntactic correctness by the editor as they are entered.
(TODO: the syntax of parameter types is not check at the moment.)
<li>Duplicated definitions are highlighted. Checks for other
semantic errors are delayed until the grammar is compiled.
</ul>
<h3>Grammar views</h3>
The editor supports three ways to view a grammar:
<ul>
<li><strong>Column view</strong>:
this is the traditional view where you see one complete grammar module at a
time. i.e. either the abstract syntax or one of the concrete syntaxes in the
grammar.
<li><strong>Matrix view</strong>:
this presents an overview of the grammar where columns
correspond to the abstract and concrete syntax modules and rows correspond
to the categories and functions in the grammar.
<li><strong>Row view</strong>:
this view shows one function form the abstract syntax and
the corresponding linearization functions from all the concrete syntaxes.
</ul>
<h3>Compiling and testing grammars</h3>
When pressing the <strong>Compile</strong> button, the grammar will be compiled
with GF, and any errors not detected by the editor will be reported.
The grammar will also be compiled when pressing the <strong>Minibar</strong>
and <strong>Quiz</strong> buttons before opening the grammar for testing
in the Minibar or the Translation Quiz, respectively.
<h3><img class=right src="P/1307545089_weather_04.png" alt="">
<img class=right src="P/1306856253_weather_06.png" alt="">Grammars in the
cloud</h3>
While the editor normally stores grammars locally in the browser, it is also
possible to store grammars in the cloud. Grammars can be stored in the cloud
just for backup, or to make them accessible from multiple devices.
<p>
There is no automatic synchronization between local grammars and the cloud.
Instead, the user should press
<img src="P/1306856253_weather_06.png" alt="[Cloud Upload]">
to upload the grammars to the cloud, and press
<img src="P/1307545089_weather_04.png" alt="[Cloud download]">
to download grammars from the cloud. In both cases, complete grammars
are copied and older versions at the destination will be overwritten.
When a grammar is deleted, both the local copy and the copy in the cloud
is deleted.
<p>
Each device is initially assigned to its own unique cloud. Each device can thus
have its own set of grammars that are not available on other devices. It is
also possible to merge clouds and share a common set of grammars between
multiple devices: when uploading grammars to the cloud, a link to this grammar
cloud appears. Accessing this link from another device will cause the clouds of
the two devices to be merged. After this, grammars uploaded from one of the
devices can be downloaded on the other devices. Any number devices can join the
same grammar cloud in this way.
<p>
<strong>Note</strong> that while it is possible to copy grammars between
multiple devices, there is no way to merge concurrent edits from multiple
devices. If the same grammar is uploaded to the
cloud from multiple devices, the last upload wins. Thus the current
implementation is suitable for a single user switching between different
devices, but not recommended for sharing grammars between multiple users.
<p>
Also <strong>note</strong> that each grammar is assigned a unique identity
when it is first created. Renaming a grammar does not change its identity.
This means that name changes are propagated between devices like other changes.
<h4 id=public>Public grammars</h4>
<blockquote>
[October 2012: this is an <strong>experimental feature</strong> that
might be replaced by an incompatible grammar sharing mechanism in the future.]
</blockquote>
<p>
The grammar cloud also includes a list of public grammars.
Grammars can be added to the public list by pressing the
<strong>Publish</strong> button shown next to the grammars in the list of
your grammars.
<p>
The <strong>Publish</strong> button creates <em>copy</em> of your grammar.
If you continue to edit your grammar, the changes will be local only.
You can press the <strong>Publish</strong> button again to update the public
copy.
<p>
You can remove a grammar from the public list by pressing the
<span class=delete>×</span> button next to the grammar in the public list.
You can <em>not</em> remove grammars published by other users.
<p>
When you open a public grammar published by another user, a <em>copy</em> of
the grammar is added to the list of your grammars. Any changes will be made in
your own copy of the grammar. If you publish your copy of the grammar, it will
appear separately in the list of public grammars. You can not
overwrite grammars published by other users, even if they have the same name.
<p>
TODO: Publishing grammars that inherit form other grammars is not recommended.
There is no way to indicate which of several grammars with the same name is
being inherited.
<p>
TODO: There should probably be a way to identify who published a grammar and
when. Maybe the publish button should be restricted to registered users...
<h3>Example-based grammar writing</h3>
We experimented with this in 2011. It is currently not included, but it
might return in a future version...
<h3>Future work</h3>
This prototype gives an idea of how a web based GF grammar editor could work.
While this editor is implemented in JavaScript and runs in the web browser,
we do not expect to create a full implementation of GF that runs in the
web browser, but let the editor communicate with a server running GF.
<p>
By developing a GF server with an appropriate API, it should
be possible to extend the editor to support a larger fragment of GF,
to do proper error checking and make more of the existing GF shell functionality
accessible directly from the editor.
<p>
The current grammar cloud service is very primitive. In particular, it is not
suitable for multiple users developing a grammar in collaboration.
<h3>Related documents</h3>
<ul>
<li><a href="http://www.grammaticalframework.org/~hallgren/Talks/GF/gf-ide.html">GF Grammar Development Tools</a>. Slides from a presentation at the MOLTO meeting in Göteborg, March 2011.
<li><a href="http://www.grammaticalframework.org/grammar-ide/">The GF Grammar IDE</a>. MOLTO deliverable.
<li><a href="http://www.grammaticalframework.org/compiler-api">The GF Grammar
Compiler API</a>. MOLTO deliverable.
</ul>
<hr>
<div class=modtime><small>
<!-- hhmts start -->Last modified: Tue Oct 9 16:52:47 CEST 2012 <!-- hhmts end -->
</small></div>
<address>
<a href="http://www.cse.chalmers.se/~hallgren/">TH</a>
<img src="http://www.altocumulus.org/~hallgren/online.cgi?icon" alt="">
</address>
</body> </html>

View File

@@ -0,0 +1,148 @@
function with_dir(cont) {
var dir=local.get("dir","");
if(dir) cont(dir);
else ajax_http_get("upload.cgi?dir",
function(dir) {
local.put("dir",dir);
cont(dir);
});
}
function remove_cloud_grammar(g) {
var dir=local.get("dir")
if(dir && g.unique_name) {
var path=dir+"/"+g.unique_name+".json"
ajax_http_get("upload.cgi?rm="+encodeURIComponent(path),debug);
}
}
// Upload the grammar to the server and check it for errors
function upload(g) {
function upload2(dir) {
var form=node("form",{method:"post",action:"upload.cgi"+dir},
[hidden(g.basename+".gf",show_abstract(g))])
for(var i in g.concretes)
form.appendChild(hidden(g.basename+g.concretes[i].langcode+".gf",
show_concrete(g)(g.concretes[i])));
editor.appendChild(form);
form.submit();
form.parentNode.removeChild(form);
}
with_dir(upload2);
}
// Upload the grammar to store it in the cloud
function upload_json(cont) {
function upload3(resptext,status) {
local.put("json_uploaded",Date.now());
//debug("Upload complete")
if(cont) cont();
else {
var sharing=element("sharing");
if(sharing) sharing.innerHTML=resptext;
}
}
function upload2(dir) {
var prefix=dir.substr(10)+"-" // skip "/tmp/gfse."
//debug("New form data");
//var form=new FormData(); // !!! Doesn't work on Android 2.2!
var form="",sep="";
//debug("Preparing form data");
for(var i=0;i<local.count;i++) {
var g=local.get(i,null);
if(g) {
if(!g.unique_name) {
g.unique_name=prefix+i;
save_grammar(g)
}
//form.append(g.unique_name+".json",JSON.stringify(g));
form+=sep+encodeURIComponent(g.unique_name+".json")+"="+
encodeURIComponent(JSON.stringify(g))
sep="&"
}
}
//debug("Upload to "+prefix);
ajax_http_post("upload.cgi"+dir,form,upload3,cont)
}
with_dir(upload2);
}
function download_json() {
var dir=local.get("dir");
var index=grammar_index();
var downloading=0;
function get_list(ok,err) {
ajax_http_get("upload.cgi?ls="+dir,ok,err);
}
function get_file(file,ok,err) {
downloading++;
ajax_http_get("upload.cgi?download="+encodeURIComponent(dir+"/"+file),ok,err);
}
function file_failed(errormsg,status) {
debug(errormsg)
downloading--;
}
function file_downloaded(grammar) {
downloading--;
var newg=JSON.parse(grammar);
debug("Downloaded "+newg.unique_name)
var i=index[newg.unique_name];
if(i!=undefined) merge_grammar(i,newg)
else {
debug("New")
newg.index=null;
save_grammar(newg);
}
if(downloading==0) done()
}
function done() {
setTimeout(function(){location.href="."},2000);
}
function download_files(ls) {
local.put("current",0);
if(ls) {
//debug("Downloading "+ls);
var files=ls.split(" ");
cleanup_deleted(files);
for(var i in files) get_file(files[i],file_downloaded,file_failed);
}
else {
debug("No grammars in the cloud")
done()
}
}
get_list(download_files);
}
function link_directories(newdir,cont) {
with_dir(function(olddir) {
ajax_http_get("upload.cgi?rmdir="+olddir+"&newdir="+newdir,cont)
})
}
/* -------------------------------------------------------------------------- */
// Send a command to the GF shell
function gfshell(cmd,cont) {
alert("gfshell(...) not implmemented!!!")
}
// Check the syntax of an expression
function check_exp(s,cont) {
function check(gf_message) {
//debug("cc "+s+" = "+gf_message);
cont(/(parse|syntax) error/.test(gf_message) ? "syntax error" : null);
}
if(navigator.onLine)
ajax_http_get("upload.cgi?cc="+encodeURIComponent(s),check)
else cont(null)
}

View File

@@ -0,0 +1,278 @@
function with_dir(cont) {
function have_dir(dir) {
var unique_id=local.get("unique_id")
if(!unique_id) {
unique_id=dir.substr(10) // skip "/tmp/gfse."
local.put("unique_id",unique_id)
}
cont(dir,unique_id)
}
var dir=local.get("dir","");
if(/^\/tmp\//.test(dir)) have_dir(dir);
else ajax_http_get("/new",
function(dir) {
local.put("dir",dir);
have_dir(dir);
});
}
function remove_cloud_grammar(g) {
var dir=local.get("dir")
if(dir && g.unique_name) {
var path=g.unique_name+".json"
gfcloud("rm",{file:path},debug);
}
}
// Upload the grammar to the server and check it for errors
function old_upload(g) {
function upload2(dir) {
var form=node("form",{method:"post",action:"/cloud"},
[hidden("dir",dir),hidden("command","make"),
hidden(g.basename+".gf",show_abstract(g))])
//var files = [g.basename+".gf"]
for(var i in g.concretes) {
var cname=g.basename+g.concretes[i].langcode+".gf";
//files.push(cname);
form.appendChild(hidden(cname,
show_concrete(g)(g.concretes[i])));
}
editor.appendChild(form);
form.submit();
form.parentNode.removeChild(form);
}
function upload3(message) { if(message) alert(message); }
with_dir(upload2)
}
// Upload the grammar to the server and check it for errors
function upload_grammars(gs,cont) {
function upload2(dir) {
var pre="dir="+encodeURIComponent(dir)
var form= {command:"make"}
for(var aix in gs) {
var g=gs[aix]
form[encodeURIComponent(g.basename+".gf")]=show_abstract(g)
var cnc=g.concretes
for(var i in cnc) {
var cname=g.basename+cnc[i].langcode+".gf";
form[encodeURIComponent(cname)]=show_concrete(g)(cnc[i]);
}
}
ajax_http_post("/cloud",pre+encodeArgs(form),upload3)
}
function upload3(json) {
var res=JSON.parse(json)
if(cont) cont(res)
else alert(res.errorcode+"\n"+res.command+"\n\n"+res.output);
}
if(navigator.onLine) with_dir(upload2)
else cont({errorcode:"Offline",command:"",output:""})
}
function assign_unique_name(g,unique_id) {
if(!g.unique_name) {
g.unique_name=unique_id+"-"+g.index;
save_grammar(g)
}
return g
}
// Upload all grammars to the cloud
function upload_json(cont) {
function upload2(dir,unique_id) {
function upload3(resptext,status) {
local.put("json_uploaded",Date.now());
//debug("Upload complete")
if(cont) cont();
else {
var sharing=element("sharing");
if(sharing) {
if(status==204) {
var a=empty("a");
a.href="share.html#"+dir.substr(5) // skip "/tmp/"
a.innerHTML=a.href;
sharing.innerHTML="";
sharing.appendChild(text("Use the following link for shared access to your grammars from multiple devices: "))
sharing.appendChild(a)
}
else
sharing.innerHTML=resptext;
}
}
}
//debug("New form data");
//var form=new FormData(); // !!! Doesn't work on Android 2.2!
var form={dir:dir};
//debug("Preparing form data");
for(var i=0;i<local.count;i++) {
var g=local.get(i,null);
if(g) {
g=assign_unique_name(g,unique_id)
//form.append(g.unique_name+".json",JSON.stringify(g));
form[encodeURIComponent(g.unique_name+".json")]=JSON.stringify(g)
}
}
ajax_http_post("/cloud","command=upload"+encodeArgs(form),upload3,cont)
}
with_dir(upload2);
}
function remove_public(name,cont,err) {
gfcloud_public_post("rm",{file:name},cont,err)
}
// Publish a single grammar
function publish_json(g,cont) {
function publish2(dir,unique_id) {
var oldname=g.publishedAs
function publish3(resptext,status) {
console.log("publish3")
if(oldname && oldname!=g.basename) {
console.log("old name="+oldname)
var name=oldname+"-"+g.unique_name+".json"
remove_public(name,cont,cont)
}
else cont()
}
g.publishedAs=g.basename;
save_grammar(g);
g=assign_unique_name(g,unique_id)
var name=g.basename+"-"+g.unique_name
var ix=g.index;
delete g.publishedAs
delete g.unique_name
delete g.index
var form={}
form[encodeURIComponent(name+".json")]=JSON.stringify(g)
g=reget_grammar(ix)
gfcloud_public_post("upload",form,publish3,cont)
}
with_dir(publish2);
}
function download_json() {
var dir=local.get("dir");
var downloading=0;
function get_list(ok,err) { gfcloud("ls",{},ok,err) }
function get_file(file,ok,err) {
downloading++;
gfcloud("download",{file:file},ok,err);
}
function file_failed(errormsg,status) {
debug(errormsg)
downloading--;
}
function file_downloaded(grammar) {
downloading--;
var newg=JSON.parse(grammar);
debug("Downloaded "+newg.unique_name)
var i=my_grammar(newg.unique_name);
if(i!=null) merge_grammar(i,newg)
else {
debug("New")
newg.index=null;
save_grammar(newg);
}
if(downloading==0) done()
}
function done() {
setTimeout(function(){location.href="."},2000);
}
function download_files(ls) {
local.put("current",0);
if(ls) {
//debug("Downloading "+ls);
var files=JSON.parse(ls);
cleanup_deleted(files);
for(var i in files) get_file(files[i],file_downloaded,file_failed);
}
else {
debug("No grammars in the cloud")
done()
}
}
get_list(download_files);
}
function link_directories(newdir,cont) {
gfcloud("link_directories",{newdir:newdir},cont)
}
/* -------------------------------------------------------------------------- */
var public_dir="/tmp/public"
// Request GF cloud service in the public directory (using GET)
function gfcloud_public_json(cmd,args,cont,err) {
var enc=encodeURIComponent;
var url="/cloud?dir="+public_dir+"&command="+enc(cmd)+encodeArgs(args)
http_get_json(url,cont,err)
}
// Request GF cloud service in the public directory (using POST)
function gfcloud_public_post(cmd,args,cont,err) {
var enc=encodeURIComponent;
var req="dir="+public_dir+"&command="+enc(cmd)+encodeArgs(args)
ajax_http_post("/cloud",req,cont,err)
}
// Request GF cloud service (using GET, for idempotent requests)
function gfcloud(cmd,args,cont,err) {
with_dir(function(dir) {
var enc=encodeURIComponent;
var url="/cloud?dir="+enc(dir)+"&command="+enc(cmd)+encodeArgs(args)
ajax_http_get(url,cont,err)
})
}
// Reqest GF cloud service (using POST, for state changing requests)
function gfcloud_post(cmd,args,cont,err) {
with_dir(function(dir) {
var enc=encodeURIComponent;
var req="dir="+enc(dir)+"&command="+enc(cmd)+encodeArgs(args)
ajax_http_post("/cloud",req,cont,err)
})
}
// Send a command to the GF shell
function gfshell(cmd,cont) {
with_dir(function(dir) {
var enc=encodeURIComponent;
ajax_http_get("/gfshell?dir="+enc(dir)+"&command="+enc(cmd),cont)
})
}
// Check the syntax of a source module
function check_module(path,source,cont) {
var enc=encodeURIComponent;
//http_get_json("/parse?"+enc(path)+"="+enc(source),cont)
ajax_http_post_json("/parse",enc(path)+"="+enc(source),cont)
}
// Check the syntax of an expression
function check_exp(s,cont) {
function check(gf_message) {
//debug("cc "+s+" = "+gf_message);
cont(/(parse|syntax) error/.test(gf_message) ? "syntax error" : null);
}
gfshell("cc "+s,check);
}
// Lexing/unlexing text
function lextext(txt,cont) { gfshell('ps -lextext "'+txt+'"',cont) }
function unlextext(txt,cont) { gfshell('ps -bind -unlextext "'+txt+'"',cont) }

View File

@@ -0,0 +1,152 @@
@import url(../cloud.css);
#editor {
/* This allows the div to grow wider than the window if necessary to
accommodate the contents. Otherwise, wide things inside can poke
through the border. */
display: table;
width: 100%;
}
div.home, div.grammar { border: 1px solid black; background: #9df; }
div.home { padding: 5px; }
div.files { margin: 0 8px 8px 8px; position: relative; }
td.public_grammars { padding-left: 2em; }
.no_publish .publish { display: none; }
div#file, table.extension td, table.extension th { border: 2px solid #009; }
div#file { border-top-width: 0; }
pre.plain { border: 2px solid #009; }
div#file, pre.plain, table.matrixview td { background: white; padding: 0.6ex; }
table.extension { border-collapse: collapse; background: white; }
table.extension td, table.extension th { padding: 1ex; }
table.extension th { border-right-width: 0; color: #009; }
table.extension td { border-left-width: 0; min-width: 30em; }
.slideshow .hidden { display: none; }
table.grammar_list { border-collapse: collapse; margin-left: 1.0em; }
table.grammar_list td { padding: 0.4ex 0.25em; }
div.namebar { background: #9df; }
div.namebar table { width: 100%; }
.namebar h3, .sheet h3 { margin: 0; color: #009; }
.home h3 { margin-top: 0; color: #009; }
div.comment:before { content: "-- "; }
div.comment { color: #009; margin-left: 1em; }
td.right { text-align: right; }
td.center { text-align: center; }
.kw { font-weight: bold; font-family: sans-serif; color: #009; }
.sep { font-weight: bold; color: #009; }
div.indent { padding-left: 1em; min-width: 1em; min-height: 1em; }
div.fun:hover, div.param:hover, div.lincat:hover, div.oper:hover, div.lin:hover,
div.template:hover
{ background: #def;}
.exb_linbuttons input[type=button] { float: right; clear: right; margin: 0; }
.exb_output { background: #dfd; float: right; margin: 0 10px; }
.more, .delete { font-weight: bold; font-family: sans-serif; }
.more, .delete, .edit, *[onclick], .onclick { cursor: pointer; }
.onclick:hover, .ident[onclick]:hover { text-decoration: underline; }
.hover .more, .hover .delete, .hover .edit { visibility: hidden }
.hover .hidden, .nohover .ifhover { display: none; }
.editable:hover, .deletable:hover { background: #ff9; }
.extensible:hover .more,.editable:hover > .edit ,.deletable:hover > .delete,
tr.deletable:hover .delete
{ visibility: visible; }
.editable { white-space: pre; }
div.lin span.editable { display: inline-block; vertical-align: top; }
.more { color: green; }
.edit { color: orange; }
.delete { color: red; }
.error_message,.inError { color: red; }
.template, .template .sep, .unimportant { color: #999; }
form { display: inline-block; }
table.tabs {
width: 100%;
border-width: 0; border-spacing: 0; empty-cells: show;
}
table.tabs td { text-align: center; border: 2px solid #009; padding: 2px; white-space: nowrap; }
table.tabs td.active { background: white; border-bottom-width: 0; }
table.tabs td.inactive {
background: #cef;
border-top-color: #66c; border-left-color: #66c; border-right-color: #66c;
}
table.matrixview th { background: #cef; color: #009; }
table.tabs td.gap
{ border-top-width: 0; border-left-width: 0; border-right-width: 0; }
table.tabs input[type=button], table.matrixview th input[type=button] {
border: 0;
background: inherit;
color: #009;
font-size: inherit;
font-weight: bold;
/*text-decoration: underline;*/
}
.string_edit { font-family: inherit; font-size: inherit; }
textarea.string_edit { vertical-align: top; }
ul.languages { -moz-column-width: 10em; }
#sharing h1, #sharing .footer { display: none; }
div.compiler_output .back_to_editor { display: none; }
textarea.text_mode {
/*font-family: inherit; font-size: inherit;*/
width: 99%;
}
div#minibar, div#syntax_editor {
border: 1px solid black;
padding: 5px;
background: #ccc url("../minibar/brushed-metal.png");
}
table.page_overlay {
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0,0,0,0.5);
}
table.page_overlay>tr>td { text-align: center; vertical-align: middle; }
div.grammar_extension {
display: inline-block;
border: 1px solid black;
background: #9df;
padding: 2ex;
margin: 2ex;
box-shadow: 10px 10px 10px rgba(0,0,0,0.3);
}
div.sheet {
position: relative;
background: white;
padding: 1ex;
margin: 2ex;
box-shadow: 5px 5px 15px rgba(0,0,0,0.5);
transition: all 0.5s ease-in-out;
-o-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
-webkit-transition: all 0.5s ease-in-out;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,190 @@
var example_based=[];
/*
-- cat lincat fun lin fun cat cat
environ :: ([(CId, CId)],[(CId, Expr)],[((CId, CId), [CId])]) -> Environ
*/
function exb_state(g,ci) {
var conc=g.concretes[ci]
function show_list(show1,xs) {
return "["+map(show1,xs).join(",")+"]";
}
function show_fun(fun) {
var t=fun.type
var res=t[t.length-1]
var args=t.slice(0,length-1);
return "(("+fun.name+","+res+"),["+args.join(",")+"])"
}
function show_lincat(lincat) {
return "("+lincat.cat+","+lincat.type+")"
}
function show_lin(lin) {
return "("+lin.fun+","+(lin.eb_lin||"?")+")"
}
function show_funs(funs) { return show_list(show_fun,funs) }
function show_lincats(lincats) { return show_list(show_lincat,lincats); }
function show_lins(lins) { return show_list(show_lin,lins) }
return "("+show_lincats(conc.lincats)
+","+show_lins(conc.lins)
+","+show_funs(g.abstract.funs)+")"
}
function exb_call(g,ci,command,args,cont) {
var url=window.exb_url || "exb/exb.fcgi";
var q=encodeArgs(args);
var cmd="?command="+command+"&state="+encodeURIComponent(exb_state(g,ci))+q;
http_get_json(url+cmd,cont)
}
function ask_possibilities(g,ci) {
var conc=g.concretes[ci];
function show_poss(poss) {
//debug("possibilities: "+JSON.stringify(poss))
var exready={}
for(var i in poss[0]) exready[poss[0][i]]=true;
var testable={}
for(var i in poss[1]) testable[poss[1][i]]=true;
example_based[ci]={exready:exready,testable:testable}
conc.example_based=true;
if(!conc.example_lang) conc.example_lang=g.concretes[0].langcode;
reload_grammar(g);
}
var exci=conc_index(g,conc.example_lang);
if(!conc.example_lang || !exci)
conc.example_lang=g.concretes[ci==0 ? 1 : 0].langcode;
var exci=conc_index(g,conc.example_lang);
exb_call(g,ci,"possibilities",{example_state:exb_state(g,exci)},show_poss)
}
var parser = { Eng: "ParseEngAbs.pgf",
Swe: "ParseSwe.pgf"
}
function exb_extra(g,ci) {
var conc=g.concretes[ci];
function stop_exb() {
conc.example_based=false;
reload_grammar(g);
}
function exblangmenu() {
function opt(conc) {
return option(concname(conc.langcode),conc.langcode);
}
function skip_target(c) { return c.langcode!=conc.langcode; }
var m =node("select",{},map(opt,filter(skip_target,g.concretes)));
if(conc.example_lang) m.value=conc.example_lang;
m.onchange=function() { conc.example_lang=m.value; save_grammar(g); }
return m
}
function ask_poss() { ask_possibilities(g,ci) }
if(navigator.onLine && g.concretes.length>1 && conc.example_based && !example_based[ci]) ask_poss();
var sb=button("Start",ask_poss)
if(parser[conc.langcode] && g.concretes.length>1)
return indent([wrap("small",text("Example-based editing: ")),
conc.example_based
? node("span",{},
[button("Stop",stop_exb),
wrap("small",text(" Example language: ")),
exblangmenu()
])
: sb])
else {
sb.disabled=true;
var why= g.concretes.length>1
? " ("+concname(conc.langcode)+" is not supported yet)"
: " (Add another language to take examples from first.)"
return indent([unimportant("Example-based editing: "), sb,
unimportant(why)])
}
}
function fun_lincat(g,conc,fun) {
var t=function_type(g,fun);
var abscat=t[t.length-1]
return cat_lincat(conc,abscat)
}
function exb_linbuttons(g,ci,f) {
var conc=g.concretes[ci];
var fun=f.fun;
var eb=example_based[ci];
var exb_output;
function fill_example(maybetree) {
var tree=maybetree.Just
if(tree) {
if(f.template)
conc.lins.push({fun:f.fun,args:f.args,
lin:tree[0],eb_lin:tree[1]});
else {
f.lin=tree[0];
f.eb_lin=tree[1];
}
ask_possibilities(g,ci)
}
else exb_output.innerHTML="Bug: no tree found"
}
function show_example(example){
exb_output.innerHTML="";
var s=prompt(example[1]);
if(s) {
var cat=fun_lincat(g,conc,fun)
exb_output.innerHTML="...";
//server.parse({from:"ParseEng",cat:cat,input:s},fill_example)
exb_call(g,ci,"abstract_example",
{cat:cat,input:s,
parser:parser[conc.langcode],
params:"["+f.args.join(",")+"]",
"abstract":example[0]},
fill_example)
}
}
function by_example() {
var dir=local.get("dir")
if(dir) {
if(exb_output) {
exb_output.innerHTML="...";
exb_call(g,ci,"provide_example",
{lang:g.basename+conc.example_lang,
parser:parser[conc.langcode],
fun:fun,
grammar:dir+"/"+g.basename+".pgf"},
show_example)
}
}
else exb_output.innerHTML="Compile the grammar first!"
}
function show_test(txt) {
exb_output.innerHTML="";
exb_output.appendChild(text(txt))
}
function test_it(b) {
if(exb_output) {
exb_output.innerHTML="...";
exb_call(g,ci,"test_function",
{fun:fun,parser:parser[conc.langcode]},
show_test)
}
}
var buttons=[];
if(conc.example_based && eb) {
if(f.template) {
var byex=button("By example",by_example);
if(!(eb.exready[fun] && fun_lincat(g,conc,fun)))
byex.disabled=true
buttons.push(byex)
}
else {
var b=button("Test it",test_it);
if(!eb.testable[fun] || !f.eb_lin) b.disabled=true;
buttons.push(b)
}
var exb_output=node("span",{"class":"exb_output"},[]);
buttons.push(exb_output)
}
return node("span",{"class":"exb_linbuttons"},buttons)
}

View File

@@ -0,0 +1,345 @@
/* Abstract syntax for a small subset of GF grammars in JavaScript */
/*
type UniqueId = String --persistent unique grammar identifier used by the editor
type Id = String -- all sorts of identifiers in grammars
type ModId = Id -- module name
type Cat = Id -- category name
type FunId = Id -- function name
type Type = [Cat] -- [Cat_1,...,Cat_n] means Cat_1 -> ... -> Cat_n
type Grammar = { basename: ModId,
comment: String,
extends: [ModId], -- in 1-to-1 correspondence with uextends
uextends: [UniqueId], -- added 2012-10-16
abstract: Abstract,
concretes: [Concrete] }
type Abstract = { startcat: Cat, cats: [Cat], funs: [Fun] }
type Fun = { name: FunId, type: Type }
type Concrete = { langcode: Id,
opens: [ModId],
params: [{name: Id, rhs: String}],
lincats : [{ cat: Cat, type: Term}],
opers: [{name: Lhs, rhs: Term}],
lins: [Lin]
}
type Lin = {fun: FunId, args: [Id], lin: Term}
type Lhs = String -- name and type of oper,
-- e.g "regN : Str -> { s:Str,g:Gender} ="
type Term = String -- arbitrary GF term (not parsed by the editor)
type LinType = [Term]
*/
// locally_defined_cats :: Grammar -> {Cat=>ModId} -> {Cat=>ModId} // destr upd
function locally_defined_cats(g,dc) {
with(g.abstract)
for(var i in cats) dc[cats[i]]=g.basename;
return dc;
}
// predefined_cats :: () -> {Cat=>ModId}
function predefined_cats() {
var pd = "Predef"
return { "Int":pd, "Float":pd, "String":pd}
}
// all_defined_cats :: Grammar -> [Grammar] -> {Cat=>ModId}
function all_defined_cats(g,igs) {
return all_inherited_cats(igs,locally_defined_cats(g,predefined_cats()))
}
// all_inherited_cats :: [Grammar] -> {Cat=>ModId} -> {Cat=>ModId} // destr upd
function all_inherited_cats(igs,dc) {
for(var i in igs) dc=locally_defined_cats(igs[i],dc)
return dc;
}
// locally_defined_funs :: [Grammar] -> {FunId=>ModId} -> {Id=>ModId} // destr upd
function locally_defined_funs(g,df) {
with(g.abstract)
for(var i in funs) df[funs[i].name]=g.basename;
return df;
}
// all_defined_funs :: Grammar -> [Grammar] -> {FunId=>ModId}
function all_defined_funs(g,igs) {
return all_inherited_funs(igs,locally_defined_funs(g,{}))
}
// all_inherited_funs :: [Grammar] -> {FunId=>ModId} -> {FunId=>ModId} // destr upd
function all_inherited_funs(igs,df) {
for(var i in igs) df=locally_defined_funs(igs[i],df)
return df;
}
// Return the index of the function the given name in the abstract syntax
// fun_index :: Grammar -> FunId -> (Int|null)
function fun_index(g,fun) {
with(g.abstract)
for(var i in funs) if(funs[i].name==fun) return i
return null;
}
// Return the type of a named function in the abstract syntax
// function_type :: Grammar -> FunId -> (Type|null)
function function_type(g,fun) {
var ix=fun_index(g,fun)
return ix==null ? null : g.abstract.funs[ix].type
}
// Return the lincat defined in a given concrete syntax for an abstract category
// cat_lincat :: Concrete -> Cat -> (Term|null)
function cat_lincat(conc,cat) {
with(conc)
for(var i in lincats) if(lincats[i].cat==cat) return lincats[i].type
return null;
}
// Return the index of the lin in a given concrete syntax for an abstract function
// lin_index :: Concrete -> FunId -> (Int|null)
function lin_index(conc,fun) {
with(conc) for(var i in lins) if(lins[i].fun==fun) return i
return null;
}
// Return the lin defined in a given concrete syntax for an abstract function
// fun_lin :: Concrete -> FunId -> (Lin|null)
function fun_lin(conc,fun) {
var i=lin_index(conc,fun)
if(i!=null) return conc.lins[i]
return null;
}
// Return the index of the concrete syntax with a given langcode
// conc_index :: Grammar -> Id -> (Int|null)
function conc_index(g,langcode) {
var c=g.concretes;
for(var ix=0;ix<c.length;ix++)
if(c[ix].langcode==langcode) return ix
return null;
}
// rename_category :: Grammar -> Cat -> Cat -> Grammar // destructive update
function rename_category(g,oldcat,newcat) {
function rename_cats(cats) {
for(var i in cats) if(cats[i]==oldcat) cats[i]=newcat;
}
function rename_type(t) {
for(var i in t) if(t[i]==oldcat) t[i]=newcat;
}
function rename_funs(funs) {
for(var i in funs) rename_type(funs[i].type)
}
function rename_abstract(a) {
rename_cats(a.cats);
rename_funs(a.funs);
}
function rename_lincat(lc) {
if(lc.cat==oldcat) lc.cat=newcat;
}
function rename_concrete(c) {
for(var i in c.lincats) rename_lincat(c.lincats[i]);
}
function rename_concretes(cs) {
for(var i in cs) rename_concrete(cs[i]);
}
rename_abstract(g.abstract)
rename_concretes(g.concretes);
return g;
}
// rename_function :: Grammar -> FunId -> FunId -> Grammar // destructive update
function rename_function(g,oldfun,newfun) {
function rename_concrete(c) {
var i=lin_index(c,oldfun)
if(i!=null) c.lins[i].fun=newfun;
}
for(var i in g.concretes) rename_concrete(g.concretes[i]);
return g;
}
// change_lin_lhs :: Grammar -> Fun -> Grammar // destructive update
function change_lin_lhs(g,fun) {
function change_concrete(c) {
var i=lin_index(c,fun.name)
if(i!=null) c.lins[i].args=arg_names(fun.type);
}
for(var i in g.concretes) change_concrete(g.concretes[i]);
return g;
}
/* --- Parsing -------------------------------------------------------------- */
// GF idenfifier syntax:
var lex_id=/^[A-Za-z][A-Za-z0-9_']*$/
// See https://developer.mozilla.org/en/JavaScript/Guide/Regular_Expressions
function check_id(s) { return lex_id.test(s); }
function check_name(s,kind) {
return check_id(s)
? null
: s+"? "+kind+" names must start with a letter and can contain letters, digits, _ and '"
}
// parse_fun :: String -> {error:String} + {ok:Fun}
function parse_fun(s) {
var ws=s.split(/\s+/);
var fun={name:"",type:[]};
/* Use a state machine to parse function definitions */
/* f : T1 -> ... -> Tn */
var state="name";
var ok=true;
for(var i=0;ok && i<ws.length;i++) {
if(ws[i]!="") {
switch(state) {
case "name": fun.name=ws[i]; state=":"; break;
case ":": ok=ws[i]==":"; state="type"; break;
case "type": fun.type.push(ws[i]); state="->"; break;
case "->": ok=ws[i]=="->"; state="type"; break;
}
}
}
var err=check_name(fun.name,"Function");
if(err) return {error: err};
return ok && state=="->"
? {ok:fun}
: { error : "Fun : Cat<sub>1</sub> -> ... -> Cat<sub>n</sub>" }
}
// parse_param :: String -> {error:String} + { ok:{name:Id,rhs:String} }
function parse_param(s) {
var ws=s.split("=");
if(ws.length==2) {
var name=ws[0].trim();
var err=check_name(name,"Parameter type");
return err ? { error:err } : { ok: { name:name,rhs:ws[1].trim() } }
}
else
return { error: "P = C1 | ... | Cn" }
}
// parse_oper :: String -> {error:String} + {ok:{name:Lhs, rhs:Term}}
function parse_oper(s) {
var i=s.indexOf(" ");
var operr = { error: "op = expr" }
if(i>0 && i<s.length-1) {
var name=s.substr(0,i).trim();
var rhs=s.substr(i).trim();
var err=check_name(name,"Operator");
return err
? {error:err}
: rhs!="" ? {ok: {name:name, rhs:rhs}}
: operr
}
else return operr
}
/* --- Print as plain text (normal GF source syntax) ------------------------ */
// show_type :: Type -> String
function show_type(t) {
var s="";
for(var i in t) {
if(i>0) s+=" -> ";
s+=t[i];
}
return s;
}
// show_lintype :: LinType -> String
function show_lintype(t) {
var s="";
for(var i in t) {
if(i>0) s+=" -> ";
s+= check_id(t[i]) ? t[i] : "("+t[i]+")";
}
return s;
}
// show_fun :: Fun -> String
function show_fun(fun) {
return fun.name+" : "+show_type(fun.type);
}
// show_grammar :: Grammar -> String
function show_grammar(g) {
return show_abstract(g)+"\n"+show_concretes(g)
}
function show_abstract(g) {
// var startcat= g.abstract.cats.length==1 ? g.abstract.cats[0] : g.abstract.startcat;
var startcat= g.abstract.startcat || g.abstract.cats[0];
return "abstract "+g.basename+" = "
+show_extends(g.extends)
+"{\n\n"
+"flags coding = utf8 ;\n\n"
+show_startcat(startcat)
+show_cats(g.abstract.cats)
+show_funs(g.abstract.funs)
+"}\n";
}
function show_extends(exts) {
return exts && exts.length>0 ? exts.join(", ")+" ** " : "";
}
function show_startcat(startcat) {
return startcat && startcat!="-"
? "flags startcat = "+startcat+";\n\n"
: "";
}
function show_cats(cats) {
return cats.length>0 ? "cat\n "+cats.join("; ")+";\n\n" : "";
}
function show_funs(funs) { return show_list("fun",show_fun,funs); }
function show_concretes(g) {
return map(show_concrete(g),g.concretes).join("\n\n");
}
function conc_extends(conc) { return function(m) { return m+conc.langcode; }}
function show_concrete(g) {
return function(conc) {
return "" // "--# -path=.:present\n"
+ "concrete "+g.basename+conc.langcode+" of "+g.basename+" = "
+show_extends((g.extends || []).map(conc_extends(conc)))
+show_opens(conc.opens)
+"{\n\nflags coding = utf8 ;\n\n"
+show_lincats(conc.lincats)
+show_lins(conc.lins)
+show_params(conc.params)
+show_opers(conc.opers)
+"}\n"
}
}
function show_list(kw,show1,list) {
return list.length>0
? kw+"\n "+map(show1,list).join(";\n ")+";\n\n"
: ""
}
function show_opens(opens) {
return opens && opens.length>0 ? "\n\nopen "+opens.join(", ")+" in " : ""
}
function show_params(params) { return show_list("param",show_param,params); }
function show_lincats(lincats) { return show_list("lincat",show_lincat,lincats); }
function show_opers(opers) { return show_list("oper",show_oper,opers); }
function show_lins(lins) { return show_list("lin",show_lin,lins); }
function show_param(p) { return p.name + " = " + p.rhs; }
function show_oper(p) { return p.name + " " + p.rhs; }
function show_lincat(p) { return p.cat + " = " + p.type; }
function show_lin(lin) {
return lin.fun + " " + lin.args.join(" ")+ " = " + lin.lin;
}

View File

@@ -0,0 +1,4 @@
CACHE MANIFEST
# 5
NETWORK:
*

View File

@@ -0,0 +1,82 @@
<!DOCTYPE html>
<html> <!-- manifest="gfse.manifest" -->
<head>
<title>GF online editor for simple multilingual grammars</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="editor.css" title="Cloud">
<link rel="alternate stylesheet" type="text/css" href="molto.css" title="MOLTO">
<link rel="stylesheet" type="text/css" href="../minibar/minibar.css">
<link rel="stylesheet" type="text/css" href="../syntax-editor/editor.css">
<link rel="stylesheet" type="text/css" href="../wordnet/gf-wordnet.css">
<link rel=author href="http://www.cse.chalmers.se/~hallgren/" title="Thomas Hallgren">
<meta name = "viewport" content = "width = device-width">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head>
<body class=hover>
<h2>GF online editor for simple multilingual grammars</h2>
<div id=editor></div>
<small class="hidden">
<span class=more>+</span>=Add an item,
<span class=delete>×</span>=Delete item,
<span class=edit>%</span>=Edit item.
</small>
<small class="ifhover">Hover over items for hints and editing options.</small>
<div id=compiler_output class=compiler_output></div>
<noscript>
This page does not work without JavaScript.
</noscript>
<hr>
<div class=modtime><small>
HTML
<!-- hhmts start -->Last modified: Thu May 21 09:47:39 CEST 2015 <!-- hhmts end -->
</small></div>
<a href="about.html">About</a>
<pre id=debug></pre>
<script type="text/javascript" src="config.js"></script> <!-- optional -->
<script type="text/javascript" src="../js/support.js"></script>
<script type="text/JavaScript" src="../js/pgf_online.js"></script>
<script type="text/javascript" src="../js/localstorage.js"></script>
<script type="text/JavaScript" src="../js/langcode.js"></script>
<script type="text/javascript" src="localstorage.js"></script>
<script type="text/javascript" src="gf_abs.js"></script>
<script type="text/javascript" src="example_based.js"></script>
<script type="text/javascript" src="sort.js"></script>
<script type="text/javascript" src="cloud2.js"></script>
<script type="text/javascript" src="editor.js"></script>
<script type="text/JavaScript" src="../minibar/minibar.js"></script>
<script type="text/JavaScript" src="../minibar/minibar_input.js"></script>
<script type="text/JavaScript" src="../minibar/minibar_translations.js"></script>
<script type="text/JavaScript" src="../minibar/minibar_support.js"></script>
<script type="text/javascript" src="../js/grammar_manager.js"></script>
<script type="text/javascript" src="../syntax-editor/ast.js"></script>
<script type="text/javascript" src="../syntax-editor/editor_menu.js"></script>
<script type="text/javascript" src="../syntax-editor/editor.js"></script>
<script type="text/javascript" src="../wordnet/js/gf-wordnet.js"></script>
<script src="https://unpkg.com/vis-network@9.0.4/standalone/umd/vis-network.min.js"></script>
<script type="text/javascript" src="../wordnet/js/wordcloud2.js"></script>
<div id="search_popup" class="search_popup">
<table id="domains" class="selector">
<thead></thead>
<tbody></tbody>
</table>
<table id="result" class="result">
<thead></thead>
<tbody></tbody>
<tfoot></tfoot>
</table>
</div>
</body>
</html>

View File

@@ -0,0 +1,4 @@
//Needs ../js/localstorage.js
var local=appLocalStorage("gf.editor.simple.grammar")

View File

@@ -0,0 +1,105 @@
body { color: #413b36; background: #fffcfa; }
h1 { font-size: 175%; }
h1,h2,h3,h4,small { font-family: sans-serif; }
h1,h2,h3,h4,a { color: #5c1a1a; }
h1:first-child, h2:first-child { margin-top: 0; margin-bottom: 1ex; }
#editor {
/* This allows the div to grow wider than the window if necessary to
accommodate the contents. Otherwise, wide things inside can poke
through the border. */
display: table;
width: 100%;
}
div.grammar { border: 2px solid #b09779; background: #642121; }
div.files { margin: 0 8px 8px 8px; }
div#file { border: 2px solid #b0977d; border-top-width: 0; }
pre.plain { border: 2px solid #b0977d; }
div#file, pre.plain { background: #fffcfa; padding: 0.6ex; }
.slideshow .hidden { display: none; }
img.cloud, img.right, div.right, div.modtime { float: right; }
.modtime { color: #999; white-space: nowrap; }
table.grammar_list { border-collapse: collapse; margin-left: 1.0em; }
table.grammar_list td { padding: 0.4ex 0.25em; }
/*div.namebar { background: #642121; }*/
div.namebar table { width: 100%; }
.namebar h3 { margin: 0; color: white; }
td.right { text-align: right; }
.kw { font-weight: bold; font-family: sans-serif; color: #642121; }
.sep { font-weight: bold; color: #642121; }
div.indent { padding-left: 1em; min-width: 1em; min-height: 1em; }
/*
div.fun, div.param, div.lincat, div.oper, div.lin
{ padding-left: 2em; text-indent: -2em; }
*/
.more, .delete { font-weight: bold; font-family: sans-serif; }
.more, .delete, .edit { cursor: pointer; }
.hover .more, .hover .delete, .hover .edit { visibility: hidden }
.hover .hidden, .nohover .ifhover { display: none; }
.editable:hover, .deletable:hover { background: #ff9; }
.namebar .editable:hover { background: #04b; }
.extensible:hover .more,.editable:hover > .edit ,.deletable:hover > .delete,
tr.deletable:hover .delete
{ visibility: visible; }
.more { color: green; }
.edit { color: orange; }
.delete { color: red; }
.error_message,.inError { color: red; }
.template, .template .sep, .unimportant { color: #999; }
form { display: inline-block; }
table.tabs {
width: 100%;
border-width: 0; border-spacing: 0; empty-cells: show;
}
table.tabs td { text-align: center; border: 2px solid #b09779; padding: 2px; white-space: nowrap; }
table.tabs td.active { background: white; border-bottom-width: 0; }
table.tabs td.inactive {
background: #e1e1e1;
border-top-color: #b09779; border-left-color: #b09779; border-right-color: #b09779;
}
table.tabs td.gap
{ border-top-width: 0; border-left-width: 0; border-right-width: 0; }
table.tabs input[type=button] {
border: 0;
background: inherit;
color: #642121;
font-size: inherit;
font-weight: bold;
/*text-decoration: underline;*/
}
input.string_edit { font-family: inherit; font-size: inherit; }
ul.languages { -moz-column-width: 20em; }
li { margin-top: 0.5ex; margin-bottom: 0.5ex; }
#sharing h1, #sharing .footer { display: none; }
div.compiler_output .back_to_editor { display: none; }
div#minibar {
border: 1px solid black;
padding: 5px;
background: #ccc url("../minibar/brushed-metal.png");
}

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html> <head>
<title>Download from Grammar Cloud</title>
<link rel="stylesheet" type="text/css" href="editor.css" title="Cloud">
<link rel="alternate stylesheet" type="text/css" href="molto.css" title="MOLTO">
</head>
<body>
<h1><img src="P/1307545048_weather_09.png" alt="">Download from Grammar Cloud</h1>
<pre id=debug></pre>
<hr>
<address></address>
<!-- hhmts start -->Last modified: Wed Apr 3 21:53:09 CEST 2013 <!-- hhmts end -->
<script type="text/javascript" src="../js/support.js"></script>
<script type="text/javascript" src="../js/localstorage.js"></script>
<script type="text/javascript" src="localstorage.js"></script>
<script type="text/javascript" src="gf_abs.js"></script>
<script type="text/javascript" src="editor.js"></script>
<script type="text/javascript" src="cloud2.js"></script>
<script type="text/javascript" src="sort.js"></script>
<script type="text/javascript">
download_from_cloud();
</script>
</body>
</html>

View File

@@ -0,0 +1,86 @@
var internet_explorer=navigator.appName=="Microsoft Internet Explorer";
/* How to change opacity in IE:
http://joseph.randomnetworks.com/archives/2006/08/16/css-opacity-in-internet-explorer-ie/
*/
var set_opacity =
internet_explorer
? function(el,o) { el.style.filter="alpha(opacity="+Math.round(o*100)+")";}
: function(el,o) { el.style.opacity=o; };
function start_slideshow(img,options) {
var p=img.parentNode;
if(p.tagName=="A") p=p.parentNode;
var is=p.getElementsByTagName("img");
if(is.length>1) {
var cur=0;
var w=img.width;
var h=img.height;
//p.style.position="relative";
p.style.minWidth=w+"px";
p.style.minHeight=h+"px";
var images=[];
for(var i=0;i<is.length;i++) {
images[i]=is[i];
var c=images[i];
if(internet_explorer) c.style.zoom=1;
c.style.position="absolute";
}
var timeout=1000*(options.delay || 5);
var ft=options.fade==null ? 1 : options.fade;
var tick=function() {
var c=images[cur];
cur= (cur+1) % images.length;
var n=images[cur];
set_opacity(n,0);
//n.style.position="static";
n.style.zIndex=1;
n.className="";
if(n.width>w) { w=n.width; p.style.minWidth=w+"px"; }
if(n.height>h) { h=n.height; p.style.minHeight=h+"px"; }
c.style.position="absolute";
c.style.zIndex=0;
fade(n,0,1,ft,function() {
if(c.width>n.width || c.height>n.height) fade(c,1,0,ft,null);
else set_opacity(c,0); });
//debug.innerHTML=w+"x"+h;
//for(var i=0;i<images.length;i++)
//debug.appendChild(text(" "+images[i].style.position));
}
//var debug=document.createElement("div");
//p.parentNode.insertBefore(debug,p);
//debug.innerHTML=w+"x"+h;
setInterval(tick,timeout);
}
//else alert("No slideshow!");
}
function fade(el,start,stop,t,after) {
// el: which element to fade
// start: starting opacity [0..1]
// stop: ending opacity [0..1]
// t: duration of fade (in seconds), default 1s
// after: function to call when done fading, optional
var dt=40; // Animation granularity, 1/40ms = 25fps
el.step=(stop-start)*dt/(1000*(t==null ? 1 : t));
el.stop=stop;
//alert("fade "+start+" "+stop+" "+el.step);
var done=function() {
clearInterval(el.timer);
el.timer=null;
if(after) after();
}
var f=function() {
var next=el.current+el.step;
if(next>=1) { next=1; done(); }
if(next<=0) { next=0; done(); }
set_opacity(el,next);
el.current=next
}
if(!el.timer) {
el.current=start;
el.timer=setInterval(f,dt);
}
}

View File

@@ -0,0 +1,164 @@
function initialize_sorting(tagList,classList) {
/*
var debugoutput=empty("pre");
document.body.appendChild(debugoutput)
function jsdebug(txt) {
//clear(debugoutput)
debugoutput.appendChild(text(txt+"\n"))
}
*/
function listToSet(list) {
var set={}
for(var i in list) set[list[i]]=true
return set;
}
var sortable_tag=listToSet(tagList)
var sortable_class=listToSet(classList)
function sortable(elem) {
return elem && elem.tagName!="INPUT" && elem.tagName!="BUTTON"
&& (sortable_tag[elem.tagName]
? sortable_class[elem.className]
? elem
: null
: sortable(elem.parentNode))
}
function move_element(elem,x,y) {
elem.style.left=x+"px";
elem.style.top=y+"px";
elem.delta={x:x,y:y};
}
function adjust_refs(elem,dy) {
//jsdebug("dy="+dy);
move_element(elem,elem.delta.x,elem.delta.y-dy);
elem.downAt.y+=dy;
elem.range.lo-=dy;
elem.range.hi-=dy;
}
function move_up(elem) {
var prev=elem.previousElementSibling;
if(prev) {
var top=elem.offsetTop;
var mid=prev.offsetTop+prev.offsetHeight/2;
if(top<mid) {
elem.parentNode.insertBefore(elem,prev);
adjust_refs(elem,elem.offsetTop-top)
}
//else jsdebug("not yet, top="+top+", mid="+mid);
}
//else jsdebug("at top");
}
function move_down(elem) {
var next=elem.nextElementSibling;
if(next) {
var top=elem.offsetTop;
var bot=top+elem.offsetHeight;
var mid=next.offsetTop+next.offsetHeight/2;
if(bot>mid) {
next.parentNode.insertBefore(next,elem);
adjust_refs(elem,elem.offsetTop-top)
}
//else jsdebug("not yet, top="+top+", bot="+bot+", mid="+mid);
}
//else jsdebug("at bottom");
}
function swap(elem,dy) {
if(dy>0) move_down(elem);
else if(dy<0) move_up(elem);
}
function restrictTo(range,y) {
return Math.min(range.hi,Math.max(range.lo,y));
}
// -------------------------------------------------------------------------
// These functions isolate the difference between mouse interfaces and touch
// interfaces
function eventPosition(event) {
var p=event
if(event.touches) p=event.touches[0]
return {x:p.screenX,y:p.screenY}
}
function setStartHandler(ondown) {
if("ontouchstart" in window) document.ontouchstart=ondown
else document.onmousedown=ondown
}
function setDragHandlers(onmove,onend) {
if("ontouchstart" in window) {
document.ontouchmove=onmove;
document.ontouchend=onend;
}
else {
document.onmousemove=onmove;
document.onmouseup=onend;
}
}
// -------------------------------------------------------------------------
function startDrag(event,elem) {
//jsdebug("Start dragging");
elem.style.position="relative";
elem.delta || (elem.delta={x:0,y:0});
var p=eventPosition(event)
elem.downAt={x:p.x-elem.delta.x,y:p.y-elem.delta.y};
var list=elem.parentNode;
// elem and list must have the same offsetParent for this to work!!
var top=list.offsetTop-elem.offsetTop+elem.delta.y;
elem.range={lo:top,hi:top+list.offsetHeight-elem.offsetHeight};
elem.style.zIndex=1;
//console.log("Start dragging",elem.id,list.offsetTop,elem.offsetTop,elem.range.lo,elem.range.hi)
function dragMove(event) {
var p=eventPosition(event)
var dx=0/*p.x-elem.downAt.x*/;
var dy=restrictTo(elem.range,p.y-elem.downAt.y);
//jsdebug("dragging to "+dx+" "+dy+" "+show_props(elem.range,"range"));
//console.log("dragging to ",dy);
move_element(elem,dx,dy);
//jsdebug("dragging to "+elem.offsetLeft+" "+elem.offsetTop);
swap(elem,dy)
return false;
}
function dragEnd() {
//jsdebug("dropped");
elem.style.zIndex=0;
move_element(elem,0,0);
setDragHandlers(null,null)
preventScroll=false;
return false;
}
preventScroll=true;
setDragHandlers(dragMove,dragEnd)
return false;
}
function mousedown(event) {
var elem=sortable(event.target);
if(elem) return startDrag(event,elem);
//else jsdebug("Clicked outside"/*+taglist(event.target)/*+show_props(event,"event")*/);
}
//var jsdebug=debug;
//https://stackoverflow.com/questions/49500339/cant-prevent-touchmove-from-scrolling-window-on-ios
var preventScroll=false;
function pd(e) {if(preventScroll) e.preventDefault()}
function init() {
setStartHandler(mousedown)
document.addEventListener("touchmove",pd,{passive:false})
//var d=element("javascriptdebug");
//if(d) jsdebug=function(msg) { d.innerHTML=msg; }
}
init();
}
//Inspired by http://tool-man.org/examples/sorting.html

View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<!-- This is the start page served by "gf -server" -->
<title>GF Cloud Service</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="cloud.css" title="Cloud">
<meta name = "viewport" content = "width = device-width">
<h1><a href="http://www.grammaticalframework.org/"><img src="Logos/gf0.png" alt=""></a><img class=nofloat src="P/gf-cloud.png" alt=""> GF Cloud Service</h1>
<h2>Web Applications</h2>
<ul>
<li><a href="minibar/minibar.html">Minibar</a>
(word-completing translation tool)
<li><a href="syntax-editor/editor.html">Syntax Editor</a>
(for building and manipulating abstract syntax trees)
<li><a href="TransQuiz/translation_quiz.html">Translation Quiz</a>
<li><a href="gfse/">GF online editor for simple multilingual grammars</a>
<li><a href="translator/">Simple Translation Tool</a>
(bilingual document editor)
<!--<li><a href="wc.html">Wide Coverage Translation Demo</a>-->
<li><a href="gfmorpho/">Word inflection with smart paradigms</a>
<li><a href="wordnet/">GF WordNet</a> (an online browser and editor for the WordNet lexicon)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a href="minibar/about.html">About Minibar</a>
<li><a href="minibar/minibar-api.html">Minibar API documentation</a>
<li><a href="gfse/about.html">About the online grammar editor</a>
<li><a href="translator/about.html">About the simple translation tool</a>
<li><a href="gf-web-api-examples.html">PGF web service API examples</a>
<li><a href="gf-web-api.html">PGF web service API documentation</a>
<li><a href="gf-cloud-api.html">GF Cloud Service API</a>
</ul>
<hr>
<a href="http://www.grammaticalframework.org/">www.grammaticalframework.org</a>
<div class=version><small><a href="/version">GF version</a></small></div>

314
src/compiler/www/js/d3Tree.js vendored Normal file
View File

@@ -0,0 +1,314 @@
// Copied from https://github.com/christos-c/tree-viewer (TH 2015-03-24)
// Inspired by "D3.js Drag and Drop Zoomable Tree" by Rob Schmuecker <robert.schmuecker@gmail.com>
// https://gist.github.com/robschmuecker/7880033
function d3Tree(treeData) {
// panning variables
var panSpeed = 200;
// Misc. variables
var i = 0;
var duration = 450;
var root;
// size of the diagram
var pageWidth = $(document).width();
var viewerWidth = pageWidth - (0.2 * pageWidth);
var viewerHeight = 500;
var tree = d3.layout.tree()
.size([viewerWidth-20, viewerHeight]);
// define a d3 diagonal projection for use by the node paths later on.
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.x, d.y];
});
// Can be used to draw the links between nodes instead of the diagonal
// TODO Doesn't work with the collapse/expand transition
//function straightLine(d) {
// return "M" + d.source.x + "," + d.source.y + "L" + d.target.x + "," + d.target.y;
//}
// A recursive helper function for performing some setup by walking through all nodes
function visit(parent, visitFn, childrenFn) {
if (!parent) return;
visitFn(parent);
var children = childrenFn(parent);
if (children) {
var count = children.length;
for (var i = 0; i < count; i++) {
visit(children[i], visitFn, childrenFn);
}
}
}
// TODO: Pan function, can be better implemented.
function pan(domNode, direction) {
var speed = panSpeed;
if (panTimer) {
clearTimeout(panTimer);
translateCoords = d3.transform(svgGroup.attr("transform"));
if (direction == 'left' || direction == 'right') {
translateX = direction == 'left' ? translateCoords.translate[0] + speed : translateCoords.translate[0] - speed;
translateY = translateCoords.translate[1];
} else if (direction == 'up' || direction == 'down') {
translateX = translateCoords.translate[0];
translateY = direction == 'up' ? translateCoords.translate[1] + speed : translateCoords.translate[1] - speed;
}
scaleX = translateCoords.scale[0];
scaleY = translateCoords.scale[1];
scale = zoomListener.scale();
svgGroup.transition().attr("transform", "translate(" + translateX + "," + translateY + ")scale(" + scale + ")");
d3.select(domNode).select('g.node').attr("transform", "translate(" + translateX + "," + translateY + ")");
zoomListener.scale(zoomListener.scale());
zoomListener.translate([translateX, translateY]);
panTimer = setTimeout(function() {
pan(domNode, speed, direction);
}, 50);
}
}
// Define the zoom function for the zoomable tree
function zoom() {
svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
// define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents
var zoomListener = d3.behavior.zoom().scaleExtent([0.1, 3]).on("zoom", zoom);
// remove the previous svg if there
d3.select("svg").remove();
// define the baseSvg, attaching a class for styling and the zoomListener
var baseSvg = d3.select("#tree-container").append("svg")
.attr("width", viewerWidth)
.attr("height", viewerHeight)
.attr("class", "overlay")
.call(zoomListener)
.on("dblclick.zoom", null);
// The arrowmarker to be appended at the end of each path
// TODO Looks terrible (not currently used)
baseSvg.append("marker")
.attr("id", "markerArrow")
.attr("markerWidth", 4)
.attr("markerHeight", 4)
.attr("refY","2")
.attr("refX", "10")
.attr("orient", "auto")
.append("polygon")
.attr("points", "0,0 4,2 0,4")
.attr("style", "fill: #ccc");
// Helper functions for collapsing and expanding nodes.
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
function expand(d) {
if (d._children) {
d.children = d._children;
d.children.forEach(expand);
d._children = null;
}
}
var overCircle = function(d) {
selectedNode = d;
updateTempConnector();
};
var outCircle = function(d) {
selectedNode = null;
updateTempConnector();
};
// Toggle children function
function toggleChildren(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else if (d._children) {
d.children = d._children;
d._children = null;
}
return d;
}
// Toggle children on click.
function click(d) {
if (d3.event.defaultPrevented) return; // click suppressed
d = toggleChildren(d);
update(d);
}
function update(source) {
// Compute the new height, function counts total children of root node and sets tree height accordingly.
// This prevents the layout looking squashed when new nodes are made visible or looking sparse when nodes are removed
// This makes the layout more consistent.
var levelHeight = [1];
var childCount = function(level, n) {
if (n.children && n.children.length > 0) {
if (levelHeight.length <= level + 1) levelHeight.push(0);
levelHeight[level + 1] += n.children.length;
n.children.forEach(function(d) {
childCount(level + 1, d);
});
}
};
childCount(0, root);
var maxLevel = levelHeight.length+2;
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Set heights between levels based on maxLevel.
nodes.forEach(function(d) {
d.y = (d.depth * (viewerHeight/(maxLevel)));
});
// Update the nodes…
var node = svgGroup.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + source.x0 + "," + source.y0 + ")";
})
.on('click', click);
nodeEnter.append("rect")
.attr('class', 'nodeRect')
// Size of the rectangle/2
.attr("x", function(d){return -(d.name.length*5+10)/2})
.attr("y", -10)
.attr("width", 0)
.attr("height", 0)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter.append("text")
.attr("y", 0)
.attr("dy", ".35em")
.attr('class', 'nodeText')
.attr("text-anchor", "middle")
.text(function(d) {
return d.name;
})
.style("fill-opacity", 0);
// Update the text to reflect whether node has children or not.
node.select('text')
.attr("y", 0)
.attr("text-anchor", "middle")
.text(function(d) {
return d.name;
});
node.select("rect.nodeRect")
.attr("width", function(d) {
// Adjust the size of the square according to the label
return d.children || d._children ? d.name.length*5+10 : 0;
})
.attr("height", function(d) {
return d.children || d._children ? 20 : 0;
})
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
// Fade the text in
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.x + "," + source.y + ")";
})
.remove();
nodeExit.select("circle")
.attr("r", 0);
nodeExit.select("text")
.style("fill-opacity", 0);
// Update the links…
var link = svgGroup.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
//TODO MARKERS LOOK TERRIBLE
// .attr("marker-end", "url(#markerArrow)")
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o,target: o});
});
// TODO doesn't work with the transition
// .attr("d", straightLine);
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// TODO doesn't work with the transition
// .attr("d", straightLine);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o,target: o});
})
// TODO doesn't work with the transition
// .attr("d", straightLine)
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Append a group which holds all nodes and which the zoom Listener can act upon.
var svgGroup = baseSvg.append("g");
// Define the root
root = treeData;
root.x0 = viewerWidth / 2;
root.y0 = 0;
// Layout the tree initially and center on the root node.
update(root);
d3.select('g').attr("transform", "translate(0,20)");
}

View File

@@ -0,0 +1,43 @@
/* --- GF robust parser interface ------------------------------------------- */
var gfrobust = {}
gfrobust.url="http://www.grammaticalframework.org:41296/robust-parser/parse"
gfrobust.jsonurl="http://www.grammaticalframework.org:41296/robust-parser.cgi"
gfrobust.grammar="Parse" // the name of the grammar
gfrobust.targetlist=[] // do not use, exposed only for debugging
gfrobust.call=function(querystring,cont) {
http_get_json(gfrobust.jsonurl+querystring,cont)
}
// Translate a sentence to the given target language
gfrobust.translate=function(source,to,cont) {
var encsrc=encodeURIComponent(source)
if(encsrc.length<200) // match limit in runtime/c/utils/pgf-server.c
gfrobust.call("?sentence="+encsrc+"&to="+gfrobust.grammar+to,cont)
else cont("[GF robust parser: sentence too long]")
}
// Get the url of a parse tree image (SVG)
gfrobust.parsetree_url=function(source) {
return gfrobust.url+"?sentence="+encodeURIComponent(source)
}
// Get functions to test which source and target langauges are supported
gfrobust.get_support=function(cont) {
function ssupport(code) { return code=="Eng" }
function tsupport(code) { return gfrobust.targets[code] }
function init2(langstr) {
var ls=langstr.split(" "); // ls probably contains an empty string here
var langs=[], pre=gfrobust.grammar, n=pre.length
for(var i=0;i<ls.length;i++)
if(ls[i].substr(0,n)==pre) langs.push(ls[i].substr(n))
gfrobust.targetlist=langs
gfrobust.targets=toSet(langs)
cont(ssupport,tsupport)
}
if(gfrobust.target) cont(ssupport,tsupport)
else gfrobust.call("",init2) // retrieve list of supported target languages
}

View File

@@ -0,0 +1,167 @@
/* --- GF wide coverage translation interface ------------------------------- */
var gftranslate = {}
gftranslate.jsonurl="/robust/App16.pgf"
gftranslate.grammar="App" // the name of the grammar
gftranslate.documented_classes=
["N", "N2", "N3", "A", "A2", "V", "V2", "VV", "VS", "VQ", "VA", "V3", "V2V",
"V2S", "V2Q", "V2A", "Adv", "Prep"]
gftranslate.call=function(querystring,cont,errcont) {
http_get_json(gftranslate.jsonurl+querystring,cont,errcont)
}
function enc_langs(g,to) {
return Array.isArray(to)
? to.map(function(l){return g+l}).join("+")
: g+to
}
function unspace_translations(g,trans) {
var langs=[g+"Chi",g+"Jpn",g+"Tha"]
for(var i=0;i<trans.length;i++) {
var lins=trans[i].linearizations
if(lins) {
for(var j=0;j<lins.length;j++) {
var lin=lins[j]
if(elem(lin.to,langs)) {
//console.log(i,j,"space",lin.to,lin.text)
lin.text=lin.text.split(" ").join("")
//console.log(i,j,"unspace",lin.to,lin.text)
}
}
}
}
return trans
}
function length_limit(lang) {
switch(lang) {
case "Bul":
case "Chi":
case "Eng":
case "Swe":
return 500
default:
return 200
}
}
function check_limit(lang,source) {
var len=source.length, limit=length_limit(lang)
return len<=limit ? null : "sentense too long, "+len+">"+limit
}
// Translate a sentence
gftranslate.translate=function(source,from,to,start,limit,cont) {
var g=gftranslate.grammar
var lexer="&lexer=text"
if(from=="Chi") lexer="",source=source.split("").join(" ")
function errcont(text,code) { cont([{error:code+" "+text}]) }
function extract(result) {
cont(unspace_translations(g,result[0].translations))
}
var too_long=check_limit(from,source)
if(too_long) cont([{error:too_long}])
else
gftranslate.call("?command=c-translate&jsontree=true&input="
+encodeURIComponent(source)
+lexer+"&unlexer=text&from="+g+from+"&to="+enc_langs(g,to)
+"&start="+start+"&limit="+limit,extract,errcont)
}
// Translate a sentence word for word (if all else fails...)
gftranslate.wordforword=function(source,from,to,cont) {
var g=gftranslate.grammar
var lexer="&lexer=text"
if(from=="Chi") lexer="",source=source.split("").join(" ")
function errcont(text,code) { cont([{error:code+" "+text}]) }
function extract(result) {
cont(unspace_translations(g,result[0].translations))
}
var enc_to = enc_langs(g,to)
var too_long=check_limit(from,source)
if(too_long) cont([{error:too_long}])
else
gftranslate.call("?command=c-wordforword&input="
+encodeURIComponent(source)
+lexer+"&unlexer=text&from="+g+from+"&to="+enc_to
,extract,errcont)
}
// Get list of supported languages
gftranslate.waiting=[]
gftranslate.get_languages=function(cont,errcont) {
function init2(grammar_info) {
var ls=grammar_info.languages
gftranslate.grammar=grammar_info.name
var langs=[], pre=gftranslate.grammar, n=pre.length
for(var i=0;i<ls.length;i++)
if(ls[i].name.substr(0,n)==pre) langs.push(ls[i].name.substr(n))
gftranslate.targetlist=langs
var w=gftranslate.waiting
for (var i=0;i<w.length;i++) w[i].cont(langs)
gftranslate.waiting=[]
}
function init2error(text,status,ct) {
var w=gftranslate.waiting
for (var i=0;i<w.length;i++) {
var e=w[i].errcont
if(e) e(text,status,ct)
}
gftranslate.waiting=[]
}
if(gftranslate.targetlist) cont(gftranslate.targetlist)
else {
gftranslate.waiting.push({cont:cont,errcont:errcont})
if(gftranslate.waiting.length<2)
gftranslate.call("?command=c-grammar",init2,init2error)
}
}
// Get functions to test which source and target langauges are supported
gftranslate.get_support=function(cont,errcont) {
function support(code) { return gftranslate.targets[code] }
function init2(langs) {
gftranslate.targets=toSet(langs)
cont(support,support)
}
if(gftranslate.targets) cont(support,support)
else gftranslate.get_languages(init2,errcont)
}
// trans_text_quality : String -> {quality:String, text:String}
function trans_text_quality(text) {
var quality="default_quality"
switch(text[0]) {
case '+': text=text.substr(1).trimLeft(); quality="high_quality"; break;
case '*': text=text.substr(1).trimLeft(); quality="low_quality"; break;
}
return {quality:quality,text:text}
}
// find_to :: Lang -> [{to:Lang,...}] -> Int
function find_to(to,lins) {
for(var i=0;i<lins.length;i++)
if(lins[i].to==to) return i
return -1 // Hmm....
}
function trans_quality(r,to) {
var ix=to ? find_to(to,r.linearizations) : 0
if(ix<0) return null
else {
var text=r.linearizations[ix].text
if(r.prob==0) return {quality:"high_quality",text:text}
else if(r.prob<0) return {quality:"bad_quality",text:text}
else {
var t=trans_text_quality(text)
if(t.quality=="default_quality" && r.tree && r.tree[0]=="?")
t.quality="low_quality"
return t
}
}
}

View File

@@ -0,0 +1,180 @@
/* --- Grammar Manager object ----------------------------------------------- */
/*
This object stores the state for:
- grammar
- startcat
- languages
Hooks which actions can be hooked to:
- onload
- change_grammar
- change_startcat
- change_languages
*/
function GrammarManager(server,opts) {
var t = this;
/* --- Configuration ---------------------------------------------------- */
// default values
this.options = {
initial: {}
};
this.actions = {
onload: [
function(gm){ debug("default action: onload"); }
],
change_grammar: [
function(grammar){ debug("default action: change grammar"); }
],
change_startcat: [
function(startcat){ debug("default action: change startcat"); }
],
change_languages: [
function(languages){ debug("default action: change languages"); }
]
}
// Apply supplied options
if(opts) for(var o in opts) this.options[o]=opts[o];
/* --- Client state initialisation -------------------------------------- */
this.server = server;
this.grammar = null; // current grammar
this.grammars=[];
this.grammar_dirs=[];
this.startcat = null; // current startcat
this.languages = this.options.initial.languages || [];
// current languages (empty means all langs)
/* --- Main program, this gets things going ----------------------------- */
this.init=function(){
this.server.get_grammarlists(bind(this.onload,this));
}
this.init();
}
//
//GrammarManager.prototype.update_grammar_list=function(dir,grammar_names,dir_count) {
GrammarManager.prototype.onload=function(dir,grammar_names,dir_count) {
var t=this;
t.grammars=[];
t.grammar_dirs=[];
t.grammar_dirs.push(dir);
t.grammars=t.grammars.concat(grammar_names.map(function(g){return dir+g}));
var grammar0=t.options.initial.grammar || t.grammars[0];
t.change_grammar(grammar0);
// Execute hooked actions
t.run_actions("onload",dir,grammar_names,dir_count);
}
/* --- Registering / unregistering actions to hooks ------------------------- */
GrammarManager.prototype.register_action=function(hook,action) {
var hookring = this.actions[hook];
hookring.push(action);
}
GrammarManager.prototype.unregister_action=function(hook,action) {
var hookring = this.actions[hook];
for (var f=0; f < hookring.length; f++) {
if (hookring[f] == action) {
hookring = Array.remove(hookring, f);
}
}
}
// Execute actions for a given hook
// TODO: any number of arguments
GrammarManager.prototype.run_actions=function(hook,arg1,arg2,arg3) {
var acts = this.actions[hook];
for (f in acts) {
acts[f](arg1,arg2,arg3);
}
}
/* --- Grammar -------------------------------------------------------------- */
// API
GrammarManager.prototype.change_grammar=function(grammar_url) {
var t=this;
t.server.switch_to_other_grammar(grammar_url, function() {
t.server.grammar_info(function(grammar){
// Set internal state
t.grammar = grammar;
// Call internal functions
t.update_startcat(grammar);
t.update_language_list(grammar);
// Execute hooked actions
t.run_actions("change_grammar",grammar);
});
});
}
/* --- Start category ------------------------------------------------------- */
// Internal
// Sets default startcat for grammar
GrammarManager.prototype.update_startcat=function(grammar) {
var t=this;
var cats=grammar.categories;
var startcat0 = t.options.initial.startcat;
if (elem(startcat0, cats))
t.startcat = startcat0;
else
t.startcat = grammar.startcat;
}
// API
GrammarManager.prototype.change_startcat=function(startcat) {
var t = this;
// Set internal state
t.startcat = startcat;
// Call internal functions
// ...
// Execute hooked actions
t.run_actions("change_startcat",startcat);
}
/* --- Languages ------------------------------------------------------------ */
// Internal
// Sets default languages for grammar
GrammarManager.prototype.update_language_list=function(grammar) {
var t = this;
function langpart(conc,abs) { // langpart("FoodsEng","Foods") == "Eng"
return hasPrefix(conc,abs) ? conc.substr(abs.length) : conc;
}
// Replace the options in the menu with the languages in the grammar
var langs=grammar.languages;
for(var i=0; i<langs.length; i++) {
var ln=langs[i].name; // "PhrasebookEng"
if(!hasPrefix(ln,"Disamb")) {
var lp=langpart(ln,grammar.name); // "Eng"
if (elem(lp, t.options.initial.languages)) {
t.languages.push(ln);
}
}
}
}
// API
GrammarManager.prototype.change_languages=function(languages) {
var t = this;
// Set internal state
t.languages = languages;
// Call internal functions
// ...
// Execute hooked actions
t.run_actions("change_languages",languages);
}

View File

@@ -0,0 +1,70 @@
// Language names and ISO-639 codes (both 3-letter and 2-letter codes)
// See http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
var languages =
function() {
function lang1(namecode2) {
function lang(code,name,code2) {
return {code:code, name:name, code2:code2}
}
var nc=namecode2.split(":")
var name=nc[0]
var ws=name.split("/")
var code2=nc.length>1 ? nc[1] : ""
return ws.length==1 ? lang(name.substr(0,3),name,code2)
: lang(ws[0],ws[1],code2)
}
var ls
// [ISO-639-2 code "/"] language name ":" ISO 639-1 code
ls=["Afrikaans:af","Amharic:am","Arabic:ar","Bulgarian:bg","Catalan:ca",
"Chinese:zh","Czech:cs","Danish:da","Dutch:nl","English:en",
"Estonian:et","Finnish:fi","French:fr","German:de","Greek:el",
"Hebrew:he","Hindi:hi","Ina/Interlingua:ia",
"Icelandic:is","Gle/Irish:ga","Italian:it","Jpn/Japanese:ja",
"Latin:la","Lav/Latvian:lv","Mlt/Maltese:mt","Mongolian:mn",
"Nepali:ne","Norwegian:nb","Pes/Persian:fa","Polish:pl",
"Portuguese:pt","Pnb/Punjabi:pa",
"Ron/Romanian:ro","Russian:ru","Snd/Sindhi:sd","Spanish:es",
"Swedish:sv","Thai:th","Turkish:tr","Urdu:ur"]
// GF uses nonstd 3-letter codes? Pes/Persian:fa, Pnb/Punjabi:pa
return map(lang1,ls)
}()
var langname={}
var langcode={}
var langcode2={}
var langcode3={}
for(var i in languages) {
langname[languages[i].code]=languages[i].name
langcode[languages[i].name]=languages[i]
langcode2[languages[i].code]=languages[i].code2
langcode3[languages[i].code2]=languages[i].code
}
function concname(code) { return langname[code] || code; }
function alangcode(code) { return langcode2[code] || code; }
// Add a country code to the language code
function add_country(code) {
switch(code) {
case "en": return "en-US" // "en-scotland" // or "en-GB"
case "sv": return "sv-SE"
case "fr": return "fr-FR"
case "de": return "de-DE"
case "fi": return "fi-FI"
case "zh": return "zh-CN"
case "hi": return "hi-IN"
case "es": return "es-ES"
case "it": return "it-IT"
case "bg": return "bg-BG" // ?
case "da": return "da-DK"
case "nb": return "nb-NO"
case "nl": return "nl-NL"
case "ja": return "ja-JP"
case "ro": return "ja-RO"
case "el": return "el-GR"
case "th": return "th-TH"
// ...
default: return code
}
}

View File

@@ -0,0 +1,60 @@
// See http://diveintohtml5.info/storage.html
function supports_html5_storage() {
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch (e) {
return false;
}
}
var fakedLocalStorage = [] // a shared substitute for persistent localStorage
// An interface to localStorage, to store JSON data under a unique prefix
function appLocalStorage(appPrefix,privateStorage) {
function parse(s,def) {
try { return JSON.parse(s) } catch(e) { return def }
}
function methods(storage) {
return {
get: function (name,def) {
var id=appPrefix+name
return parse(storage[id]||"",def);
},
put: function (name,value) {
var id=appPrefix+name;
storage[id]=JSON.stringify(value);
},
remove: function(name) {
var id=appPrefix+name;
delete storage[id]
},
ls: function(prefix) {
var pre=appPrefix+(prefix||"")
var files=[]
for(var i in storage)
if(hasPrefix(i,pre)) files.push(i.substr(pre.length))
files.sort()
return files
},
get count() { return this.get("count",0); },
set count(v) { this.put("count",v); }
}
}
function get_html5_storage() {
try {
return 'localStorage' in window
&& window['localStorage']
|| fakedLocalStorage
} catch (e) {
return fakedLocalStorage; // fake it
}
}
return methods(privateStorage || get_html5_storage())
}

View File

@@ -0,0 +1,80 @@
/* --- Grammar access object ------------------------------------------------ */
function pgf_online(options) {
var server = {
// State variables (private):
grammars_url: "/grammars/",
other_grammars_urls: [],
grammar_list: null,
current_grammar_url: null,
// Methods:
switch_grammar: function(grammar_url,cont) {
this.current_grammar_url=this.grammars_url+grammar_url;
if(cont) cont();
},
add_grammars_url: function(grammars_url,cont) {
this.other_grammars_urls.push(grammars_url);
if(cont) cont();
},
switch_to_other_grammar: function(grammar_url,cont) {
this.current_grammar_url=grammar_url;
if(cont) cont();
},
get_grammarlist: function(cont,err) {
if(this.grammar_list) cont(this.grammar_list)
else http_get_json(this.grammars_url+"grammars.cgi",cont,err);
},
get_grammarlists: function(cont,err) { // May call cont several times!
var ds=this.other_grammars_urls;
var n=1+ds.length;
function pair(dir) {
return function(grammar_list){cont(dir,grammar_list,n)}
}
function ignore_error(err) { console.log(err) }
this.get_grammarlist(pair(this.grammars_url),err)
for(var i in ds)
http_get_json(ds[i]+"grammars.cgi",pair(ds[i]),ignore_error);
},
pgf_call: function(cmd,args,cont,err) {
var url=this.current_grammar_url+"?command="+cmd+encodeArgs(args)
http_get_json(url,cont,err);
},
get_languages: function(cont,err) {
this.pgf_call("grammar",{},cont,err);
},
grammar_info: function(cont,err) {
this.pgf_call("grammar",{},cont,err);
},
get_random: function(args,cont,err) { // cat, limit
args.random=Math.random(); // side effect!!
this.pgf_call("random",args,cont,err);
},
linearize: function(args,cont,err) { // tree, to
this.pgf_call("linearize",args,cont,err);
},
complete: function(args,cont,err) { // from, input, cat, limit
this.pgf_call("complete",args,cont,err);
},
parse: function(args,cont,err) { // from, input, cat
this.pgf_call("parse",args,cont,err);
},
translate: function(args,cont,err) { // from, input, cat, to
this.pgf_call("translate",args,cont,err);
},
translategroup: function(args,cont,err) { // from, input, cat, to
this.pgf_call("translategroup",args,cont,err);
},
browse: function(args,cont,err) { // id, format
if(!args.format) args.format="json"; // sife effect!!
this.pgf_call("browse",args,cont,err);
}
};
for(var o in options) server[o]=options[o];
if(server.grammar_list && server.grammar_list.length>0)
server.switch_grammar(server.grammar_list[0]);
return server;
}

View File

@@ -0,0 +1,431 @@
/* --- Accessing document elements ------------------------------------------ */
function element(id) {
return document.getElementById(id);
}
/* --- JavaScript tricks ---------------------------------------------------- */
// To be able to use object methods that refer to "this" as callbacks
// See section 3.3 of https://github.com/spencertipping/js-in-ten-minutes/raw/master/js-in-ten-minutes.pdf
function bind(f, this_value) {
return function () {return f.apply (this_value, arguments)};
};
// Implement Array.isArray for older browsers that lack it.
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
if(!Array.isArray) {
Array.isArray = function (arg) {
return Object.prototype.toString.call(arg) == '[object Array]';
};
}
// Create a clone of an array
// http://davidwalsh.name/javascript-clone-array
if(!Array.clone) {
Array.clone = function(arg) {
return arg.slice(0);
};
}
// Array Remove - By John Resig (MIT Licensed)
// http://ejohn.org/blog/javascript-array-remove/if(!Array.remove) {
if(!Array.remove) {
Array.remove = function(arg, from, to) {
var rest = arg.slice((to || from) + 1 || arg.length);
arg.length = from < 0 ? arg.length + from : from;
return arg.push.apply(arg, rest);
};
}
/* --- JSONP ---------------------------------------------------------------- */
// Inspired by the function jsonp from
// http://www.west-wind.com/Weblog/posts/107136.aspx
// See also http://niryariv.wordpress.com/2009/05/05/jsonp-quickly/
// http://en.wikipedia.org/wiki/JSONP
function jsonp(url,callback)
{
if (url.indexOf("?") > -1)
url += "&jsonp="
else
url += "?jsonp="
url += callback;
//url += "&" + new Date().getTime().toString(); // prevent caching
var script = empty("script");
script.setAttribute("src",url);
script.setAttribute("type","text/javascript");
document.body.appendChild(script);
}
var json = {next:0};
// Like jsonp, but instead of passing the name of the callback function, you
// pass the callback function directly, making it possible to use anonymous
// functions.
function jsonpf(url,callback,errorcallback)
{
var name="callback"+(json.next++);
json[name]=function(x) { delete json[name]; callback(x); }
jsonp(url,"json."+name);
}
/* --- AJAX ----------------------------------------------------------------- */
function GetXmlHttpObject(handler)
{
var objXMLHttp=null
if (window.XMLHttpRequest)
{
// See http://www.w3.org/TR/XMLHttpRequest/
// https://developer.mozilla.org/en/xmlhttprequest
objXMLHttp=new XMLHttpRequest()
}
else if (window.ActiveXObject)
{
objXMLHttp=new ActiveXObject("Microsoft.XMLHTTP")
}
return objXMLHttp
}
function ajax_http(method,url,body,contenttype,callback,errorcallback) {
var http=GetXmlHttpObject()
if (!http) {
var errortext="Browser does not support HTTP Request";
if(errorcallback) errorcallback(errortext,500)
else alert(errortext)
}
else {
function statechange() {
if (http.readyState==4 || http.readyState=="complete") {
if(http.status<300) callback(http.responseText,http.status);
else if(errorcallback)
errorcallback(http.responseText,http.status,
http.getResponseHeader("Content-Type"));
else alert("Request for "+url+" failed: "
+http.status+" "+http.statusText);
}
}
http.onreadystatechange=statechange;
http.open(method,url,true)
if (contenttype != null) {
http.setRequestHeader("Content-Type", contenttype)
}
http.send(body)
}
return http
}
function ajax_http_get(url,callback,errorcallback) {
ajax_http("GET",url,null,null,callback,errorcallback)
}
function ajax_http_post(url,formdata,callback,errorcallback) {
ajax_http("POST",url,formdata,null,callback,errorcallback)
// See https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest#Using_FormData_objects
}
// JSON via AJAX
function ajax_http_get_json(url,cont,errorcallback) {
ajax_http_get(url, with_json(cont,errorcallback), errorcallback);
}
function ajax_http_post_json(url,formdata,cont,errorcallback) {
ajax_http_post(url, formdata, with_json(cont,errorcallback), errorcallback);
}
function ajax_http_post_querystring_json(url,querystring,cont,errorcallback) {
ajax_http("POST",url,querystring,"application/x-www-form-urlencoded",with_json(cont,errorcallback),errorcallback);
}
function with_json(cont,errorcallback) {
return function(txt){
if(txt) {
try {
var json=eval("("+txt+")")
} catch (e) {
if(errorcallback)
errorcallback("JSON parsing problem",500,"text/plain")
return
}
cont(json);
}
else {
if(errorcallback)
errorcallback("Empty response form server (crash?)",500,"text/plain")
}
}
}
function sameOrigin(url) {
var a=empty("a");
a.href=url; // converts to an absolute URL
return hasPrefix(a.href,location.protocol+"//"+location.host+"/");
}
/*
// Use AJAX when possible, fallback to JSONP
function http_get_json(url,cont,errorcallback) {
if(sameOrigin(url)) ajax_http_get_json(url,cont,errorcallback);
else jsonpf(url,cont,errorcallback);
}
*/
// For better error handling, always use AJAX, don't fallback to JSONP
// Cross-origin requests are allowed by the PGF service and are supported in
// all modern browsers.
// See http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
function http_get_json(url,cont,errorcallback) {
ajax_http_get_json(url,cont,errorcallback);
}
/* --- URL construction ----------------------------------------------------- */
function encodeArgs(args) {
var q=""
for(var arg in args)
if(args[arg]!=undefined)
q+="&"+arg+"="+encodeURIComponent(args[arg]);
return q;
}
/* --- HTML construction ---------------------------------------------------- */
function text(s) { return document.createTextNode(s); }
function node(tag,as,ds) {
var n=attr(as,document.createElement(tag));
if(ds) for(var i in ds) n.appendChild(ds[i]);
return n;
}
function attr(as,n) {
for(var a in as) n.setAttribute(a,as[a]);
return n
}
function empty(tag,name,value) {
var el=node(tag,{},[])
if(name && value) el.setAttribute(name,value);
return el;
}
function empty_id(tag,id) { return empty(tag,"id",id); }
function empty_class(tag,cls) { return empty(tag,"class",cls); }
function div_id(id,cs) { return node("div",{id:id},cs); }
function span_id(id) { return empty_id("span",id); }
function wrap(tag,contents) {
return node(tag,{},Array.isArray(contents) ? contents : [contents]);
}
function wrap_class(tag,cls,contents) {
return node(tag,{"class":cls},
contents ? Array.isArray(contents) ?
contents : [contents] : [])
}
function span_class(cls,contents) { return wrap_class("span",cls,contents); }
function div_class(cls,contents) { return wrap_class("div",cls,contents); }
function p(contents) { return wrap("p",contents); }
function dt(contents) { return wrap("dt",contents); }
function dd(contents) { return wrap("dd",contents); }
function li(contents) { return wrap("li",contents); }
function th(contents) { return wrap("th",contents); }
function td(contents) { return wrap("td",contents); }
function tr(cells) { return wrap("tr",cells); }
function button(label,action,key) {
var el=node("input",{"type":"button","value":label},[]);
if(typeof action=="string") el.setAttribute("onclick",action);
else el.onclick=action;
if(key) el.setAttribute("accesskey",key);
return el;
}
function option(label,value) {
return node("option",{"value":value},[text(label)]);
}
function hidden(name,value) {
return node("input",{type:"hidden",name:name,value:value},[])
}
function tda(cs) { return node("td",{},cs); }
function img(src) { return empty("img","src",src); }
function title(t,n) { return attr({title:t},n) }
/* --- Document modification ------------------------------------------------ */
function clear(el) { replaceInnerHTML(el,""); }
function replaceInnerHTML(el,html) { if(el) el.innerHTML=html; }
function replaceChildren(el,newchild) { clear(el); el.appendChild(newchild); }
function appendChildren(el,ds) {
for(var i in ds) el.appendChild(ds[i]);
return el;
}
function replaceNode(el,ref) {
ref.parentNode.replaceChild(el,ref)
}
function insertFirst(parent,child) {
parent.insertBefore(child,parent.firstChild);
}
function insertBefore(el,ref) { ref.parentNode.insertBefore(el,ref); }
function insertAfter(el,ref) {
ref.parentNode.insertBefore(el,ref.nextSibling);
}
function toggleHidden(el) {
if (el.classList.contains("hidden"))
el.classList.remove("hidden")
else
el.classList.add("hidden")
}
// Update the selected options in a menu that allow multiple selections
function updateMultiMenu(menu,selection) {
var set=toSet(selection)
var os=menu.options
for(var i=0;i<os.length;i++)
os[i].selected=set[os[i].value] || false
}
/* --- Document data extraction --------------------------------------------- */
// List the selected options in a menu that allows multiple selections
function multiMenuSelections(menu) {
var selection=[]
var os=menu.options;
for(var i=0;i<os.length;i++)
if(os[i].selected) selection.push(os[i].value)
return selection
}
/* --- Debug ---------------------------------------------------------------- */
function debug(s) {
var d=element("debug");
if(d) d.appendChild(text(s+"\n"))
}
function show_props(obj, objName) {
var result = "";
for (var i in obj) {
result += objName + "." + i + " = " + obj[i] + "<br>";
}
return result;
}
function field_names(obj) {
var result = "";
for (var i in obj) {
result += " " + i;
}
return result;
}
/* --- Data manipulation ---------------------------------------------------- */
function swap(a,i,j) { // Note: this doesn't work on strings.
var tmp=a[i];
a[i]=a[j];
a[j]=tmp;
return a;
}
function sort(a) {
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/sort
return a.sort();
/* // Note: this doesn't work on strings.
for(var i=0;i<a.length-1;i++) {
var min=i;
for(var j=i+1;j<a.length;j++)
if(a[j]<a[min]) min=j;
if(min!=i) swap(a,i,min);
}
return a;
*/
}
function filter(p,xs) {
var ys=[];
for(var i=0;i<xs.length;i++)
if(p(xs[i])) ys[ys.length]=xs[i];
return ys;
}
function implode(cs) { // array of strings to string
/*
var s="";
for(var i=0;i<cs.length;i++)
s+=cs[i];
return s;
*/
return cs.join("");
}
function hasPrefix(s,pre) { return s.substr(0,pre.length)==pre; }
function commonPrefix(s1,s2) {
for(var i=0;i<s1.length && i<s2.length && s1[i]==s2[i];i++);
return s1.substr(0,i);
}
/*
function all(p,xs) {
for(var i=0;i<xs.length;i++)
if(!p(xs[i])) return false;
return true;
}
*/
function map(f,xs) {
var ys=[];
for(var i=0;i<xs.length;i++) ys[i]=f(xs[i]);
return ys;
}
// map in continuation passing style
function mapc(f,xs,cont) { mapc_from(f,xs,0,[],cont); }
function mapc_from(f,xs,i,ys,cont) {
if(i<xs.length)
f(xs[i],function(y){ys[i]=y;mapc_from(f,xs,i+1,ys,cont)});
else
cont(ys);
}
function overlaps(as,bs) {
for(var i=0;i<as.length;i++)
if(elem(as[i],bs)) return true;
return false;
}
function elem(a,as) {
if (!as) return false;
for(var i=0;i<as.length;i++)
if(a==as[i]) return true;
return false;
}
function shuffle(a) {
for(i=0;i<a.length;i++) swap(a,i,Math.floor(Math.random()*a.length))
return a;
}
// Convert an array of strings to a set (for quick & easy membership tests)
function toSet(a) {
var set={}
for(var i=0;i<a.length;i++) set[a[i]]=true
return set
}

539
src/compiler/www/js/wc.js Normal file
View File

@@ -0,0 +1,539 @@
/* --- Wide Coverage Translation Demo web app ------------------------------- */
var wc={}
wc.selected_cnls=[] // list of grammar names
wc.cnls={} // maps grammars names to {pgf_online:...,grammar_info:{...}}
wc.f=document.forms[0]
wc.o=element("output")
wc.e=element("extra")
wc.i=element("grammarinfo")
wc.p=element("pick")
wc.grammarbox=element("grammarbox")
wc.os=[] /* output segment list
[{input,text:String; from,to::Lang;
target:Node;
rs::[TranslationResults];
current_pick::Int // index into rs or -1
}] */
wc.cache={} // output segment cache, indexed by source text
wc.translating=""
wc.delayed_translate=function() {
function restart(){
if(wc.f.input.value!=wc.translating) wc.translate()
var h=wc.f.input.scrollHeight,bh=document.body.clientHeight
if(h>bh) h=bh
if(wc.f.input.clientHeight<h) wc.f.input.style.height=h+15+"px"
}
if(wc.timer) clearTimeout(wc.timer);
wc.timer=setTimeout(restart,500)
}
wc.clear=function() {
wc.f.input.value=""
wc.f.input.style.height=""
clear(wc.o)
wc.delayed_translate()
}
wc.save=function() {
if(wc.local) {
var f=wc.f
wc.local.put("from",f.from.value)
wc.local.put("to",f.to.value)
wc.local.put("input",f.input.value)
wc.local.put("colors",f.colors.checked)
wc.local.put("cnls",wc.selected_cnls)
}
}
wc.load=function() {
if(wc.local) {
var f=wc.f
f.input.value=wc.local.get("input",f.input.value)
f.from.value=wc.local.get("from",f.from.value)
f.to.value=wc.local.get("to",f.to.value)
f.colors.checked=wc.local.get("colors",f.colors.checked)
wc.selected_cnls=wc.local.get("cnls",wc.selected_cnls)
wc.colors()
wc.delayed_translate()
}
}
wc.translate=function(redo) {
// redo=true => discard translated segment cache and resubmit translation
// requests to the server (browser cache may still be used)
var f=wc.f, e=wc.e, p=wc.p
/*
function disable(yes) {
f.translate.disabled=yes
f.to.disabled=yes
if(f.swap) f.swap.disabled=yes
}
*/
function split_punct(s) {
return s.split(/([.!?]+[ \t\n]+|\n\n+|[ \t\n]*[-•*+#]+[ \t\n]+)/)
}
function find_pick(rs) {
for(var i=0;i<rs.length && !rs[i].t;i++)
;
return i
}
function translate_segment(so) { // so = segment output
so.rs=[] // list of alternative translations for this segment
so.current_pick= -1 // index of currently selected alternative
function show_error(msg) {
//if(e) e.innerHTML="<span class=low_quality>Translation problem: "+msg+"</span>"
//else
{
replaceChildren(so.target,text("["+msg+"]"))
so.target.className="error"
}
//disable(false)
}
function show_pick(i) { return function() { show_trans(i); return false; } }
function show_picks() {
clear(p)
for(var i=0;i<so.rs.length;i++) {
if(so.rs[i].t) {
var pick=text(i+1) // +"⃝"
if(i!=so.current_pick) {
var pick=node("a",{href:"#"},[pick])
pick.onclick=pick.onmouseover=show_pick(i)
}
var q=so.rs[i].t.quality
p.appendChild(text(" "))
p.appendChild(span_class("pick "+q,pick))
}
}
if(!so.got_more) p.appendChild(text("..."))
/*
p.appendChild(wrap_class("small","pick",
node("a",{href:wc.google_translate_url(),
target:"google_translate"},
[text("Google Translate")])))
*/
}
function treetext(tree) {
function inflect(w,wcls) {
function show_inflections(lins) {
if(wc.e2) wc.e2.innerHTML=lins[0].text
}
function get_inflections() {
var tree="MkDocument+%22%22+(Inflection"+wcls+"+"+w+")+%22%22"
var l=gftranslate.grammar+f.to.value
gftranslate.call("?command=c-linearize&to="+l+"&tree="+tree,show_inflections)
}
var wn=wrap_class("span","inflect",text(w))
if(wc.e2) wn.onclick=get_inflections
return wn
}
function word(w) {
var ps=w.split("_")
var n=ps.length
return ps.length>1 && elem(ps[n-1],gftranslate.documented_classes)
? inflect(w,ps[n-1]) : text(w)
}
return tree.split(/([ ()]+)/).map(word)
}
function show_more() {
wc.selected=so
var r=so.rs[so.current_pick]
var prob=r.prob<=0 ? "" : r.prob || ""
if(e) {
clear(e)
var speak_from=speech_buttons(so.from,"",so.input)
var speak_to=speech_buttons(so.to,"",so.text)
speak_to.className=speak_from.className="speech_buttons"
e.appendChild(wrap("div",[speak_from,
text(so.input+" → "+so.text),
speak_to]))
e.appendChild(wrap("div",text(prob)))
if(r.tree) {
wc.e2=node("div",{id:"tree-container","class":"e2"})
e.appendChild(wrap("span",treetext(r.tree)))
/*
var g=gftranslate.jsonurl
var u="format=svg&tree="+encodeURIComponent(r.tree)
var from="&from="+r.grammar+f.to.value
r.imgurls=[g+"?command=c-abstrtree&"+u,
g+"?command=c-parsetree&"+u+from]
if(!r.img) {
r.img=node("img",{src:r.imgurls[0]},[])
r.img_ix=0
r.img.onclick=function() {
r.img_ix=1-r.img_ix
r.img.src=r.imgurls[r.img_ix]
}
}
else if(r.img.src!=r.imgurls[r.img_ix]) // language change?
r.img.src=r.imgurls[r.img_ix]
wc.e2.appendChild(r.img)
*/
e.appendChild(wc.e2)
if(window.d3 && window.d3Tree) window.d3Tree(wc.bracketsToD3(r.jsontree))
}
}
if(wc.p /*&& so.rs.length>1*/) show_picks()
//if(f.speak.checked) wc.speak(t.text,f.to.value)
if(!so.got_more) {
so.got_more=true
if(so.rs.length<10)
trans(so.input,so.rs.length,10-so.rs.length)
}
}
so.target.onclick=show_more
function show_trans(i) {
var r=so.rs[i]
if(!r.t) {
i=find_pick(so.rs)
r=so.rs[i]
}
if(r && r.t) {
replaceChildren(so.target,text(r.t.text))
so.text=r.t.text
so.target.className=r.t.quality
so.current_pick=i
if(wc.selected==so) show_more()
}
}
function showit(r,grammar) {
r.grammar=grammar
r.t=trans_quality(r,grammar+f.to.value)
so.rs.push(r)
var j=so.rs.length-1
if(so.current_pick<0 || so.current_pick==j) show_trans(j)
else if(wc.selected==so) show_picks()
//disable(false)
}
function word_for_word(text,cont) {
function step3(tra) {
if(tra.length>=1) {
var r=tra[0]
r.prob = -1
if(r.linearizations) showit(r,gftranslate.grammar)
else if(r.error!=undefined)
show_error(r.error)
}
else if(so.rs.length==0)
show_error("Unable to translate")
}
gftranslate.wordforword(text,f.from.value,wc.languages || f.to.value,step3)
}
function trans(text,i,count) {
function step3(tra) {
if(tra.length>=1) {
var r=tra[0]
if(r.error!=undefined) {
if(i==0 && so.rs.length==0) {
//show_error(r.error)
word_for_word(text)
}
}
else {
function cmp(a,b) { return a.prob-b.prob; }
tra=tra.sort(cmp)
for(var ti=0;ti<tra.length;ti++) {
var r=tra[ti]
if(r.linearizations) showit(r,gftranslate.grammar)
//else show_error("no linearizations")
}
}
}
else if(i==0 && so.rs.length==0)
show_error("Unable to translate")
}
gftranslate.translate(text,f.from.value,wc.languages || f.to.value,i,count,step3)
}
function step2(text) { trans(text,0,10) }
function step2cnl(text,ix) {
function step3cnl(results) {
var trans=results[0].translations
if(trans && trans.length>=1) {
for(var i=0;i<trans.length;i++) {
var r=trans[i]
r.prob=0
showit(r,cnl)
}
}
step2cnl(text,ix+1)
}
if(ix<wc.selected_cnls.length) {
var g=wc.cnls[wc.selected_cnls[ix]]
var gi=g.grammar_info
var langs=gi.languages.map(function(l) { return l.name; })
var cnl=gi.name
var from=cnl+f.from.value,to=cnl+f.to.value
if(elem(from,langs) && elem(to,langs))
g.pgf_online.translate({from:from,
//to:to,
lexer:"text",unlexer:"text",
jsontree:true,input:text},
step3cnl,
function(){step2cnl(text,ix+1)})
else step2cnl(text,ix+1)
}
else step2(text)
}
if(wc.selected_cnls) step2cnl(so.input,0)
else step2(so.input)
}
function change_segment_to(so,to) {
var rs=so.rs
if(rs) {
for(var i=0;i<rs.length;i++)
rs[i].t=trans_quality(rs[i],rs[i].grammar+to)
var i=so.current_pick
if(!rs[i].t) {
i=find_pick(rs)
//console.log("Change pick",so.current_pick,i)
so.current_pick=i
clear(p)
wc.selected=null
}
var r=rs[i]
so.text=r.t.text
replaceChildren(so.target,text(r.t.text))
so.target.className=r.t.quality
}
so.to=to
}
//disable(true)
clear(wc.o)
clear(e)
clear(p)
var old_selected=wc.selected
wc.selected=null
if(redo) {
wc.cache={}
var old=wc.cache
}
else {
var old=wc.cache
for(var i=0;i<wc.os.length;i++) old[wc.os[i].input]=wc.os[i]
// could also keep all copies if the same text occurs more than once...
wc.os=[]
}
wc.translating=f.input.value
var is=split_punct(wc.translating+"\n")
for(var i=0;i<is.length;i++) {
var same=old[is[i]]
if(same /*&& same.to==f.to.value*/ && same.from==f.from.value) {
// reuse an unchanged segment
wc.os[i]=same
wc.o.appendChild(same.target)
if(same==old_selected) wc.selected=same
delete old[is[i]] // can't use the same node twice
if(same.to!=f.to.value) change_segment_to(same,f.to.value)
}
else {
// create a new output segment
var o=wc.os[i]={input:is[i],text:is[i],
from:f.from.value,to:f.to.value}
if(i&1) { // punctuation
o.target=span_class("punct",text(is[i]))
wc.o.appendChild(o.target)
}
else { // text segment to be translated
o.target=span_class("placeholder",text(is[i]))
wc.o.appendChild(o.target)
translate_segment(o)
}
}
}
wc.save()
return false;
}
wc.speak=function(text,lang) {
if(wc.speech) {
var u=new SpeechSynthesisUtterance(text)
u.lang=add_country(alangcode(lang))
speechSynthesis.cancel()
speechSynthesis.speak(u)
}
}
wc.colors=function() {
document.body.className=wc.f.colors.checked ? "colors" : ""
wc.local.put("colors",wc.f.colors.checked)
}
wc.swap=function() {
var f=wc.f
function txt(so) { return so.text }
f.input.value=wc.os.map(txt).join("").trimRight()
var from=f.from.value
f.from.value=f.to.value
f.to.value=from
wc.translate()
}
wc.google_translate_url=function() {
return "http://translate.google.com/"
+"#"+alangcode(wc.f.from.value)
+"/"+alangcode(wc.f.to.value)
+"/"+encodeURIComponent(wc.f.input.value)
}
wc.try_google=function() {
var w=window.open(wc.google_translate_url(),
"google_translate")
w.focus()
}
wc.bracketsToD3=function(bs) {
if(bs.token) return {name:bs.token}
else if(bs.other) return {name:bs.other}
else if(bs.fun) {
var t={name:bs.fun}
if(bs.children/* && bs.children.length>0*/)
t.children=bs.children.map(wc.bracketsToD3)
return t
}
else return {name:"??"}
}
// Update language selection menus with the languages supported by the grammar
wc.init_languages=function () {
function init2(langs) {
replaceInnerHTML(wc.i,"Enter text to translate above")
wc.languages=langs
var langset=toSet(langs)
function update_menu(m) {
var l=m.value
clear(m)
for(var i=0;i<langs.length;i++)
m.appendChild(option(concname(langs[i]),langs[i]))
if(langset[l]) m.value=l
}
update_menu(wc.f.from)
update_menu(wc.f.to)
}
function initerror(errortext,status,ct) {
var msg = status==404 ? "The wide cover translation grammar was not found on the server" : "Server problem "+status
replaceChildren(wc.i,text(msg))
if(wc.i) wc.i.className="error"
}
replaceInnerHTML(wc.i,"Loading the wide coverage translation grammar, please wait...")
gftranslate.get_languages(init2,initerror)
}
wc.init_speech=function() {
var speak=element("speak")
if(speak) {
wc.speech=window.speechSynthesis && window.speechSynthesis.getVoices().length>0
if(wc.speech) speak.style.display="inline"
}
}
wc.show_grammarbox=function() {
wc.grammarbox.parentNode.style.display="block";
}
wc.hide_grammarbox=function() {
wc.grammarbox.parentNode.style.display="";
clear(wc.grammarbox)
}
wc.init_cnl=function(grammar) {
var g
if(wc.cnls[grammar]) g=wc.cnls[grammar]
else g=wc.cnls[grammar]={}
g.pgf_online=pgf_online({})
g.pgf_online.switch_grammar(grammar)
g.pgf_online.grammar_info(function(info){g.grammar_info=info})
}
wc.init_cnls=function() {
var gs=wc.selected_cnls
for(var i=0;i<gs.length;i++) wc.init_cnl(gs[i])
}
wc.select_grammars=function() {
function done() {
wc.hide_grammarbox()
var gs=[]
var glist=list.children
for(var i=0;i<glist.length;i++)
if(glist[i].cb.checked) gs.push(glist[i].grammar)
wc.selected_cnls=gs
wc.init_cnls()
wc.local.put("cnls",wc.selected_cnls)
wc.translate(true)
}
function cancel() {
wc.hide_grammarbox()
}
function remove(x,xs) {
function other(y) { return y!=x; }
return filter(other,xs)
}
function checkbox(grammar,checked) {
var vb=node("input",{type:"checkbox"})
vb.checked=checked
return vb
}
function grammar_pick(grammar,checked) {
var cb=checkbox(grammar,checked)
var p=[cb,text(" "+grammar.split(".pgf")[0])]
var dt=node("dt",{class:"grammar_pick"},p)
dt.cb=cb
dt.grammar=grammar
return dt
}
function show_list(grammars) {
var sg=wc.selected_cnls
for(var i=0;i<sg.length;i++) {
if(elem(sg[i],grammars))
list.appendChild(grammar_pick(sg[i],true))
else
remove(sg[i],wc.selected_cnls)
}
for(var i=0;i<grammars.length;i++)
if(!elem(grammars[i],wc.selected_cnls))
list.appendChild(grammar_pick(grammars[i],false))
}
clear(wc.grammarbox)
wc.grammarbox.appendChild(wrap("h2",[button("X",cancel),text("Select which domain-specific grammars to use")]))
wc.grammarbox.appendChild(text("These grammars are tried before the wide-coverage grammar. They can give higher quality translations within their respective domains."))
var list=empty("dl")
wc.grammarbox.appendChild(list)
wc.grammarbox.appendChild(button("OK",done))
wc.grammarbox.appendChild(button("Cancel",cancel))
wc.show_grammarbox()
wc.pgf_online.get_grammarlist(show_list)
}
wc.initialize=function(grammar_name,grammar_url) {
if(grammar_name && grammar_url) {
gftranslate.grammar=grammar_name
gftranslate.jsonurl=grammar_url
}
wc.init_languages()
//init_speech()
setTimeout(wc.init_speech,500) // A hack for Chrome.
wc.pgf_online=pgf_online({});
wc.local=appLocalStorage("gf.wc."+gftranslate.grammar+".")
wc.load()
wc.init_cnls()
initialize_sorting(["DT"],["grammar_pick"])
wc.f.input.focus()
}

View File

@@ -0,0 +1,267 @@
<!DOCTYPE html>
<html> <head>
<title>About Minibar</title>
<link rel=stylesheet type="text/css" href="minibar.css">
<meta charset="UTF-8">
<meta name = "viewport" content = "width = device-width">
<style>
:target .summary { font-weight: bold; background: white; }
</style>
</head>
<body class=minibar>
<header>
<h1>About Minibar</h1>
</header>
<main>
<a href="minibar.html">Minibar</a> is an alternative implementation of the
<a href="http://www.grammaticalframework.org/">GF</a> web app
<a href="http://www.grammaticalframework.org:41296/fridge/">Fridge Poetry</a>.
It doesn't do everything the original Fridge Poetry does (e.g. drag-and-drop is missing),
so I refer to it as a minibar rather than a full refrigerator :-)
<p>
Some implementation details:
<ul class=space>
<li>It is implemented directly in JavaScipt. It does not use Google Web Toolkit or any big JavaScript libraries.
<li>It has been tested and found to work in the following browsers:
<ul>
<li>On the Mac: Firefox 3.5 &amp; 3.6, Safari 4.0, Opera 10.10 and
Google Chrome 4.0.249.49.
<li>On Linux: Firefox 3.0.18 & 3.5, Opera 10.10.
<li>On the Android Dev Phone: Android Mobile Safari 3.0.4 & 3.1.2
and Android Opera Mini 4.2.
</ul>
It does not seem work in Internet Explorer 7
(there are both styling and scripting issues).
There seems to be some rendering bugs in Chrome 5.0.342.9 β.
<li>The implementation consist of two JavaScript files:
<a href="minibar.js">minibar.js</a> and <a href="../js/support.js">support.js</a>
The latter is also used in
<a href="http://spraakbanken.gu.se/swe/forskning/saldo/ordspel">a couple of
small web apps</a> based on the
<a href="http://spraakbanken.gu.se/sal/ws/">SALDO web services</a>.
<li>To access the GF web service, it uses the
<a href="http://en.wikipedia.org/wiki/JSONP">JSONP method</a>
mentioned in the GF
web services paper, which allows the web app to be hosted on a different server
from the GF web service. (To demonstrate this, I put the Minibar demo on
www.cs.chalmers.se, while the GF server that it calls is on
www.grammaticalframework.org.)
<li>As an experiment, it does no use the <code>grammars.xml</code> file,
but instead calls a little CGI script,
<a href="http://www.grammaticalframework.org:41296/grammars/grammars.cgi.txt">grammars.cgi</a>
which lists the .pgf files in the directory, in JSONP format.
(Note: if you want to install this on your own computer,
<ul>
<li>if you click on the link,
the CGI script will be downloaded as <code>grammars.cgi.txt</code>,
but it should be called <code>grammars.cgi</code> and stored on the server
in the same directory as the grammar files.
<li>for CGI scripts to work with lighttpd, <code>"mod_cgi"</code> needs
to be included in the definition of <code>server.modules</code> in the
<code>lighttpd.conf</code> file.)
</ul>
<li>[Added 2010-02-16] There is a button for generating random sentences.
<li>[Added 2010-02-23] All translations are shown, not just the first one,
if there are multiple parses.
<li>[Added 2010-02-25] Next to each translation, there is now a little tree
icon that you can click on to see a drawing of an abstract syntax tree or a
parse tree. If you click on a drawing it collapses back into a tree icon.
<li>[Added 2010-04-09] Preparations to support different ways to access the
grammar: currently we access a PGF server via JSONP, but I would also like
to support AJAX, and local/downloaded JavaScript grammars.
<li>[Added 2010-04-19] A text entry field appears when you click in
the sentence area (with a dashed border). This allows you to enter words by
typing on the keyboard. As you start typing word magnets that don't match what
you are typing are removed. When only one magnet remains, you can press enter
to complete the word.
<li>[Added 2010-04-19] There is a menu for choosing the output language:
you can pick "All" to translate to all available languages, or pick one
particular language.
<li>[Added 2010-04-19] You can pass options to the function
<code>start_minibar</code> to customize the user interface. The default is
<code>{show_abstract:true,show_trees:true}</code> to show the abstract syntax
of parsed sentences, and to show icons that expand to syntax/parse trees next
each translation.
These features can be turned off by setting the fields to <code>false</code>.
<li>[Added 2010-04-30] The grammar menu is omitted if there is only one
grammar in the grammar list.
<li>[Added 2010-04-30] Fewer hardwired constants and new
<code>start_minibar</code> options (server, grammars_url, grammar_list,
show_grouped_translations, delete_button_text) to make
<code>minibar.js</code> more resuable.)
<li>[Added 2010-05-26] The magnets are now created with
<code>&lt;input type=button&gt;</code> tags to make them clickable in more
browsers.
<li>[Added 2010-05-26] The text entry field is now visible from the start,
and it is removed when no more words can be added to the sentence. When you
press enter, a word is added if there is only one magnet left,
<em>or</em> if what you have entered exactly matches one of the remaining
magnet.
<li>[Added 2010-05-28] Added a link to make it easy to try the same sentence in
<a href="http://translate.google.com">Google Translate</a>.This can be
turned off by passing the option <code>{try_google:false}</code> to
<code>start_minibar</code>.
<li>[Added 2010-06-02] Added support for Help and Feedback buttons, controlled
by the options <code>feedback_url</code> and <code>help_url</code> passed to
<code>start_minibar</code>.
<li>[Added 2010-06-02] New option: <code>default_source_language</code>.
<li>[Added 2010-09-10] Minibar now automatically uses
<a href="http://en.wikipedia.org/wiki/XMLHttpRequest">XHR</a>
instead of JSONP when possible (i.e. when the HTML document and the
PGF service are on the same server).
<li>[Added 2010-09-10] The default input language is now the user's preferred
language, if possible. This is implemented by consulting the
<code>userLanguage</code> field in the grammar info output by pgf-server.
<li>[Added 2010-10-27] Keyboard input and completion should now work much
more smoothly:
<ul>
<li>When you press space, the current word will be completed (if incomplete)
and a new magnet will be created. If there is more than one possible
completion, no magnet is created, but the common prefix of the possible
completions is added to the text box.
<li>Instead of asking the server for possible completions every time a new
letter is added to the curent word, minibar only ask for completions for
whole words and then filters the list locally when more letters are entered,
speeding things up when server responses are slow.
</ul>
<li>[Added 2010-10-27] Code restructuring:
<ul>
<li>The PGF server API has been moved to its own file:
<a href="../js/pgf_online.js">pgf_online.js</a>. This
allows it to be reused in other applicaitons without importing the entire
minibar. It also allows minibar to be used with different server
interfaces. <a href="minibar.html">minibar.html</a> has been updated to
show how you use the new <a href="minibar.js">minibar.js</a> and
<a href="../js/pgf_online.js">pgf_online.js</a>.
<li>The minibar code has been rewritten to avoid storing state information
in the document tree and accessing it by referring to named document
elements. The code now also avoids using string literals containing
the names of top-level functions to specify event handlers for buttons
and menus. (The code is no longer introspective, so &alpha; conversion
will not change its meaning.)
</ul>
<li>[Added 2010-11-09] Some new documentation:
<ul>
<li><a href="gf-web-api-examples.html">gf-web-api-examples.html</a>:
examples illustrating the PGF server API provided by
<a href="../js/pgf_online.js">pgf_online.js</a>.
<li><a href="example.html">example.html</a>: a minimal example of a web
page that uses <a href="../js/pgf_online.js">pgf_online.js</a> to talk to the
PGF server.
</ul>
<li>[Added 2011-03-03] Added a button to display word alignment.
<li>[Changed 2011-03-22] Don't force focus to the typed input field
after every word. On touch-based devices, the on-screen keyboard kept
popping up after every word, which was very annoying if you were
entering a sentence by tapping on the magnets.
<li>[Changed 2011-08-03] Moved the initialization code in minibar.html to
<a href="minibar_online.js">minibar_online.js</a>.
<li>[Changed 2011-08-08] For improved modularity and reusability,
two smaller objects have been factored out from the Minibar object:
Input and Translations. These have been placed in two separate files:
<a href="minibar_input.js">minibar_input.js</a> and
<a href="minibar_translations.js">minibar_translations.js</a>.
Some common auxiliary functions have also been moved to a separate file:
<a href="minibar_support.js">minibar_support.js</a>.
<li>[Added 2011-08-09] Added some <a href="minibar-api.html">Minibar API</a>
documentation.
<li>[Changed 2011-08-22] Quick fix to allow literals to be entered:
if you press Enter, the current word will be accepted, even if there are no
matching completions.
(You can now use names of people when constructing sentences in the Letter
grammar, for example.)
<li>[Added 2011-10-18] Added a button to display some grammar info and a
start category menu. The start category menu can be turned off by passing
the option <code>{startcat_menu:false}</code> when starting the minibar.
<li>[Added 2012-02-10] New minibar option <code>initial_grammar</code> to
control which of the available grammars is selected initially.
<li>[Added 2012-03-08] Added the option
<code>{tree_img_format: <var>fmt</var>}</code>
to make it easy to select <code>"gif"</code>, <code>"png"</code> or
<code>"svg"</code> output
for abstract synax trees, parse trees and word alignment diagrams.
<li>[Added 2012-03-19] Minibar now remembers the most recently used grammar
and automatically selects it the next time you return to the minibar.
(This is implemented using localStorage, i.e. the data is stored locally on
the user's device.)
<li>[Added 2012-03-26] Support for adding grammars from several servers to
the grammar menu. You can also add grammars from several directories on the
same server of course. The included minibar configuration file adds the
user's own grammars from the grammar editor.
<li id=wordforword>
[Added 2012-04-02] <span class=summary>Word-for-word replacements</span>:
when a complete and
unambiguous sentence has been entered, the words glow blue and when you
click on one, possible replacements appear. (Replacements are shown as names
of functions in the abstract syntax for now. They should be shown as words
in the concrete syntax instead.) When you click on a replacement word,
the sentence is reconstructed with the new word, adjusting other words
as needed for agreement. This functionality is activated by passing the
option <code>{word_replacements:true}</code> when starting the minibar.
<li>[Added 2012-04-03]
If you leave the minibar and later return, the previous input will be
restored. One input string per grammar is remembered, so you can also switch
back and forth between grammars without losing the input.
(This is implemented using localStorage, i.e. the data is stored locally on
the user's device.)
<li>[Added 2012-04-04] Grammar editor integration: as part of the
word-for-word replacement interface, minibar can now show a button for
extending the grammar with a new word. This is activated by passing the
option <code>{extend_grammar:<var>func</var>}</code> when starting the
minibar, where <var>func</var> is the editor function to call
(to be document further).
<li>[Added 2012-04-18] Added buttons to use generated translations as input.
(This functionality is available in the original Fridge Poetry, but has
been missing in Minibar, until now.)
<li>[Added 2012-11-23] Added support for switching back and forth between the
minibar and the new syntax tree editor. The default configuration in
<code>minibar_online.js</code> shows how to enable it.
<li>[Added 2012-11-29] After pressing the <strong>i</strong> button to view
grammar info, there is now a <strong>More info</strong> button to get
more detailed information about all categories and functions in the
grammar.
<li>[Added 2013-03-25] Initial support for selecting an arbitrary subset
of languages to appear in the translations and in word alignment
diagrams.
<li>[Added 2013-03-26] Parse tree visualizations: click once to show parse
trees without function names. Click again to add function names.
<li>[Added 2013-03-27] Click on a linearization to obtain a table with all
variants and forms. Click again to hide the table.
<li>[Added 2013-04-02] Abstract syntax tree visualizations: click once to
show abstract syntax trees without category names. Click again to add
category names.
<li>[Added 2013-04-02] Minibar now remembers from one visit to the next
the selected set of target languages for each grammar.
<li>[Added 2019-08-05] Minibar can now display grammar documentation.
The documentation is taken from a file called
<var>Grammar</var><code>.pgf_info</code>, located
next to the <var>Grammar</var><code>.pgf</code> file on the server.
The first line of the documentation is displayed below the menu bar in
the minibar. The rest of the documentation is displayed when you press
the <b>More info</b> button (or the <b>i</b> button). The documentation
can contain HTML markup. Blank lines are treated as paragraph breaks.
<li>[Added 2019-11-21] It is now possible to configure a list of preferred
grammars.
A preferred grammar is selected when a user visits Minibar for the
first time. (Like before, Minibar remembers the selected grammar for
future visits.)
<p>
A preferred list of grammars can be specified in <code>config.js</code>
in the <code>…/minibar</code> directory on the server, e.g. like this:
<pre> preferred_grammars=["/grammars/Foods.pgf","/grammars/ResourceDemo.pgf"]</pre>
The first available grammar from the list is used.
</ul>
</main>
<footer>
<hr>
<div class=modtime><small>
<!-- hhmts start -->Last modified: Thu Nov 21 14:23:59 CET 2019 <!-- hhmts end -->
</small></div>
<address>
<a href="http://www.cse.chalmers.se/~hallgren/">Thomas Hallgren</a>
</address>
</footer>
</body> </html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html> <head>
<title>PGF online server example</title>
<style type="text/css">
body { background: #ddd; }
h1, h2, h3, small, th { font-family: sans-serif; }
div.modtime { float: right; }
.modtime { color: #666; white-space: nowrap; }
</style>
<script type="text/JavaScript" src="../js/support.js"></script>
<script type="text/JavaScript" src="../js/pgf_online.js"></script>
<script type="text/JavaScript">
var server_options={
grammars_url: "http://www.grammaticalframework.org/grammars/",
grammar_list: ["Foods.pgf"]
}
var pgf_server = pgf_online(server_options);
function call_server() {
pgf_server.parse({from:"FoodsEng",input:document.forms[0].input.value},
show_output)
}
function show_output(parsed) {
document.getElementById("output").innerHTML=parsed[0].trees[0]
}
</script>
</head>
<body>
<h1>PGF online server example</h1>
<form onsubmit="call_server(); return false">
Input:
<input name=input size=50 value="this cheese is expensive">
<input type=submit value=Parse>
<p>
Output:
<span id=output></span>
</form>
<h2>Documentation</h2>
<ul>
<li><a href="gf-web-api-examples.html">GF Web API examples</a>
</ul>
<hr>
<div class=modtime><small>
<!-- hhmts start -->Last modified: Tue Nov 20 13:42:21 CET 2012 <!-- hhmts end -->
</small></div>
<address><a href="http://www.cse.chalmers.se/~hallgren/">TH</a></address>
</body> </html>

View File

@@ -0,0 +1,44 @@
#!/bin/bash
bin=bin
AUTOHEADER=no
. $bin/cgistart.sh
save_feedback() {
getquery
if [ -n "$feedback_path" ] &&
echo "t=$(date +%F+%T)&ip=$REMOTE_ADDR&$query&accept_language=$HTTP_ACCEPT_LANGUAGE&user_agent=$(echo -n $HTTP_USER_AGENT | plain2url)" >> "$feedback_path"
then
pagestart "Thank you!"
echo "Your feedback has been saved."
begin script type="text/javascript"
echo "setTimeout(function(){window.close()},4000);"
end
pageend
else
pagestart "Feedback error"
echo "Your feedback could not be saved. Sorry."
p
tag 'input type=button onclick="javascript:history.back()" value="&lt;- Go back"'
pageend
fi
}
view_feedback() {
charset="UTF-8"
pagestart "Collected Feedback"
begin pre class=feedbacklist
Reg show reverse drop color_depth,pixel_depth,outer_size,inner_size,available_screen_size urlmatch-v 'from=&input=&improvement=&comment=' urlmatch-v 'user_agent=*Googlebot*' from-url <"$PATH_TRANSLATED" | plain2html
end
pageend
}
case "$PATH_TRANSLATED" in
"") save_feedback ;;
*) view_feedback
esac

View File

@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html> <head>
<title>Feedback</title>
<link rel=stylesheet type="text/css" href="minibar.css">
<script type="text/JavaScript" src="../js/support.js"></script>
<script type="text/JavaScript" src="minibar_support.js"></script>
<script type="text/JavaScript" src="minibar.js"></script>
<meta name = "viewport" content = "width = device-width">
</head>
<body class=minibar onload="prefill_feedback_form()">
<h2><span id=grammar></span> Feedback</h2>
<form class=feedback name=feedback action="feedback.cgi" method="post">
<input type=hidden name="grammar">
<p>
<input type=hidden name="from"> <span class=field id=from>...</span> input:
<input type=hidden name="input"> <span class=field id=input>...</span>
<div id=translation_box>
<p><input type=hidden name="to"> <span class=field id="to">...</span> translation:
<input type=hidden name="translation"> <span class=field id=translation>...</span>
<p><label accesskey="S">Suggest a better translation:
<textarea rows=3 name="improvement"></textarea></label>
</div>
<p><label accesskey="C">Comments:
<br><textarea rows=5 name="comment"></textarea></label>
<p>
<input type=submit value="Submit Feedback">
<input type=button value="Cancel" onclick="window.close()">
<input type=hidden name="inner_size">
<input type=hidden name="outer_size">
<input type=hidden name="screen_size">
<input type=hidden name="available_screen_size">
<input type=hidden name="color_depth">
<input type=hidden name="pixel_depth">
</form>
</body>
</html>

View File

@@ -0,0 +1,259 @@
<!DOCTYPE html>
<html>
<head>
<title>GF web services API examples</title>
<meta charset="UTF-8">
<meta name = "viewport" content = "width = device-width">
<style type="text/css">
body { background: #eee; }
h1, h2, h3, small, th { font-family: sans-serif; }
th { text-align: left; }
h1,h2 { border-bottom: 2px solid black }
dt { background: #cef; }
code { background: #ffc; }
dt.js { background: white; margin-bottom: 1ex; }
dt.js em { color: #36f; }
dd { background: #ffc; margin-top: 1ex; margin-bottom: 1ex; }
dl.apiexamples>dt, dl.apiexamples>dd { font-family: monospace; }
dl.apiexamples>dd { white-space: pre; }
table.border { border-collapse: collapse; margin-top: 1ex; margin-bottom: 1ex; }
table.border td, table.border th { border: 1px solid black; background: #fcfcfc; }
div.modtime { float: right; }
.modtime { color: #666; white-space: nowrap; }
</style>
<body>
<h1>Minibar API</h1>
The Minibar web app consists of the following objects:
<ul>
<li><a href="#Minibar">Minibar</a>
<li><a href="#Input">Input</a>
<li><a href="#Translations">Translations</a>
</ul>
They are described below.
<h2 id=Minibar>The Minibar object</h2>
<p>
This object implements the complete Minibar web app. It is defined in
<a href="minibar.js">minibar.js</a>. It also uses the <code>Input</code>
and <code>Translations</code> objects described below, and some auxiliary
functions defined in <a href="minibar_support.js">minibar_support.js</a>
and <a href="../js/support.js">support.js</a>, so to use it in an
HTML file, you would normally include at least the following:
<blockquote><pre>
&lt;script type="text/JavaScript" src="minibar.js">&lt;/script>
&lt;script type="text/JavaScript" src="minibar_input.js">&lt;/script>
&lt;script type="text/JavaScript" src="minibar_translations.js">&lt;/script>
&lt;script type="text/JavaScript" src="minibar_support.js">&lt;/script>
&lt;script type="text/JavaScript" src="../js/support.js">&lt;/script>
</pre></blockquote>
<p>
For an example, see <a href="minibar.html">minibar.html</a>.
<h3>Constructor</h3>
<code>var minibar=new Minibar(server,options,target)</code>
<ul>
<li><code>server</code> is the PGF service object.
<li><code>options</code> is an object where the following properties
can be set to override various default options:
<table class=border>
<tr><th>Option<th>Default<th>Description
<tr><td>show_abstract<td>false<td rowspan=6>See Translations,
not used directly by Minibar
<tr><td>show_trees<td>false
<tr><td>tree_img_format<td>"png"
<tr><td>show_grouped_translations<td>true
<tr><td>show_brackets<td>false
<tr><td>translate_limit<td>25
<tr><td>delete_button_text<td>"⌫"<td rowspan=5>See Input,
not used directly by Minibar
<tr><td>default_source_language<td>null
<tr><td>startcat_menu<td>true
<tr><td>random_button<td>true
<tr><td>word_replacements<td>false
<tr><td>try_google<td>true<td>Include a button to try the current
sentence in Google Translate
<tr><td>feedback_url<td>null<td>Include a button to open a feedback
form. The HTTP server must be configured to handle form submissions
for this to work.
<tr><td>help_url<td>null<td>Include a button to open a help text.
</table>
<li><code>target</code> is the <code>id</code> of the HTML element inside
which the minibar user interface is created. It can be omitted if
the <code>id</code> is <code>minibar</code>. The HTML document should
contain something like this:
<blockquote><code>&lt;div id="minibar">&lt;/div></code></blockquote>
</ul>
<h3>Methods</h3>
There are several internal methods, but since this is a self-contained
web app, there is usually no need to call any methods from outside.
<h2 id=Input>The Input object</h2>
This object handles user input. Text can be entered by typing or by clicking
on the "refrigerator magnets".
<p>
It is defined in
<a href="minibar_input.js">minibar_input.js</a>.
It also uses some auxiliary functions defined
in <a href="minibar_support.js">minibar_support.js</a>
and <a href="../js/support.js">support.js</a>, so to use it in an
HTML file, you would normally include at least the following:
<blockquote><pre>
&lt;script type="text/JavaScript" src="minibar_input.js">&lt;/script>
&lt;script type="text/JavaScript" src="minibar_support.js">&lt;/script>
&lt;script type="text/JavaScript" src="../js/support.js">&lt;/script>
</pre></blockquote>
<h3>Constructor</h3>
<code>var input=new Input(server,translations,options)</code>
<ul>
<li><code>server</code> is the PGF service object
<li><code>options</code> is an object where the following properties
can be set to override various default options:
<table class=border>
<tr><th>Option<th>Default<th>Description
<tr><td>delete_button_text<td>"⌫"<td>The label for the button that deletes the last word.
<tr><td>default_source_language<td>null<td>The concrete language to
use for input in case the user's browers doesn't supply a suitable
default. If none is provided the first language in alphabetical
order will be used.
<tr><td>startcat_menu<td>true<td>Include a menu to choose which start
category to use for parsing (instead of the grammar's default start
category).
<tr><td>random_button<td>true<td>Include a button to generate a
random sentence.
<tr><td>word_replacements<td>false<td>Enable
<a href="about.html#wordforword">word-for-word replacements</a>.
(This is an experimental feature.)
</table>
<li><code>translations</code> is the object that is notified when the input
has changed. In the minibar, this is the object that display translations, but
other apps might of course use the entered text for other purposes.
The following methods will be called:
<ul>
<li><code>translations.clear()</code> is called when there is no entered
text.
<li><code>translations.translateFrom({from:<var>conc</var>,input:<var>string</var>})</code>
is called when the user has entered some text. The <code>from</code>
property is the name of the concrete syntax and the <code>input</code>
property is the entered text.
</ul>
</ul>
<h3>Properties and user interface</h3>
The <code>input</code> object created by the <code>Input</code> constructor
contains the following fields that the caller should add to the user interface:
<ul>
<li><code>input.main</code> is the main user interface where the current
input and the refrigerator magnets are displayed.
<li><code>input.menus</code> contains the menu for selecting input language.
<li><code>input.buttons</code> contains
buttons for deleting the last word, clearing the input and generating
a random sentence (if enabled in the options).
</ul>
<h3>Methods</h3>
<ul>
<li><code>input.change_grammar(grammar_info)</code> should be called
after a different grammar is selected in the <code>server</code> object. It
will clear away old input and magnets, and update the input language menu
with the languages available in the new grammar.
</ul>
<h2 id=Translations>The Translations object</h2>
This object displays translations. It is defined in
<a href="minibar_translations.js">minibar_translations.js</a>.
It also uses some auxiliary functions defined
in <a href="minibar_support.js">minibar_support.js</a>
and <a href="../js/support.js">support.js</a>, so to use it in an
HTML file, you would normally include at least the following:
<blockquote><pre>
&lt;script type="text/JavaScript" src="minibar_translations.js">&lt;/script>
&lt;script type="text/JavaScript" src="minibar_support.js">&lt;/script>
&lt;script type="text/JavaScript" src="../js/support.js">&lt;/script>
</pre></blockquote>
<h3>Constructor</h3>
<code>var translations=new Translations(server,options)</code>
<ul>
<li><code>server</code> is the PGF service object.
<li><p><code>options</code> is an object where the following properties
can be set to override various default options:
<table class=border>
<tr><th>Option<th>Default<th>Description
<tr><td>show_abstract<td>false<td>show the abstract syntax in addition
to the concrete syntax for the translations
<tr><td>show_trees<td>false<td>add buttons to display syntax trees
next to translations.
<tr><td>tree_img_format<td>"png"<td>format for trees & alignment images,
can be "gif", "png" or "svg"
<tr><td>show_grouped_translations<td>true<td>in case there are
multiple translations, group them by concrete language
<tr><td>show_brackets<td>false<td>show bracketed string
<tr><td>translate_limit<td>25<td>maximum number of parse trees to retrieve </table>
</ul>
<h3>Properties and user interface</h3>
The <code>translations</code> object created by the <code>Translations</code>
constructor contains the following fields that the caller should add to
the user interface:
<ul>
<li><code>translations.main</code> is the main user interface where the current
translations are displayed.
<li><code>translations.menus</code> contains the menu for selecting
a target language. The user can select <em>All</em> or one particular
language.
</ul>
<h3>Methods</h3>
<ul>
<li><code>translations.change_grammar(grammar_info)</code> should be called
after a different grammar is selected in the <code>server</code> object. It
will clear away old translations and update the target language menu
with the languages available in the new grammar.
<li><code>translations.clear()</code> clears the main output area from old translations.
<li><code>translations.translateFrom({from:<var>conc</var>,input:<var>string</var>})</code>
displays translations of the text given in the <code>input</code>
property (as an array of words)
from the concrete syntax indicated by the <code>from</code>.
</ul>
<hr>
<div class=modtime>
<small class=modtime>
HTML <!-- hhmts start -->Last modified: Tue Nov 27 15:03:15 CET 2012 <!-- hhmts end -->
</small>
</div>
<address>
<a href="Http://www.cse.chalmers.se/~hallgren/">TH</a>
</address>

View File

@@ -0,0 +1,93 @@
body.minibar {
background: #ccc url("brushed-metal.png");
}
h1, h2, h3, small, th { font-family: sans-serif; }
h1, h2, h3 { color: #303030; text-shadow: rgba(0,0,0,0.25) 3px 3px 5px; }
h1:first-child, h2:first-child { margin-top: 0; margin-bottom: 1ex; }
th, td { vertical-align: baseline; text-align: left; }
div.menubar { font-family: sans-serif; font-size: small; }
div.infobar { margin: 5px; }
div#surface {
min-height: 3ex;
margin: 5px;
padding: 5px;
border: 3px dashed #e0e0e0;
}
div#words {
min-height: 3ex;
margin: 5px;
padding: 6px;
border: 3px solid #e0e0e0;
}
div.word, span.word, div#words div, div#words input[type=button], div#surface input[type=button] {
display: inline-block;
font-family: sans-serif;
font-size: 100%;
background-color: white;
border: 1px solid black;
padding: 3px;
margin: 3px;
box-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
div#surface span.editable, div#surface input[type=button] {
color: blue;
border-color: blue;
box-shadow: 2px 2px 4px rgba(0,0,255,0.5);
cursor: pointer;
}
.invalid { color: red; }
div.modtime { float: right; }
.modtime { color: #666; white-space: nowrap; }
ul.space>li { margin-top: 0.75ex; }
div#saldospel input[type=button] { font-size: 100%; }
div#saldospel input.correct { color: green; }
div#saldospel input.incorrect { color: red; }
#surface input[type=text] { width: 5em; }
.feedback textarea { width: 95%; }
span.field { background-color: #eee; }
pre.feedbacklist { background: white }
img.button { padding: 1px; }
div.brackets {
margin: 5px;
}
span.brackets {
background: #ddd;
display: inline-block;
border-color: black;
border-style: solid;
border-width: 0 0 1px 0;
/*border: 1px solid black;*/
padding: 2px 3px;
}
div.brackets .token {
padding: 0 0.5ex;
}
div.browse, table.browse td {
background: white;
padding: 2px;
}
table.lintable { display: inline-table; border-collapse: collapse; }
table.lintable td { border: 1px solid black; padding: 2px; }

View File

@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<head>
<title>Minibar</title>
<link rel=stylesheet type="text/css" href="minibar.css">
<link rel=stylesheet type="text/css" href="../syntax-editor/editor.css">
<link rel=author href="http://www.cse.chalmers.se/~hallgren/" title="Thomas Hallgren">
<meta name = "viewport" content = "width = device-width">
<meta charset="UTF-8">
</head>
<body class=minibar>
<h2>Minibar online</h2>
<div id=minibar></div>
<div id=syntax_editor></div>
<noscript>This page doesn't works unless JavaScript is enabled.</noscript>
<hr>
<small>
[<a href="about.html">About Minibar</a><!--
| <a href="http://www.grammaticalframework.org:41296/fridge/">Original Fridge Poetry</a>
&amp; <a href="http://www.grammaticalframework.org:41296/translate/">Translator</a>-->]
</small>
<small class=modtime>
HTML <!-- hhmts start -->Last modified: Wed Oct 10 14:14:10 CEST 2018 <!-- hhmts end -->
</small>
<div id="debug" class="hidden"></div>
<script type="text/javascript" src="config.js"></script> <!-- optional -->
<script type="text/JavaScript" src="../js/grammar_manager.js"></script>
<script type="text/JavaScript" src="../js/support.js"></script>
<script type="text/JavaScript" src="../js/localstorage.js"></script>
<script type="text/JavaScript" src="../js/langcode.js"></script>
<script type="text/JavaScript" src="minibar.js"></script>
<script type="text/JavaScript" src="minibar_input.js"></script>
<script type="text/JavaScript" src="minibar_translations.js"></script>
<script type="text/JavaScript" src="minibar_support.js"></script>
<script type="text/JavaScript" src="../js/pgf_online.js"></script>
<script type="text/javascript" src="../syntax-editor/ast.js"></script>
<script type="text/javascript" src="../syntax-editor/editor_menu.js"></script>
<script type="text/javascript" src="../syntax-editor/editor.js"></script>
<script type="text/javascript" src="minibar_online.js"></script>
<script type="text/javascript" src="../gfse/gf_abs.js"></script>
</body>
</html>

View File

@@ -0,0 +1,377 @@
/* minibar.js
needs: minibar_support.js, minibar_input.js, minibar_translations.js, support.js
*/
/*
// This is essentially what happens when you call start_minibar:
if(server.grammar_list) grammars=server.grammar_list;
else grammars=server.get_grammarlist();
show_grammarlist(grammars)
select_grammar(grammars[0])
grammar_info=server.get_languages()
show_languages(grammar_info)
new_language()
complete_output=get_completions()
show_completions(complete_output)
*/
// For backward compatibility:
function start_minibar(server,opts,target) {
if(target) opts.target=target;
return new Minibar(server,opts);
}
/* --- Main Minibar object -------------------------------------------------- */
function Minibar(server,opts) {
// Contructor, typically called when the HTML document is loaded
/* --- Configuration ---------------------------------------------------- */
// default values for options:
this.options={
target: "minibar",
try_google: true,
feedback_url: null,
help_url: null,
initial_grammar: null
}
// Apply supplied options
this.server=server;
if(opts) for(var o in opts) this.options[o]=opts[o];
// LocalStorage
this.local=appLocalStorage("gf.minibar.")
/* --- Syntax editor integration ---------------------------------------- */
if(!this.options.abstract_action) this.integrate_syntax_editor()
/* --- Creating the components of the minibar --------------------------- */
this.translations=new Translations(server,this.options)
this.input=new Input(server,this.translations,this.options)
/* --- Creating user interface elements --------------------------------- */
this.menubar=div_class("menubar");
this.infobar=div_class("infobar");
this.extra=div_id("extra");
this.minibar=element(this.options.target);
this.minibar.innerHTML="";
with(this) {
appendChildren(menubar,[input.menus,translations.menus,input.buttons])
appendChildren(minibar,[menubar,infobar,input.main,translations.main,extra]);
if(options.help_url)
menubar.appendChild(button("Help",bind(open_help,this)));
append_extra_buttons(extra,options);
}
this.set_hidden = function(b) {
this.hidden=b
this.minibar.style.display= b ? "none" : ""
}
this.hide = function() { this.set_hidden(true); }
this.show = function() { this.set_hidden(false); }
/* --- Minibar client state initialisation ------------------------------ */
this.grammar=null;
/* --- Main program, this gets things going ----------------------------- */
with(this) {
server.get_grammarlists(bind(show_grammarlist,this));
}
}
Minibar.prototype.integrate_syntax_editor=function() {
var minibar=this
var editor_target="syntax_editor"
var e=element(editor_target)
if(!e || !window.Editor) return
e.style.display="none"
minibar.options.abstract_action=function(tree) {
function not_all(s) { return s!="All" }
var languages=filter(not_all,minibar.translations.toLangs)
var editor_options = {
target: editor_target,
show_grammar_menu: minibar.grammars.length>1,
show_startcat_menu: minibar.input.options.startcat_menu,
initial: { grammar: minibar.grammar_menu.value, // hmm
startcat: minibar.input.startcat_menu.value, // hmm
languages: languages,
abstr: tree
},
lin_action: function(new_input,langFrom) {
console.log(editor.menu.ui.grammar_menu.value)
var grammar_url=editor.menu.ui.grammar_menu.value // hmm
|| minibar.server.current_grammar_url // hmm
var startcat=editor.get_startcat()
|| minibar.input.startcat_menu.value // hmm
var toLangs=gm.languages // hmm
minibar.input.set_input_for(grammar_url,
{from:langFrom,
startcat:startcat,
input:gf_lex(new_input)})
minibar.translations.set_toLangs_for(grammar_url,toLangs)
//Better: keep editor around and reactivate it next time:
editor.hide()
// ...
//Easier: delete the editor and create a new one next time:
clear(editor.container)
editor=minibar.editor=null;
// Even if the grammar is the same as before, this call is
// what eventually triggers the new_input to be loaded:
minibar.select_grammar(grammar_url)
// Make the minibar visible again
minibar.show()
}
}
minibar.hide()
var gm = new GrammarManager(minibar.server,editor_options);
var editor=minibar.editor=new Editor(gm,editor_options)
editor.show()
}
}
Minibar.prototype.get_current_input=function(cont) {
var t=this
if(!t.hidden) cont(gf_unlex(t.input.current.input))
else {
var tree=t.editor.get_ast()
function pick(lins) { cont(lins[0].text) }
t.server.linearize({tree:tree,to:t.input.current.from},pick)
}
}
Minibar.prototype.show_grammarlist=function(dir,grammar_names,dir_count) {
var t=this;
var first_time= !t.grammar_menu
if(first_time) {
t.grammar_menu=empty_id("select","grammar_menu");
t.grammars=[];
t.grammar_dirs=[];
}
t.grammar_dirs.push(dir);
t.grammars=t.grammars.concat(grammar_names.map(function(g){return dir+g}))
function glabel(g) {
return hasPrefix(dir,"/tmp/gfse.") ? "gfse: "+g : g
}
function opt(g) { return option(glabel(g),dir+g); }
appendChildren(t.grammar_menu,map(opt,grammar_names));
function pick() {
var grammar_url=t.grammar_menu.value
t.local.put("last_grammar",grammar_url)
t.select_grammar(grammar_url);
}
function pick_first_grammar() {
if(t.timeout) clearTimeout(t.timeout),t.timeout=null;
if(t.grammar_menu.length>1 && !t.grammar_menu.parentElement) {
t.grammar_menu.onchange=pick;
insertFirst(t.menubar,title("Show grammar info",button("i",bind(t.show_grammarinfo,t))))
insertFirst(t.menubar,t.grammar_menu);
insertFirst(t.menubar,text("Grammar: "));
}
var grammar0=t.options.initial_grammar
if(!grammar0) {
var last_grammar=t.local.get("last_grammar");
if(last_grammar && elem(last_grammar,t.grammars))
grammar0=last_grammar;
}
var pgs=t.options.preferred_grammars
if(!grammar0 && pgs)
for(var i in pgs)
if(elem(pgs[i],t.grammars)) {
grammar0=pgs[i]
break
}
if(!grammar0) grammar0=t.grammars[0];
//console.log("grammar0=",grammar0)
t.grammar_menu.value=grammar0;
t.select_grammar(grammar0);
}
// Wait at most 1.5s before showing the grammar menu.
if(first_time) t.timeout=setTimeout(pick_first_grammar,1500);
if(t.grammar_dirs.length>=dir_count) pick_first_grammar();
}
Minibar.prototype.select_grammar=function(grammar_url) {
var t=this;
//debug("select_grammar ");
function change_grammar() {
t.server.grammar_info(bind(t.change_grammar,t));
}
t.server.switch_to_other_grammar(grammar_url,change_grammar);
}
Minibar.prototype.change_grammar=function(grammar_info) {
var t=this;
//debug("show_languages ");
t.grammar=grammar_info;
t.input.change_grammar(grammar_info)
t.translations.change_grammar(grammar_info)
t.get_pgf_info()
}
Minibar.prototype.get_pgf_info=function() {
var t=this;
var info_url=t.server.current_grammar_url+"_info";
clear(t.infobar)
t.pgf_info=null;
ajax_http_get(info_url,bind(t.show_pgf_info,t),function(){})
}
Minibar.prototype.show_pgf_info=function(info) {
var t=this;
var cnt=0;
//console.log(info)
info=info.split("\n");
for(var i=0;i<info.length;i++) {
if(info[i]=="") info[i]="<p>"
else cnt++
}
t.pgf_info=info.join("\n")
t.infobar.innerHTML=info[0]+" "
if(cnt>1)
t.infobar.appendChild(button("More info",bind(t.show_grammarinfo,t)))
}
Minibar.prototype.show_grammarinfo=function() {
var t=this
var g=this.grammar;
function draw_cats(cats) {
function draw_cat(cat) {
var i=cats[cat]
return tr([td(text(i.def)),
td(text(i.producers.join(", "))),
td(text(i.consumers.join(", ")))])
}
var table=wrap_class("table","browse",g.categories.map(draw_cat))
var hdr=tr([th(text("Category")),
th(text("Producers")),
th(text("Consumers"))])
insertFirst(table,hdr)
return table
}
function draw_funs(funs) {
function draw_fun(fun) {
var def=funs[fun].def.split(":")
return tr([td(text(def[0])),td(text(":")),td(text(def[1]||""))])
}
return div_class("browse",wrap("table",g.functions.map(draw_fun)))
}
function draw_more(info) {
replaceChildren(cats,draw_cats(info.cats))
replaceChildren(funs,draw_funs(info.funs))
btn.disabled=true;
}
function more() { t.server.browse({},draw_more) }
var cats=wrap("div",text(g.categories.join(", ")))
var funs=wrap("div",text(g.functions.join(", ")))
var btn=button("Show more details",more)
var info=empty("div")
if(t.pgf_info) info.innerHTML=t.pgf_info
if(true || /^\/(tmp|grammars)\//.test(server.grammars_url)) {
var q="?"+server.grammars_url+" "+server.current_grammar_url
var link=node("a",{href:q})
link.appendChild(text(link.href));
info.appendChild(wrap("p",[text("Link directly to this grammar: "),link]))
}
clear(t.translations.main)
var hdr=[text(g.name)]
if(g.lastmodified) {
hdr.push(text(" "))
hdr.push(wrap("small",text("("+g.lastmodified+")")))
}
appendChildren(this.translations.main,
[wrap("h3",hdr),
info,
text("The categories and functions in the grammar are listed below."),
btn,
wrap("h4",text("Start category")), text(g.startcat || ""),
wrap("h4",text("Categories")), cats,
wrap("h4",text("Functions")), funs])
}
Minibar.prototype.append_extra_buttons=function(extra,options) {
with(this) {
if(options.try_google)
extra.appendChild(button("Try Google Translate",bind(try_google,this)));
if(options.feedback_url) {
var b=button("Feedback",bind(open_feedback,this));
b.title="Click to suggest improvements. Select a language in the To: menu first to suggest a better translation to that language."
appendChildren(extra,[text(" "),b]);
}
}
}
Minibar.prototype.try_google=function() {
with(this) {
var to=translations.target_lang();
var s=gf_unlex(input.current.input);
if(input.surface.typed) s+=" "+input.surface.typed.value;
var url="http://translate.google.com/?sl="
+langpart(input.current.from,grammar.name);
if(to!="All") url+="&tl="+to;
url+="&q="+encodeURIComponent(s);
window.open(url);
}
}
Minibar.prototype.open_help=function() {
open_popup(this.options.help_url,"help");
}
Minibar.prototype.open_feedback=function() {
with(this) {
// make the minibar state easily accessible from the feedback page:
minibar.state={grammar:grammar,current:input.current,
to:translations.to_menu.value,
translations:translations.main};
open_popup(options.feedback_url,'feedback');
}
}
// This function is called from feedback.html
function prefill_feedback_form() {
var state=opener_element("minibar").state;
var trans=state.translations;
var gn=state.grammar.name
var to=langpart(state.to,gn);
var form=document.forms.namedItem("feedback");
setField(form,"grammar",gn);
setField(form,"from",langpart(state.current.from,gn));
setField(form,"input",gf_unlex(state.current.input));
setField(form,"to",to);
if(to=="All") element("translation_box").innerHTML=" To suggest a better translation to a particular language, select that language in the To: menu before pressing the Feedback button."
else setField(form,"translation",trans.single_translation.join(" / "));
// Browser info:
form["inner_size"].value=window.innerWidth+"×"+window.innerHeight;
form["outer_size"].value=window.outerWidth+"×"+window.outerHeight;
form["screen_size"].value=screen.width+"×"+screen.height;
form["available_screen_size"].value=screen.availWidth+"×"+screen.availHeight;
form["color_depth"].value=screen.colorDepth;
form["pixel_depth"].value=screen.pixelDepth;
window.focus();
}
/*
se.chalmers.cs.gf.gwt.TranslateApp/align-btn.png
GET /grammars/Foods.pgf?&command=abstrtree&tree=Pred+(This+Fish)+(Very+Fresh)
GET /grammars/Foods.pgf?&command=parsetree&tree=Pred+(This+Fish)+Expensive&from=FoodsAfr
GET /grammars/Foods.pgf?&command=alignment&tree=Pred+(This+Fish)+Expensive
*/

View File

@@ -0,0 +1,562 @@
/* --- Input object --------------------------------------------------------- */
function Input(server,translations,opts) { // Input object constructor
var t=this
t.server=server;
t.translations=translations;
// Default values for options:
t.options={
delete_button_text: "⌫",
default_source_language: null,
startcat_menu: true,
random_button: true,
word_replacements: false
}
// Apply supplied options
if(opts) for(var o in opts) t.options[o]=opts[o];
// User interface elements
t.main=empty("div");
t.menus=empty("span");
t.buttons=empty("span");
t.surface=div_id("surface");
t.words=div_id("words");
t.from_menu=empty("select");
t.startcat_menu=empty("select")
with(t) {
appendChildren(main,[surface,words]);
if(options.startcat_menu)
appendChildren(menus,[text(" Startcat: "),startcat_menu])
appendChildren(menus,[text(" From: "),from_menu])
appendChildren(buttons,
[title("Delete last word",button(options.delete_button_text,bind(delete_last,t),"H")),
button("Clear",bind(clear_all,t),"L")]);
if(options.random_button)
buttons.appendChild(button("Random",bind(generate_random,t),"R"));
var o=options;
if(o.initial_grammar && o.initial && o.initial.from && o.initial.input)
t.set_input_for(o.initial_grammar,o.initial)
}
/* --- Input client state initialization --- */
t.current={from: null, input: [] };
t.from_menu.onchange=bind(t.change_language,t);
t.startcat_menu.onchange=bind(t.change_startcat,t);
}
Input.prototype.change_grammar=function (grammar) {
var t=this;
grammar.browse={} // for caching output from the browse command
t.grammar=grammar;
t.local=mi_local(t.server.current_grammar_url)
update_language_menu(t.from_menu,grammar);
set_initial_language(t.options,t.from_menu,grammar,t.local.get("from"));
t.update_startcat_menu(grammar)
t.change_language();
}
Input.prototype.update_startcat_menu=function (grammar) {
var menu=this.startcat_menu;
menu.innerHTML="";
var cats=grammar.categories;
for(var cat in cats) menu.appendChild(option(cats[cat],cats[cat]))
var startcat=this.local.get("startcat") || grammar.startcat;
if(startcat) menu.value=startcat;
else {
insertFirst(menu,option("Default",""));
menu.value="";
}
}
Input.prototype.change_startcat=function () {
this.local.put("startcat",this.startcat_menu.value)
this.clear_all();
}
Input.prototype.change_language=function () {
this.current.from=this.from_menu.value;
this.local.put("from",this.current.from)
var old_current=this.local.get("current");
var new_input= old_current && old_current.from==this.current.from
? old_current.input : []
this.clear_all1();
this.add_words(new_input)
}
Input.prototype.set_input_for=function(grammar_url,initial) {
var local=mi_local(grammar_url)
local.put("from",initial.from)
local.put("current",{from:initial.from,input:initial.input})
if(initial.startcat) local.put("startcat",initial.startcat)
}
Input.prototype.clear_all2=function() {
with(this) {
current.input=[];
local.put("current",current)
remove_surface_words()
}
}
Input.prototype.clear_all1=function() {
with(this) {
remove_typed_input();
clear_all2();
translations.clear();
}
}
Input.prototype.clear_all=function() {
this.clear_all1();
this.get_completions();
}
Input.prototype.get_completions=function() {
with(this) {
//debug("get_completions ");
words.innerHTML="...";
var s=gf_unlex(current.input)+" ";
var args={from:current.from, input:s, cat:startcat_menu.value}
server.complete(args,bind(show_completions,this));
if(options.word_replacements) server.parse(args,bind(get_tree1,this));
// Making two server calls in parallel! The two continuations can
// be called in any order, make sure they are appropriately independent.
}
}
Input.prototype.show_completions=function(complete_output) {
var self=this;
function switch_input(lin) {
with(self) {
local.put("current",{from:lin.to,input:lin.text.split(" ")})
from_menu.value=lin.to;
change_language()
}
}
with(self) {
//debug("show_completions ");
var completions=complete_output[0].completions;
var emptycnt=add_completions(completions)
translations.translateFrom(current,startcat_menu.value,switch_input);
if(surface.typed && emptycnt==completions.length) {
if(surface.typed.value=="") remove_typed_input();
}
else add_typed_input();
}
}
Input.prototype.add_completions=function(completions) {
with(this) {
if(words.timeout) clearTimeout(words.timeout),words.timeout=null;
words.innerHTML="";
words.completions=completions;
words.word=[];
var t=surface.typed ? surface.typed.value : "";
var emptycnt=0;
for(var i=0;i<completions.length;i++) {
var s=completions[i];
if(s.length>0) {
var w=word(s);
words.appendChild(w);
words.word[i]=w;
}
else emptycnt++;
}
filter_completions(t,true);
return emptycnt;
}
}
Input.prototype.filter_completions=function(t,dim) {
with(this) {
if(words.timeout) clearTimeout(words.timeout),words.timeout=null;
words.filtered=t;
//if(dim) debug('filter "'+t+'"');
var w=words.word;
words.count=0;
var dimmed=0;
var prefix=""; // longest common prefix, for completion
for(var i=0;i<w.length;i++) {
var s=words.completions[i];
var keep=hasPrefix(s,t);
if(keep) {
if(words.count==0) prefix=s;
else prefix=(commonPrefix(prefix,s));
words.count++;
}
if(dim) {
w[i].style.opacity= keep ? "1" : "0.5";
if(keep) w[i].style.display="inline";
else dimmed++;
}
else
w[i].style.display=keep ? "inline" : "none";
}
words.theword=prefix;
if(dimmed>0)
words.timeout=setTimeout(function(){ filter_completions(t,false)},1000);
}
}
Input.prototype.add_typed_input=function() {
with(this) {
if(!surface.typed) {
var inp=empty("input","type","text");
inp.value="";
inp.setAttribute("accesskey","t");
inp.style.width="10em";
inp.onkeyup=bind(complete_typed,this);
surface.appendChild(inp);
surface.typed=inp;
inp.focus();
}
}
}
Input.prototype.remove_surface_words=function() {
with(this) {
var typed=surface.typed;
if(typed) while(typed.previousSibling)
surface.removeChild(typed.previousSibling)
else clear(surface)
}
}
Input.prototype.remove_typed_input=function() {
with(this) {
if(surface.typed) {
surface.typed.parentNode.removeChild(surface.typed);
surface.typed=null;
}
}
}
Input.prototype.complete_typed=function(event) {
with(this) {
//element("debug").innerHTML=show_props(event,"event");
var inp=surface.typed;
//debug('"'+inp.value+'"');
var s=inp.value;
var ws=s.split(" ");
if(ws.length>1 || event.keyCode==13) {
if(ws[0]!=words.filtered) filter_completions(ws[0],true);
if(words.count==1) add_word(words.theword);
else if(event.keyCode==13) add_word(ws[0]) // for literals
else if(elem(ws[0],words.completions)) add_word(ws[0]);
else if(words.theword.length>ws[0].length) inp.value=words.theword;
}
else if(s!=words.filtered) filter_completions(s,true)
}
}
Input.prototype.generate_random=function() {
var t=this;
function show_random(random) {
t.clear_all1();
t.add_words(gf_lex(random[0].text));
}
function lin_random(abs) {
t.server.linearize({tree:abs[0].tree,to:t.current.from},show_random);
}
t.server.get_random({cat:t.startcat_menu.value},lin_random);
}
Input.prototype.add_words=function(ws) {
this.add_words1(ws);
this.get_completions();
}
Input.prototype.add_words1=function(ws) {
for(var i=0;i<ws.length;i++)
if(ws[i]) this.add_word1(ws[i]);
this.local.put("current",this.current)
}
Input.prototype.word=function(s) {
var t=this;
function click_word() {
if(t.surface.typed) t.surface.typed.value="";
t.add_word(s);
}
return button(s,click_word);
}
Input.prototype.add_word=function(s) {
with(this) {
add_word1(s);
local.put("current",current)
if(surface.typed) {
var s2;
if(hasPrefix(s2=surface.typed.value,s)) {
s2=s2.substr(s.length);
while(s2.length>0 && s2[0]==" ") s2=s2.substr(1);
surface.typed.value=s2;
}
else surface.typed.value="";
}
get_completions();
}
}
Input.prototype.add_word1=function(s) {
with(this) {
current.input.push(s);
var w=span_class("word",text(s));
surface.insertBefore(w,surface.typed);
}
}
Input.prototype.delete_last=function() {
with(this) {
if(surface.typed && surface.typed.value!="")
surface.typed.value="";
else if(current.input.length>0) {
current.input.pop();
local.put("current",current)
if(surface.typed) {
surface.removeChild(surface.typed.previousSibling);
surface.typed.focus();
}
else surface.removeChild(surface.lastChild);
translations.clear();
get_completions();
}
}
}
/* --- Structural editing --------------------------------------------------- */
Input.prototype.get_tree1=function(parse) {
var t=this;
function proceed(lin) { t.get_tree2(lin,parse[0].trees[0]) }
if(parse.length==1 && parse[0].from==t.current.from
&& parse[0].trees && parse[0].trees.length==1)
t.server.linearize({to:t.current.from,tree:parse[0].trees[0]},proceed);
else t.end_structural_editing();
}
Input.prototype.get_tree2=function(lin,tree) {
var t=this;
with(t) {
if(lin.length==1 && lin[0].to==current.from
&& lin[0].text==gf_unlex(current.input)
&& (lin[0].brackets)) {
var bs=lin[0].brackets;
//var tree=show_abstract_tree(bs);
function proceed() { t.enable_structural_editing(bs,tree) }
server.linearize({to:current.from,tree:tree},
proceed,bind(end_structural_editing,t))
}
else end_structural_editing();
}
}
Input.prototype.end_structural_editing=function() {
var t=this;
if(t.surface.structural_editing_enabled) {
var ws=t.current.input;
t.clear_all2()
t.add_words1(ws);
t.surface.structural_editing_enabled=false;
}
}
Input.prototype.enable_structural_editing=function(bracketss,tree) {
var t=this;
with(t) {
var typed=surface.typed;
function add_bracket(brackets) {
function add_bs(b,parent) {
if(b.token) {
var fun=parent.fun,cat=parent.cat;
function showrepl() {
t.show_replacements(brackets,parent,tree)
}
if(fun && cat) {
var w= span_class("word editable",text(b.token));
w.onclick=showrepl
}
else
var w= span_class("word",text(b.token));
w.title=(fun||"_")+":"+(cat||"_")+" "+parent.fid+":"+parent.index
surface.insertBefore(w,typed);
}
else b.children.map(function(c){add_bs(c,b)})
}
add_bs(brackets,null)
}
remove_surface_words()
//add_bs(brackets);
if(Array.isArray(bracketss))
bracketss.map(add_bracket) // gf>3.5
else
add_bracket(bracketss) // gf<=3.5
t.surface.structural_editing_enabled=true;
}
}
Input.prototype.show_replacements=function(brackets,parent,tree) {
var fun=parent.fun,cat=parent.cat;
var t=this;
with(t) {
function browse1(fun_info) {
var fun_type = fun_info.def.split(":")[1];
function browse2(cat_info) {
var extb=null;
function examine_replacement(rfun) {
function browse3(rfun_info) {
var rfun_type=rfun_info.def.split(":")[1];
function replace() {
t.replace_word(brackets,parent,rfun,tree);
}
function show_replacement(lin) {
//console.log(lin)
t.words.insertBefore(button(lin[0].text || rfun,replace),extb);
}
if(rfun_type==fun_type) {
var tmpl=fun_template(rfun,rfun_type)
if(tmpl)
t.server.linearize({to:t.current.from,cat:cat,tree:tmpl},show_replacement)
else
t.words.insertBefore(button(rfun,replace),extb)
}
}
t.browse(rfun,browse3)
}
var ps=cat_info.producers;
clear(t.words);
var extf=t.options.extend_grammar;
if(extf) {
function update() {
console.log("update minibar")
t.grammar.browse={}; // clear cache
t.show_replacements(brackets,parent,tree)
}
extb=button("New "+cat+"...",
function() { extf(cat,fun_type,update)})
t.words.appendChild(extb)
}
if(ps)
for(var pi in ps)
if(ps[pi]!=fun) examine_replacement(ps[pi])
}
t.browse(cat,browse2)
}
t.browse(fun,browse1)
}
}
Input.prototype.replace_word=function(brackets,parent,fun,tree) {
var t=this;
function proceed(tree) {
//parent.fun=fun;
//var tree=show_abstract_tree(brackets);
tree=modify_tree(tree,parent.fid,fun)
tree=show_tree(tree) // Convert JSON repr of tree back to string
function replace(lin_output) {
if(lin_output.length==1 && lin_output[0].to==t.current.from) {
t.clear_all1();
t.add_words(gf_lex(lin_output[0].text))
}
}
function err(text,status,ctype) {
t.words.innerHTML=
ctype.split(";")[0]=="text/html"
? text
: "Word replacement failed"
}
t.server.linearize({to:t.current.from,tree:tree},replace,err)
}
// Convert the string representaiton of the abstract syntax tree to JSON:
t.server.pgf_call("abstrjson",{tree:tree},proceed)
}
Input.prototype.browse=function(id,cont) {
var t=this;
if(t.grammar.browse[id]) cont(t.grammar.browse[id])
else {
function browsed(info) {
t.grammar.browse[id]=info;
cont(info);
}
t.server.browse({id:id},browsed);
}
}
/* --- Auxiliary functions -------------------------------------------------- */
function mi_local(grammar_url) {
return appLocalStorage("gf.minibar_input."+grammar_url+".")
}
function set_initial_language(options,menu,grammar,old_from) {
var user_from=old_from || grammar.userLanguage;
if(user_from) menu.value=user_from; // !! What if the language was removed?
else if(options.default_source_language) {
for(var i=0;i<menu.options.length;i++) {
var o=menu.options[i].value;
var l=langpart(o,grammar.name);
if(l==options.default_source_language) menu.value=o;
}
}
}
/*
function show_abstract_tree(b) { return show_tree(abstract_tree(b)) }
function abstract_tree(b) {
return { fun:b.fun,children:abstract_trees(b.children) }
}
function abstract_trees(bs) {
var as=[];
for(var i in bs)
if(bs[i].fun) as.push(abstract_tree(bs[i]))
return as
}
*/
function show_tree(t) {
return t.children
? t.fun+" "+t.children.map(show_tree_atomic).join(" ")
: t.fun;
}
function show_tree_atomic(t) {
var s=show_tree(t);
return t.children && t.children.length>0 ? "("+s+")" : s
}
// Find the node labelled fid in tree and replace the function there with fun
function modify_tree(tree,fid,fun) {
if(tree.fun) {
if(tree.fid==fid) tree.fun=fun
else if(tree.children)
tree.children.map(function(t) { modify_tree(t,fid,fun) })
}
return tree;
}
function fun_template(fname,ftype) {
if(window.parse_fun) {
var fun=parse_fun(fname+" : "+ftype).ok
if(fun) {
var t=fname;
for(var i=1;i<fun.type.length;i++) t+=" ?"
return t;
}
}
return null;
}

View File

@@ -0,0 +1,67 @@
<!DOCTYPE html>
<html manifest="minibar_offline.manifest">
<head>
<title>Minibar Offline</title>
<link rel=stylesheet type="text/css" href="http://www.grammaticalframework.org/src/www/minibar/minibar.css">
<meta name = "viewport" content = "width = device-width">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta charset="UTF-8">
</head>
<body class=minibar>
<h2>Minibar Offline, technology preview</h2>
<div id=beskrivning_intro>
<p>
<a href="javascript:dolj('intro')">[Hide introduction]</a>
<br>
<strong>These examples work flawlessly in the <a href="http://www.opera.com/browser/">Opera</a> web browser.</strong>
They also work to some extent in Opera Mobile 11 and WebKit browsers
(Safari and Chrome) and recent versions of Gecko browers (Firefox).
A hardcoded
<a href="http://tests.novemberborn.net/javascript/callstack-size.html">call stack size limit</a> still causes some problems.
<p>
How it works: a JavaScript translation of the Haskell PGF run-time library
is downloaded and run in the browser. The binary PGF files are downloaded
from the server, parsed and used in the normal way by functions in the PGF
library. The brower is
<a href="http://diveintohtml5.info/offline.html">directed to keep a
local copy of everything</a>, so that you can have access to the minibar
even when you are offline.
</div>
<p id=beskriv_intro style="display: none"><a href="javascript:beskriv('intro')">[Show introduction]</a>
<div id=minibar>
</div>
<hr>
<small>
[<a href="http://www.altocumulus.org/~hallgren/hs2js/tests/">About hs2js</a>
| <a href="minibar.html">Minibar online</a>]
</small>
<small class=modtime>
HTML <!-- hhmts start -->Last modified: Tue Nov 20 13:45:07 CET 2012 <!-- hhmts end -->
</small>
<address><a href="http://www.cse.chalmers.se/~hallgren/">TH</a></address>
<pre id=nodebug>
</pre>
<script type="text/javascript" src="http://www.grammaticalframework.org/~hallgren/hs2js/test/debug.js"></script>
<script type="text/javascript" src="http://www.grammaticalframework.org/~hallgren/hs2js/test/json.js"></script>
<script type="text/javascript" src="http://www.grammaticalframework.org/~hallgren/hs2js/test/builtin.js"></script>
<script type="text/JavaScript" src="http://www.grammaticalframework.org/~hallgren/hs2js/test/Services.js"></script>
<script type="text/javascript" src="http://www.grammaticalframework.org/~hallgren/hs2js/test/visadolj.js"></script>
<script type="text/JavaScript" src="../js/support.js"></script>
<script type="text/JavaScript" src="minibar.js"></script>
<script type="text/JavaScript" src="minibar_input.js"></script>
<script type="text/JavaScript" src="minibar_translations.js"></script>
<script type="text/JavaScript" src="minibar_support.js"></script>
<script type="text/JavaScript" src="pgf_offline.js"></script>
<script type="text/javascript" src="minibar_offline.js"></script>
</body>
</html>

View File

@@ -0,0 +1,18 @@
var offline_options = {
grammars_url: "/~hallgren/hs2js/test/",
grammar_list: ["Foods.pgf","Smart.pgf","Phrasebook.pgf"]
}
var server=pgf_offline(offline_options);
var minibar_options= {
show_abstract: true,
show_trees: false,
show_grouped_translations: false,
default_source_language: "Eng",
try_google: true,
random_button: false
}
var minibar1=new Minibar(server,minibar_options);

View File

@@ -0,0 +1,4 @@
CACHE MANIFEST
# 12
NETWORK:
*

View File

@@ -0,0 +1,40 @@
// minibar_online.js, assumes that minibar.js and pgf_online.js have been loaded.
var online_options={
//grammars_url: "http://www.grammaticalframework.org/grammars/",
//grammars_url: "http://tournesol.cs.chalmers.se:41296/grammars/",
//grammars_url: "http://localhost:41296/grammars/",
//grammar_list: ["Foods.pgf"], // leave undefined to get list from server
}
if(window.grammar_list) online_options.grammar_list=grammar_list
var minibar_options= {
show_abstract: true,
show_trees: true,
// tree_img_format: "png", // or "svg"
show_grouped_translations: false,
show_brackets: true,
word_replacements: true,
default_source_language: "Eng",
//feedback_url: "feedback.html",
try_google: true
}
if(window.preferred_grammars)
minibar_options.preferred_grammars=preferred_grammars
if(/^\?\/(tmp|grammars)\//.test(location.search)) {
var args=decodeURIComponent(location.search.substr(1)).split(" ")
if(args[0]) online_options.grammars_url=args[0];
if(args[1]) minibar_options.initial_grammar=args[1];
}
else if(supports_html5_storage()) {
var s=window.localStorage["gf.editor.simple.grammardir"]
if(s) var editor_dir=JSON.parse(s);
}
var server=pgf_online(online_options);
if(editor_dir) server.add_grammars_url(editor_dir+"/");
var minibar=new Minibar(server,minibar_options);

View File

@@ -0,0 +1,117 @@
/* --- Auxiliary functions -------------------------------------------------- */
function langpart(conc,abs) { // langpart("FoodsEng","Foods") == "Eng"
return hasPrefix(conc,abs) ? conc.substr(abs.length) : conc;
}
// Lookup language codes (from "flags language = ..." in the source grammar)
function langCode(grammar,conc) {
if(!grammar.langCode) {
var ls=grammar.languages
var langCode={}
for(var i=0;i<ls.length;i++)
if(ls[i].languageCode)
langCode[ls[i].name]=ls[i].languageCode
grammar.langCode=langCode
}
return grammar.langCode[conc]
}
// Words are separated by spaces (for now). GF has other lexers/unlexers.
function gf_lex(s) { return s.split(" "); }
function gf_unlex(ws) { return ws.join(" "); }
function update_language_menu(menu,grammar) {
// Replace the options in the menu with the languages in the grammar
var lang=grammar.languages;
menu.innerHTML="";
for(var i=0; i<lang.length; i++) {
var ln=lang[i].name;
if(!hasPrefix(ln,"Disamb")) {
var lp=langpart(ln,grammar.name);
menu.appendChild(option(lp,ln));
}
}
}
function button_img(url,action) {
var i=node("img",{"class":"button","src":url});
i.onclick=action;
return i;
}
function cycle_images(img_urls) {
var current=0;
function cycle() {
current++;
if(current>=img_urls.length) current=0;
i.src=img_urls[current]
}
var i=button_img(img_urls[0],cycle);
return i
}
function toggle_img(i) {
var tmp=i.src;
i.src=i.other;
i.other=tmp;
}
function setField(form,name,value) {
form[name].value=value;
var el=element(name);
if(el) el.innerHTML=value;
}
function open_popup(url,target) {
var w=window.open(url,target,'toolbar=no,location=no,status=no,menubar=no');
w.focus();
}
function opener_element(id) { with(window.opener) return element(id); }
function supportsSVG() {
return document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")
}
function speech_buttons(to3,to2,txt) {
var voices = window.speechSynthesis && window.speechSynthesis.getVoices() || []
var dvs = voices.filter(function(v){return v.default})
function pick2dash(v) { return hasPrefix(v.lang,to2dash) }
if(to2)
var pick=function (v) { return v.lang==to2 }
else if(to3.length==3) {
var to2dash=alangcode(to3)+"-"
var pick=pick2dash
}
else {
// Maybe the name of the concrete syntax is the name of the language
// like in Numerals.pgf
var lang=to3.substr(0,1).toUpperCase()+to3.substr(1).toLowerCase()
var codes=langcode[lang]
var to2dash=(codes ? codes.code2 : to3)+"-"
var pick=pick2dash
}
function btn(v) {
// Remove spaces more fluent Thai:
var txt2 = v.lang=="th-TH" ? txt.split(" ").join("") : txt
var u=new SpeechSynthesisUtterance(txt2)
u.lang=v.lang // how to use v.voiceURI or v.name?
function speak() {
speechSynthesis.cancel()
speechSynthesis.speak(u)
}
return button(v.lang,speak)
}
//console.log(voices.length,"voices")
var vs=dvs.filter(pick)
if(vs.length==0) vs=voices.filter(pick)
//console.log(vs.length,"voices for "+to3+" "+to2)
var btns=vs.map(btn)
//console.log(btns.length,"voice buttons")
return wrap("span",btns)
}

View File

@@ -0,0 +1,332 @@
/* --- Translations object -------------------------------------------------- */
var tree_icon="../minibar/tree-btn.png";
var alignment_icon="../minibar/align-btn.png";
function Translations(server,opts) {
this.server=server;
// Default values for options:
this.options={
show_abstract: false,
abstract_action: null, // action when selecting the abstracy syntax tree
show_trees: false, // add buttons to show abstract syntax trees,
// parse trees & word alignment
tree_img_format: supportsSVG() ? "svg" : "png",
// format for trees & alignment images,
// can be "gif", "png" or "svg"
show_grouped_translations: true,
to_multiple: true, // allow selection of multiple target languages
show_brackets: false, // show bracketed string
speech: true, // enable speech synthesis buttons
translate_limit: 25 // maximum number of parse trees to retrieve
}
// Apply supplied options
if(opts) for(var o in opts) this.options[o]=opts[o];
this.main=empty("div");
this.menus=empty("span");
var tom_opts={id:"to_menu"}
if(this.options.to_multiple)
tom_opts.multiple=true,tom_opts.size=8,
tom_opts.style="position: absolute";
var tom=this.to_menu=node("select",tom_opts,[]);
if(this.options.to_multiple) {
tom.style.display="none"
function toggle_tom() {
tom.style.display= tom.style.display=="none" ? "" : "none"
}
appendChildren(this.menus,[button("To:",toggle_tom),tom,text(" ... ")])
}
else appendChildren(this.menus,[text(" To: "),tom])
tom.onchange=bind(this.change_language,this);
var o=this.options
if(o.initial_grammar && o.initial_toLangs)
this.set_toLangs_for(o.initial_grammar,o.initial_toLangs)
/* // This seems triggers weird scrolling behavior in Firefox and Chrome:
tom.onmouseover=function() { var n=tom.options.length;
tom.size=n<12 ? n : 12; }
tom.onmouseout=function() { var n=tom.options.length;
tom.size=n<4 ? n : 4; }
*/
}
Translations.prototype.change_grammar=function(grammar) {
var t=this
t.grammar=grammar;
t.local=mt_local(t.server.current_grammar_url)
update_language_menu(t.to_menu,grammar);
insertFirst(t.to_menu,option("All","All"));
t.to_menu.value="All";
var toLangs=t.local.get("toLangs")
if(toLangs && toLangs.length>0) {
t.toLangs=toLangs
t.toSet=toSet(toLangs)
updateMultiMenu(t.to_menu,toLangs)
}
else {
t.toLangs=["All"]
t.toSet={"All":true}
}
}
Translations.prototype.clear=function() {
this.main.innerHTML="";
}
Translations.prototype.set_toLangs_for=function(grammar_url,toLangs) {
var local=mt_local(grammar_url)
local.put("toLangs",toLangs)
}
Translations.prototype.change_language=function() {
var t=this
t.toLangs=multiMenuSelections(t.to_menu)
t.toSet=toSet(t.toLangs)
t.local.put("toLangs",t.toLangs)
t.get_translations();
}
Translations.prototype.translateFrom=function(current,startcat,lin_action) {
this.current=current;
this.startcat=startcat;
this.lin_action=lin_action;
this.get_translations();
}
Translations.prototype.get_translations=function() {
with(this) {
var c=current;
var args={from:c.from,input:gf_unlex(c.input),cat:startcat}
if(options.translate_limit) args.limit=options.translate_limit
if(options.show_grouped_translations)
server.translategroup(args,bind(show_groupedtranslations,this));
else
server.translate(args,bind(show_translations,this));
}
}
Translations.prototype.target_lang=function() {
with(this) return langpart(to_menu.value,grammar.name);
}
Translations.prototype.show_translations=function(translationResults) {
var self=this;
function abs_tdt(tree) {
var as = self.options.show_trees
? [self.abstree_button(tree),
self.alignment_button(tree,to=="All",self.toLangs),
text(" ")]
: []
as.push(text(tree))
return td(as)
}
function text_speech(langcode,to,txt,lin) {
var langcode2=langCode(self.grammar,to)
return wrap("span",[txt,speech_buttons(langcode,langcode2,lin)])
}
function lin_tdt(tree,to,langcode,lin) {
var txt=wrap("span",text("▸ "+lin))
var ts = text_speech(langcode,to,txt,lin)
var as = wrap("span",
self.options.show_trees
? [self.parsetree_button(tree,to,self.grammar),text(" "),ts]
: [ts])
as.active=txt
as.swap=ts
return as
}
function act(lin) {
return self.lin_action ? function() { self.lin_action(lin) } : null
}
function atext(s,act) {
var txt=wrap("span",text(s))
txt.onclick=act
return txt
}
function show_lin(langcode,lin,tree) {
function draw_table(lintable,action) {
function draw_texts(texts) {
function text1(s) {
var txt=atext(s,action)
return wrap("div",text_speech(langcode,lin.to,txt,s))
}
return texts.map(text1)
}
function draw_row(row) {
return tr([td(atext(row.params,action)),td(draw_texts(row.texts))])
} // ▼ ▾
return wrap("span",
[atext("▾ ",action),
wrap_class("table","lintable",lintable.map(draw_row))])
}
function get_tabular() {
function show_table(lins) {
if(lins.length==1) {
function restore() { replaceNode(one.swap,all) }
var all=draw_table(lins[0].table,restore)
replaceNode(all,one.swap)
one.active.onclick=function() { replaceNode(all,one.swap) }
}
}
self.server.pgf_call("linearizeTable",{"tree":tree,"to":lin.to},
show_table)
}
var one= lin_tdt(tree,lin.to,langcode,lin.text) // ▶
one.active.onclick=get_tabular
return td(one)
}
with(self) {
var trans=main;
//var to=target_lang(); // wrong
var to=to_menu.value;
var toLangs=self.toLangs
var toSet=self.toSet
var cnt=translationResults.length; // cnt==1 usually
//trans.translations=translations;
trans.single_translation=[];
trans.innerHTML="";
/*
trans.appendChild(wrap("h3",text(cnt<1 ? "No translations?" :
cnt>1 ? ""+cnt+" translations:":
"One translation:")));
*/
for(var p=0;p<cnt;p++) {
var tra=translationResults[p];
var bra=tra.brackets;
if (tra.translations != null) {
for (q = 0; q < tra.translations.length; q++) {
var t = tra.translations[q];
var lin=t.linearizations;
var tbody=empty("tbody");
if(options.show_abstract && t.tree) {
function abs_act() {
self.options.abstract_action(t.tree)
}
var abs_hdr = options.abstract_action
? title("Edit the syntax tree",
button("Abstract",abs_act))
: text("Abstract: ")
tbody.appendChild(tr([th(abs_hdr),abs_tdt(t.tree)]));
}
for(var i=0;i<lin.length;i++) {
if(lin[i].to==to && toLangs.length==1)
trans.single_translation.push(lin[i].text);
if(lin[i].to==current.from && lin[i].brackets)
bra=lin[i].brackets;
if(to=="All" || toSet[lin[i].to]) {
var langcode=langpart(lin[i].to,grammar.name)
//var hdr=text(langcode+": ")
var hdr=title("Switch input language to "+langcode,
button(langcode,act(lin[i])))
//hdr.disabled=lin[i].to==current.from
tbody.appendChild(
tr([th(hdr),show_lin(langcode,lin[i],t.tree)]));
}
}
trans.appendChild(wrap("table",tbody));
}
}
else if(tra.typeErrors) {
var errs=tra.typeErrors;
for(var i=0;i<errs.length;i++)
trans.appendChild(wrap("pre",text(errs[i].msg)))
}
if(options.show_brackets)
trans.appendChild(div_class("brackets",draw_bracketss(bra)));
}
}
}
Translations.prototype.show_groupedtranslations=function(translationsResult) {
with(this) {
var trans=main;
var to=target_lang();
//var to=to_menu.value // wrong
var cnt=translationsResult.length;
//trans.translations=translationsResult;
trans.single_translation=[];
trans.innerHTML="";
for(var p=0;p<cnt;p++) {
var t=translationsResult[p];
if(to=="All" || t.to==to) {
var lin=t.linearizations;
var tbody=empty("tbody");
if(to=="All") tbody.appendChild(tr([th(text(t.to+":"))]));
for(var i=0;i<lin.length;i++) {
if(to!="All") trans.single_translation[i]=lin[i].text;
tbody.appendChild(tr([td(text(lin[i].text))]));
if (lin.length > 1) tbody.appendChild(tr([td(text(lin[i].tree))]));
}
trans.appendChild(wrap("table",tbody));
}
}
}
}
Translations.prototype.abstree_button=function(abs) {
var f=this.options.tree_img_format;
var img=this.server.current_grammar_url+"?command=abstrtree&format="+f+"&tree="+encodeURIComponent(abs)
var btn=tree_button(img,"&nocat=true");
btn.title="Click to display abstract syntax tree"
return btn
}
Translations.prototype.alignment_button=function(abs,all,toLangs) {
var f=this.options.tree_img_format;
var i=button_img(alignment_icon,function(){toggle_img(i)});
var to= all ? "" : "&to="+encodeURIComponent(toLangs.join(" "))
i.title="Click to display word alignment"
i.other=this.server.current_grammar_url+"?command=alignment&format="+f+"&tree="+encodeURIComponent(abs)+to;
return i;
}
Translations.prototype.parsetree_button=function(abs,lang,grammar) {
var f=this.options.tree_img_format;
var img=this.server.current_grammar_url
+"?command=parsetree&format="+f+"&nodep=true&nodefont=arial"
+"&from="+lang+"&tree="+encodeURIComponent(abs);
var img_nofun=img+"&nofun=true"
var help="Click again to display parse tree. Click again to show function names."
if(f=="svg" && grammar.hasDependencyLabels) {
var depimg=this.server.current_grammar_url
+"?command=deptree&format=svg&to="+lang
+"&tree="+encodeURIComponent(abs);
var imgs=[tree_icon,depimg,img_nofun,img]
help="Click to display dependency tree. "+help
}
else var imgs=[tree_icon,img_nofun,img]
var btn=cycle_images(imgs)
btn.title=help
return btn;
}
/* --- Auxiliary functions -------------------------------------------------- */
function mt_local(grammar_url) {
return appLocalStorage("gf.minibar_translations."+grammar_url+".")
}
function tree_button(img_url,opt) {
return cycle_images([tree_icon,img_url+(opt||"&nofun=true"),img_url])
}
function draw_brackets(b) {
return b.token
? span_class("token",text(b.token))
: node("span",{"class":"brackets",
title:(b.fun||"_")+":"+b.cat+" "+b.fid+":"+b.index},
b.children.map(draw_brackets))
}
function draw_bracketss(bs) {
return Array.isArray(bs)
? bs.map(draw_brackets) //with gf>3.5, in some cases
: draw_brackets(bs) // with gf<=3.5
}

View File

@@ -0,0 +1,117 @@
// Assumes that Services.js has been loaded
function pgf_offline(options) {
var server = {
// State variables (private):
grammars_url: "",
other_grammars_urls: [],
grammar_list: ["Foods.pgf"],
current_grammar_url: null,
pgf : null,
// Methods:
switch_grammar: function(grammar_url,cont) {
var new_grammar_url=this.grammars_url+grammar_url;
this.switch_to_other_grammar(new_grammar_url,cont)
},
add_grammars_url: function(grammars_url,cont) {
this.other_grammars_urls.push(grammars_url);
if(cont) cont();
},
switch_to_other_grammar: function(new_grammar_url,cont) {
//debug("switch_grammar ");
var self=this;
var update_pgf=function(pgfbinary) {
debug("Got "+new_grammar_url+", length="
+pgfbinary.length+", parsing... ");
self.pgf = {v: Services_decodePGF.v({v:pgfbinary}) }
//debug("done")
self.current_grammar_url=new_grammar_url;
if(cont) cont();
}
ajax_http_get_binary(new_grammar_url+"?command=download",update_pgf);
},
get_grammarlist: function(cont,err) {
if(this.grammar_list) cont(this.grammar_list)
else http_get_json(this.grammars_url+"grammars.cgi",cont,err);
},
get_grammarlists: function(cont,err) { // May call cont several times!
var ds=this.other_grammars_urls;
var n=1+ds.length;
function pair(dir) {
return function(grammar_list){cont(dir,grammar_list,n)}
}
function ignore_error(err) { console.log(err) }
this.get_grammarlist(pair(this.grammars_url),err)
for(var i in ds)
http_get_json(ds[i]+"grammars.cgi",pair(ds[i]),ignore_error);
},
get_languages: function(cont) {
cont(fromJSValue(Services_grammar.v(this.pgf)))
},
grammar_info: function(cont) {
cont(fromJSValue(Services_grammar.v(this.pgf)))
},
get_random: function(cont) {
alert("Random generation not supported yet in the offline version");
},
linearize: function(args,cont) {
cont(fromJSValue(Services_linearize.v(this.pgf)(v(args.tree))(v(args.to))));
},
complete: function(args,cont) {
cont(fromJSValue(Services_complete.v(this.pgf)(v(args.from))(v(args.input))));
},
parse: function(args,cont) {
cont(fromJSValue(Services_parse.v(this.pgf)(v(args.from))(v(args.input))));
},
translate: function(args,cont) {
cont(fromJSValue(Services_translate.v(this.pgf)(v(args.from))(v(args.input))));
},
translategroup: function(args,cont) {
cont(fromJSValue(Services_translategroup.v(this.pgf)(v(args.from))(v(args.input))));
}
};
for(var o in options) server[o]=options[o];
return server;
};
// See https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest
function ajax_http_get_binary(url,callback) {
var http=GetXmlHttpObject()
if (http==null) {
alert ("Browser does not support HTTP Request")
return
}
var statechange=function() {
if (http.readyState==4 || http.readyState=="complete") {
if(http.status==200) {
var buffer=http.mozResponseArrayBuffer;
if(buffer) callback(bufferToString(buffer)) // Gecko 2 (Firefox 4)
else callback(http.responseText); // other browsers
}
else alert("Request for "+url+" failed: "
+http.status+" "+http.statusText);
}
}
http.onreadystatechange=statechange;
http.open("GET",url,true);
http.overrideMimeType('text/plain; charset=x-user-defined');
http.send(null);
//dump("http get "+url+"\n")
return http;
}
function bufferToString(buffer) {
// This function converts to the current representation of ByteString,
// but it would be better to use binary buffers for ByteStrings as well.
debug("bufferToString");
var u=new Uint8Array(buffer);
var a=new Array(u.length);
for(var i=0;i<u.length;i++)
a[i]=String.fromCharCode(u[i]);
return a.join("");
}

View File

@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html>
<head>
<title>Phrasebook</title>
<link rel=stylesheet type="text/css" href="minibar.css">
<meta charset="UTF-8">
<meta name = "viewport" content = "width = device-width">
</head>
<body class=minibar>
<div id=minibar></div>
<hr>
<small>
Powered by <a href="http://www.grammaticalframework.org/">GF</a>,
see <a href="http://www.grammaticalframework.org/examples/phrasebook/doc-phrasebook.html">doc</a>.
</small>
<script type="text/JavaScript" src="../js/support.js"></script>
<script type="text/JavaScript" src="../js/localstorage.js"></script>
<script type="text/JavaScript" src="minibar.js"></script>
<script type="text/JavaScript" src="minibar_input.js"></script>
<script type="text/JavaScript" src="minibar_translations.js"></script>
<script type="text/JavaScript" src="minibar_support.js"></script>
<script type="text/JavaScript" src="../js/pgf_online.js"></script>
<script type="text/JavaScript">
var online_options={
// grammars_url: "http://www.grammaticalframework.org/grammars/",
//grammars_url: "http://tournesol.cs.chalmers.se:41296/grammars",
//grammars_url: "http://localhost:41296/grammars/",
grammar_list: ["Phrasebook.pgf"] // leave undefined to get list from server
}
var server=pgf_online(online_options);
var phrasebook_options={
delete_button_text: "Del",
help_url: "http://www.grammaticalframework.org/examples/phrasebook/help-phrasebook.html",
feedback_url: "feedback.html",
default_source_language: "Eng",
startcat_menu: false,
to_multiple: false
}
start_minibar(server,phrasebook_options)
</script>
</body>
</html>

View File

@@ -0,0 +1,30 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html> <head>
<title>Saldotest</title>
<link rel=stylesheet type="text/css" href="minibar.css">
<script type="text/JavaScript" src="../js/support.js"></script>
<script type="text/JavaScript" src="saldotest.js"></script>
<meta name = "viewport" content = "width = device-width">
</head>
<body onload="start_saldotest();start_saldospel()">
<h2>Vilket ord ska bort?</h2>
<div id=saldospel>
</div>
<h2>Hel- och halvspöke</h2>
<div id=saldotest>
</div>
<hr>
<small>
[Baserad på <a href="http://spraakbanken.gu.se/sal/ws/">SALDOs nättjänster</a>]
</small>
<small class=modtime>
HTML <!-- hhmts start -->Last modified: Tue Nov 20 13:47:13 CET 2012 <!-- hhmts end -->
</small>
<address>TH <img src="http://www.altocumulus.org/~hallgren/online.cgi?icon" alt=""></address>
</body>
</html>

View File

@@ -0,0 +1,340 @@
var Saldo_ws_url = "http://spraakbanken.gu.se/ws/saldo-ws/";
//var Saldo_ff_url = Saldo_ws_url+"ff/json+remember_completions/";
var Saldo_lid_url = Saldo_ws_url+"lid/json";
function saldo_ws(fn,fmt,arg,cont_name) {
jsonp(Saldo_ws_url+fn+"/"+fmt+(cont_name ? "+"+cont_name : "")+"/"+arg,"");
}
function saldo_json(fn,arg,cont_name) { saldo_ws(fn,"json",arg,cont_name); }
function saldo_lid(arg,cont_name) { saldo_json("lid",arg,cont_name); }
function saldo_lid_rnd(cont_name) { saldo_lid("rnd?"+Math.random(),cont_name); }
var ordlista=[];
var current="";
function start_saldotest() {
appendChildren(element("saldotest"),
[button("Slumpa","random_word()"),
button("Rensa","clear_all()"),
button("⌫","delete_last()"),
//button("Ordlista","show_ordlista()"),
button("Visa tänkbara drag","show_moves()"),
button("Gör ett drag","make_a_move()"),
//button("Visa prefix","show_prefixes()"),
div_id("surface"),
div_id("words"),
div_id("translations")])
var style0="min-height: 3ex; margin: 5px; padding: 5px;";
element("surface").setAttribute("style",style0+"border: 3px dashed #e0e0e0;");
element("words").setAttribute("style",style0+"border: 3px solid #e0e0e0;");
clear_all();
}
function random_word() {
saldo_lid_rnd("show_random");
}
function show_random(lid) {
var lex=lid.lex;
reset_all(lex.substring(0,lex.indexOf('.')));
}
function clear_all() { reset_all(""); }
function reset_all(s) {
current=s;
element("surface").innerHTML=s;
element("translations").innerHTML="";
get_completions();
}
function delete_last() {
var len=current.length;
if(len>0) {
current=current.substring(0,len-1);
var s=element("surface");
s.innerHTML=current;
element("translations").innerHTML="";
get_completions();
}
}
function with_completions(s,cont) {
var c=ordlista[s];
if(c && c.a) cont(c);
else {
//if(c) alert("c already has fields"+field_names(c));
ordlista[s]={put: function(c) { ordlista[s]=c; cont(c); }};
var url=Saldo_ws_url+"ff/json+ordlista[\""+s+"\"].put/"+encodeURIComponent(s);
jsonp(url,"");
}
}
function get_completions() {
with_completions(current,show_completions);
}
function word(s) {
//var w=span_class("word",text(s));
//if(s==" ") w.innerHTML="&nbsp;";
//w.setAttribute("onclick",'extend_current("'+s+'")');
//return w;
return button(s,'extend_current("'+s+'")');
}
function extend_current(s) {
current+=s;
element("words").innerHTML="";
element("surface").innerHTML=current;
get_completions();
}
function show_completions(saldo_ff) {
var box=element("words");
box.innerHTML="";
//var c=saldo_ff.c.split("");
var c=filter(allowed,saldo_ff.c);
sort(c);
for(var i=0;i<c.length;i++) {
var s=c[i];
if(s!='-')
box.appendChild(word(s));
}
show_translations(saldo_ff.a);
}
function allowed(c) {
switch(c) {
case 'å':
case 'ä':
case 'ö':
case 'é':
case 'ü':
return true;
default:
return 'a'<=c && c<='z';
}
}
// ordklasser: mxc sxc (förekommer bara som prefix),
// *h (förekommer bara som suffix)
function ignore(msd) {
switch(msd) {
case "c":
case "ci":
case "cm":
case "seg":
case "sms":
return true;
default:
return false;
}
}
function count_wordforms(a) {
var cnt=0;
for(var i=0;i<a.length;i++)
if(!ignore(a[i].msd)) cnt++;
return cnt;
}
function pad(s) {
return s.length>0 ? " "+s : "";
}
function show_translations(a) {
var tr=element("translations");
tr.innerHTML="";
//if(!a) alert("a undefined in show_translations");
if(count_wordforms(a)<1) {
tr.appendChild(p(text(a.length<1 ? "Detta är inte en giltig ordform"
: "Denna form förekommer bara i sammansättningar")));
element("surface").setAttribute("class","invalid");
}
else {
element("surface").setAttribute("class","valid");
for(var i=0;i<a.length;i++)
if(!ignore(a[i].msd))
tr.appendChild(p(text(a[i].gf+" ("+a[i].pos+pad(a[i].is)+", "+a[i].msd+")")));
}
}
function show_ordlista() {
var trans=element("translations");
trans.innerHTML="Följande ord har slagits upp: ";
var apnd=function(el) { trans.appendChild(el) };
for(var i in ordlista) {
apnd(empty("br"));
apnd(span_class(ordlista[i].a.length<1 ? "invalid" : "valid",text(" "+i)));
apnd(text(": "+(ordlista[i].ok!=null ? ordlista[i].ok.length : "?")
+"/"+(ordlista[i].allowed!=null ? ordlista[i].allowed.length : "?")));
}
}
function extend_ordlista(s,cs,cont) {
if(cs.length<1) cont();
else {
var c=cs[0];
var cs2=cs.substring(1);
with_completions(s+c,function(o){extend_ordlista(s,cs2,cont)});
}
}
function known_possible_moves(s,cont) {
var c=implode(sort(filter(allowed,ordlista[s].c)));
ordlista[s].allowed=c;
extend_ordlista(s,c,function() {
var ok="";
for(var i=0;i<c.length;i++) {
var next=s+c[i];
var ff=ordlista[next];
//if(!ff.a) alert(show_props(ff,"ff"));
if(next.length<2 || count_wordforms(ff.a)<1) ok+=c[i];
}
ordlista[s].ok=ok;
cont(ok);
}
);
}
function unknown_possible_moves(s,cont) {
with_completions(s,function(c){known_possible_moves(s,cont);});
}
function currently_possible_moves(cont) {
known_possible_moves(current,cont);
}
function show_moves() {
var trans=element("translations");
trans.innerHTML="Letar efter möjliga drag";
currently_possible_moves(function(ok) {
trans.innerHTML="Tänkbara drag: "+ok;
winning_moves(trans,ok);
});
}
function winning_moves(trans,ok) {
var ws=map(function(c){return current+c;},ok);
mapc(unknown_possible_moves,ws,function(oks){
var winning="";
for(i=0;i<oks.length;i++)
if(oks[i].length<1) winning+=ok[i];
trans.innerHTML+="<br>Vinnande drag: "+winning;
});
}
function make_a_move() {
currently_possible_moves(function(ok) {
if(ok.length<1) element("translations").innerHTML="Hittade inga möjliga drag!";
else {
var i=Math.floor(Math.random()*ok.length);
extend_current(ok[i]);
}
}
);
}
function show_prefixes_of(trans,s) {
if(s.length>0) {
var p=s.substr(0,s.length-1);
with_completions(p,function(c) {
if(count_wordforms(c.a)>0) trans.innerHTML+="<br>"+p;
show_prefixes_of(trans,p);
});
}
}
function show_prefixes() {
var trans=element("translations");
trans.innerHTML="Prefix av "+current+":";
show_prefixes_of(trans,current);
}
/* -------------------------------------------------------------------------- */
var spel={ antal_ord: 4, // antal närbesläktade ord att visa
antal_korrekta_svar: 0,
antal_felaktiga_svar: 0
};
function start_saldospel() {
spel.hylla=div_id("hylla");
spel.status=div_id("status");
//element("saldospel").innerHTML="<span id=score></span>";
appendChildren(element("saldospel"),
[spel.hylla,spel.status,
p(text("")),
button("Nya ord","spel0()"),
text(" "),
wrap("b",span_id("score"))]);
spel.score=element("score");
show_score();
spel0();
}
function spel0() { // Välj ord 1
saldo_lid_rnd("spel1");
}
function spel1(lid) { // Slå upp md1 för ord 1
spel.lid=lid;
saldo_json("md1",lid.lex,"spel2");
}
function spel2(md1) { // Kontrollera att det finns minst 4 ord i md1 för ord1
if(md1.length<spel.antal_ord) spel0();
else {
spel.md1=md1;
spel3();
}
}
function spel3() { // Välj ord 2
saldo_lid_rnd("spel4");
}
function spel4(lid) { // Slå upp md1 för ord 2
spel.lid2=lid;
saldo_json("md1",lid.lex,"spel5");
}
function spel5(md1) { // Kontrollera att ord 1 och ord 2 inte har något gemensamt
var ordlista1=map(wf,spel.md1);
var ord2=wf(spel.lid2.lex);
var ordlista2=map(wf,md1).concat(ord2);
if(overlaps(ordlista1,ordlista2)) spel3();
else spel6(ordlista1,ord2);
}
function spel6(ordlista1,ord2) {
spel.ord2=ord2;
var pos=Math.floor(Math.random()*spel.antal_ord);
var ordlista=shuffle(shuffle(ordlista1).slice(0,spel.antal_ord).concat(ord2));
spel.hylla.innerHTML="";
var lista=empty_class("div","space");
for(var i=0;i<ordlista.length;i++)
lista.appendChild((button(ordlista[i],"spel7(this)")));
spel.hylla.appendChild(lista);
}
function spel7(btn) {
btn.disabled=true;
var ok=btn.value==spel.ord2;
//btn.setAttribute("class",ok ? "correct" : "incorrect");
btn.setAttribute("style",ok ? "color: green" : "color: red");
if(ok) spel.antal_korrekta_svar++; else spel.antal_felaktiga_svar++;
show_score();
if(ok) spel0();
}
function show_score() {
spel.score.innerHTML=""+spel.antal_korrekta_svar+" rätt, "
+spel.antal_felaktiga_svar+" fel";
}
function wf(ord) { // word form, wf("band..1") == "band"
return ord.split(".",1)[0].split("_").join(" ");
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

View File

@@ -0,0 +1,4 @@
User-agent: *
Disallow: /grammars
Disallow: /robust
Disallow: /*.pgf

View File

@@ -0,0 +1,2 @@
html:
pandoc --from=markdown+pipe_tables --to=html --output=about.html README.md

View File

@@ -0,0 +1,76 @@
<!DOCTYPE html>
<html>
<head>
<title>About the syntax editor</title>
<link rel=stylesheet type="text/css" href="../minibar/minibar.css">
<link rel=stylesheet type="text/css" href="editor.css">
<meta charset="UTF-8">
</head>
<body class="about">
# GF web-based syntax editor
John J. Camilleri
January 2013
A tool for building and manipulating abstract syntax trees in GF.
This is meant as improved replacement of the [old syntax editor][old].
[old]:http://www.grammaticalframework.org/~meza/restWiki/editor.html
## Example usage
If you want to use the tool in your own application, everything you need in the source
files `editor.html` and `editor_online.js`. Contact the [GF developer mailing list][gf-dev]
if you have any problems.
[gf-dev]:http://groups.google.com/group/gf-dev
## Available startup options
### Grammar Manager
| Options | Description | Default |
|---------------------+-------------------------------------------------------------------------+-------------------|
| `initial.grammar` | Initial grammar URL, e.g. `"http://localhost:41296/grammars/Foods.pgf"` | |
| `initial.startcat` | Initial startcat | (grammar default) |
| `initial.languages` | Initial linearisation languages, e.g. `["Eng","Swe","Mlt"]` | (all) |
### Editor
| Options | Description | Default |
|----------------------+---------------------------------------------------------------------------------+----------|
| `target` | | "editor" |
| `initial.abstr` | Initial abstract tree (as string), e.g. `"Pred (That Fish) Expensive"` | |
| `lin_action` | Function called when clicking on the language button beside each linearisation. | |
| `lin_action_tooltip` | Tooltip for the button beside each linearisation. | |
| `show_grammar_menu` | Show grammar menu? | True |
| `show_startcat_menu` | Show startcat menu? | True |
| `show_to_menu` | Show languages menu? | True |
| `show_random_button` | Show random button? | True |
| `show_import` | Show import button/panel? | True |
| `show_export` | Show export button? | True |
## Notes
- Tested with latest Chrome and Firefox (only).
## To do/feature requests
- Compatibility with grammars with dependent category types
- Clicking on tokens to select tree node
- Clipboard of trees
- Usage of printnames
- Enter string/float/int literals
- more prominence to Disamb-linearizations
- show all resulting linearization variants
- undo/redo (or back/forward) navigation
- structure fridge magnets more (eg newline before the magnet whose first letter is different)
## Known bugs
- Change startcat doesn't work when given an initial startcat
</body>
</html>

View File

@@ -0,0 +1,129 @@
<!DOCTYPE html>
<html>
<head>
<title>
About the syntax editor
</title>
<link rel=stylesheet type="text/css" href="../minibar/minibar.css"> <link rel=stylesheet type="text/css" href="editor.css"> <meta charset="UTF-8">
</head>
<body class="about">
<h1 id="gf-web-based-syntax-editor">GF web-based syntax editor</h1>
<p>John J. Camilleri<br />January 2013</p>
<p>A tool for building and manipulating abstract syntax trees in GF. This is meant as improved replacement of the <a href="http://www.grammaticalframework.org/~meza/restWiki/editor.html">old syntax editor</a>.</p>
<h2 id="example-usage">Example usage</h2>
<p>If you want to use the tool in your own application, everything you need in the source files <code>editor.html</code> and <code>editor_online.js</code>. Contact the <a href="http://groups.google.com/group/gf-dev">GF developer mailing list</a> if you have any problems.</p>
<h2 id="available-startup-options">Available startup options</h2>
<h3 id="grammar-manager">Grammar Manager</h3>
<table>
<thead>
<tr class="header">
<th align="left">Options</th>
<th align="left">Description</th>
<th align="left">Default</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>initial.grammar</code></td>
<td align="left">Initial grammar URL, e.g. <code>&quot;http://localhost:41296/grammars/Foods.pgf&quot;</code></td>
<td align="left"></td>
</tr>
<tr class="even">
<td align="left"><code>initial.startcat</code></td>
<td align="left">Initial startcat</td>
<td align="left">(grammar default)</td>
</tr>
<tr class="odd">
<td align="left"><code>initial.languages</code></td>
<td align="left">Initial linearisation languages, e.g. <code>[&quot;Eng&quot;,&quot;Swe&quot;,&quot;Mlt&quot;]</code></td>
<td align="left">(all)</td>
</tr>
</tbody>
</table>
<h3 id="editor">Editor</h3>
<table>
<thead>
<tr class="header">
<th align="left">Options</th>
<th align="left">Description</th>
<th align="left">Default</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>target</code></td>
<td align="left"></td>
<td align="left">&quot;editor&quot;</td>
</tr>
<tr class="even">
<td align="left"><code>initial.abstr</code></td>
<td align="left">Initial abstract tree (as string), e.g. <code>&quot;Pred (That Fish) Expensive&quot;</code></td>
<td align="left"></td>
</tr>
<tr class="odd">
<td align="left"><code>lin_action</code></td>
<td align="left">Function called when clicking on the language button beside each linearisation.</td>
<td align="left"></td>
</tr>
<tr class="even">
<td align="left"><code>lin_action_tooltip</code></td>
<td align="left">Tooltip for the button beside each linearisation.</td>
<td align="left"></td>
</tr>
<tr class="odd">
<td align="left"><code>show_grammar_menu</code></td>
<td align="left">Show grammar menu?</td>
<td align="left">True</td>
</tr>
<tr class="even">
<td align="left"><code>show_startcat_menu</code></td>
<td align="left">Show startcat menu?</td>
<td align="left">True</td>
</tr>
<tr class="odd">
<td align="left"><code>show_to_menu</code></td>
<td align="left">Show languages menu?</td>
<td align="left">True</td>
</tr>
<tr class="even">
<td align="left"><code>show_random_button</code></td>
<td align="left">Show random button?</td>
<td align="left">True</td>
</tr>
<tr class="odd">
<td align="left"><code>show_import</code></td>
<td align="left">Show import button/panel?</td>
<td align="left">True</td>
</tr>
<tr class="even">
<td align="left"><code>show_export</code></td>
<td align="left">Show export button?</td>
<td align="left">True</td>
</tr>
</tbody>
</table>
<h2 id="notes">Notes</h2>
<ul>
<li>Tested with latest Chrome and Firefox (only).</li>
</ul>
<h2 id="to-dofeature-requests">To do/feature requests</h2>
<ul>
<li>Compatibility with grammars with dependent category types</li>
<li>Clicking on tokens to select tree node</li>
<li>Clipboard of trees</li>
<li>Usage of printnames</li>
<li>Enter string/float/int literals</li>
<li>more prominence to Disamb-linearizations</li>
<li>show all resulting linearization variants</li>
<li>undo/redo (or back/forward) navigation</li>
<li>structure fridge magnets more (eg newline before the magnet whose first letter is different)</li>
</ul>
<h2 id="known-bugs">Known bugs</h2>
<ul>
<li>Change startcat doesn't work when given an initial startcat</li>
</ul>
</body>
</html>

View File

@@ -0,0 +1,366 @@
/* --- ID for a node in a tree ---------------------------------------------- */
function NodeID(x) {
this.id = new Array();
this.id.push(0);
// Initialize from input
if (x) {
var type = Object.prototype.toString.call(x);
switch (type) {
case "[object Number]": this.id = [x]; break;
case "[object String]": this.id = map(function(s){return parseInt(s)}, x.split(",")); break;
case "[object Array]" : this.id = Array.clone(x); break;
case "[object Object]": this.id = Array.clone(x.get()); break; // another NodeID
}
}
// get id
this.get = function() {
return this.id;
}
// Add child node to id
this.add = function(x) {
this.id.push(parseInt(x));
return this.id;
}
// compare with other id
this.equals = function(other) {
return JSON.stringify(this.id)==JSON.stringify(other.id);
}
// clone
this.clone = function() {
return new NodeID( this );
}
// Return NodeID as string
this.toString = function() {
return this.id.toString();
}
}
/* --- Abstract Syntax Tree (with state)------------------------------------- */
function ASTNode(data) {
for (var d in data) this[d]=data[d];
this.children = [];
if (data) for (var c in data.children) {
this.children.push( new ASTNode(data.children[c]) );
}
this.hasChildren = function(){
return this.children.length > 0;
}
// generic HOF for traversing tree
this.traverse = function(f) {
function visit(node) {
f(node);
for (var i in node.children) {
visit(node.children[i]);
}
}
visit(this);
}
}
function AST(fun, cat) {
// local helper function for building ASTNodes
var newNode = function(fun, cat) {
return new ASTNode({
"fun": fun,
"cat": cat,
"string": "", // for String literals
"deps": {}, // dependent types
"children": []
});
}
this.root = newNode(fun, cat);
this.currentID = new NodeID(); // current id in tree
this.currentNode = this.root; // current node in tree
this.getCurrentNode = function() {
return this.currentNode;
}
this.getCurrentID = function() {
return this.currentID;
}
this.setCurrentID = function(id) {
this.currentID = id; // new new NodeID(id);
this.currentNode = this.find(this.currentID);
}
this.hasParent = function() {
return this.currentID.get().length > 1;
// return !this.atRoot();
}
this.atRoot = function() {
return this.currentNode == this.root;
// return !this.hasParent();
}
this.getRoot = function() {
return this.root;
}
this.getFun = function() {
return this.currentNode.fun;
}
this.setFun = function(f) {
this.currentNode.fun = f;
}
this.getCat = function() {
return this.currentNode.cat;
}
this.setCat = function(c) {
this.currentNode.cat = c;
}
// Add a single type dependency at current node
this.addDep = function(k, type) {
// Add unassigned type variable to current
this.currentNode.deps[k] = null;
// Add actual type dep node
var node = newNode(k, type);
node.depid = k; // links to dep in parent
this._add(this.currentID, node);
return node;
}
// Add a node as child of current node
this.add = function(fun, cat) {
var node = newNode(fun,cat);
this._add(this.currentID, node);
return node;
}
// add node as child of id
this._add = function(id, node) {
var x = this.find(id);
x.children.push(node);
}
// Wrap the current node inside another node
// Doesn't check whether child_ix is within in range
this.wrap = function(typeobj, child_ix) {
var subtree = new ASTNode(this.currentNode);
this.currentNode.fun = typeobj.name;
this.currentNode.cat = typeobj.ret;
this.currentNode.children = [];
for (var i in typeobj.args) {
this.add(null, typeobj.args[i]);
}
this.currentNode.children[child_ix] = subtree;
return subtree;
}
// Wrap the current node inside another node
// Doesn't check whether child_ix is within in range
this.unwrap = function() {
var parent_id = this.currentID.clone();
parent_id.get().pop();
if (parent_id.get().length==1) {
this.root = this.currentNode;
this.currentID = new NodeID();
} else {
var gparent_id = parent_id.clone();
gparent_id.get().pop();
var gparent = this.find(gparent_id);
child_ix = parent_id.clone().get().pop();
gparent.children[child_ix] = this.currentNode;
this.currentID = parent_id;
}
}
// Determine if current node is writable (empty/no children)
this.is_writable=function() {
var cn = this.currentNode;
var blank = cn.fun == null || cn.children.length == 0;
return blank;
}
// Determine if a fun would fit in a current hole
this.fits_in_place=function(typeobj) {
var cn = this.currentNode;
var inplace = false;
if (typeobj.args.length == cn.children.length) {
var matches = 0;
for (var i in typeobj.args) {
if (typeobj.args[i] == cn.children[i].cat)
matches++;
}
inplace = matches == cn.children.length;
}
return inplace;
}
// Set entire subtree at current node
this.setSubtree = function(node) {
this._setSubtree(this.currentID, node);
}
// set tree at given id
this._setSubtree = function(id, subtree) {
var lid = Array.clone(id.get()); // clone NodeID array
var node = this.root;
if (lid.length==1) {
// Insert at root
this.currentNode = this.root = new ASTNode(subtree);
}
else {
lid.shift(); // throw away root
while (lid.length>1 && node.hasChildren()) {
node = node.children[lid.shift()];
}
this.currentNode = node.children[lid.shift()] = new ASTNode(subtree);
}
}
// Find a node in the tree from its ID
this.find = function(id) {
var lid = undefined
if (Object.prototype.toString.call(id) == "[object Object]") {
lid = Array.clone( id.get() );
} else {
alert("non-NodeID passed to AST.find()");
}
var node = this.root;
if (lid[0] == 0) lid.shift();
while (lid.length>0 && node.children.length>0) {
node = node.children[lid.shift()];
}
if (lid.length>0)
return undefined;
return node;
}
// Clear children of current node
this.removeChildren = function() {
this.currentNode.children = [];
}
// Move current ID to next hole
this.toNextHole = function() {
var id = new NodeID(this.currentID);
// loop until we're at top
while (id.get().length > 0) {
var node = this.find(id);
// first check children
for (var i in node.children) {
var child = node.children[i];
if (!child.fun) {
var newid = new NodeID(id);
newid.add(i);
this.setCurrentID(newid);
return;
}
}
// otherwise go up to parent
id.get().pop();
}
}
// Return parent of current node
this.getParent = function() {
var parent_id = this.currentID.clone();
parent_id.get().pop();
return this.find(parent_id);
}
// Move current id to child number i
this.toChild = function(i) {
if (i < this.currentNode.children.length) {
this.currentID.add(i);
this.currentNode = this.currentNode.children[i];
}
}
// generic HOF for traversing tree
// this.traverse = function(f) {
// this.root.traverse(f);
// }
this.traverse = function(f) {
function visit(id, node) {
f(node);
for (var i in node.children) {
var newid = new NodeID(id);
newid.add(parseInt(i));
visit(newid, node.children[i]);
}
}
visit(new NodeID(), this.root);
}
// Return tree as string
this.toString = function() {
var s = "";
function visit(node) {
if (node.cat == "String")
s += '"' + ((node.string) ? node.string : "") + '"';
else
s += node.fun ? node.fun : "?" ;
if (!node.hasChildren())
// if (node.children.length == 0)
return;
for (var i in node.children) {
s += " (";
visit(node.children[i]);
s += ")";
}
}
visit(this.root);
return s;
}
}
// Parse type signature into a JSON object
// (This probably needs a better home)
AST.parse_type_signature = function(str) {
var obj = {
signature: str,
type: undefined,
name: undefined,
deps: [],
args: [],
ret: undefined
};
var ix = str.indexOf(":");
// judgement type
var bits = str.substr(0, ix).trim().split(" ");
obj.type = bits[0];
// name (possibly with constructors)
obj.name = bits.slice(1).join(" ");
// function args (possibly with type dependency)
var regex_dep = new RegExp(/\(\s*(.+?)\s*:\s*(.+?)\s*\)/);
var bits = map(function(s){return s.trim()}, str.substr(ix+1).split("->"));
for (var i=0 ; i<bits.length-1; i++) {
var bit = bits[i];
var m = regex_dep.exec(bit);
if (m == null) {
obj.args.push(bit);
} else {
// We have a type dependency
obj.deps.push({ "id": m[1], "type": m[2] });
}
}
//return type
obj.ret = bits[bits.length-1];
return obj;
}

View File

@@ -0,0 +1,140 @@
body.syntax-editor {
background: #ccc url("../minibar/brushed-metal.png");
}
.hidden
{
display:none;
}
.editor select#to_menu
{
height: 10em;
position: absolute;
min-width: 5em;
}
#import
{
position: absolute;
display: inline-block;
padding: 0.3em;
background: #EEE;
border: 1px solid #999;
font-size: 0.95em;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
#import.hidden
{
display: none;
}
#import input[type=text]
{
width: 20em;
font-family: monospace;
padding: 0.2em;
}
#tree, #tree_str
{
white-space:pre-wrap;
font-family: monospace;
background: rgba(238, 238, 238, 0.6);
padding:0.5em;
margin:0.5em 0;
}
#tree .node
{
margin: 0.5em 0 0.5em 0.75em;
border-left: 1px dashed #BBB;
padding-left: 1.25em;
}
#tree .node.first
{
margin-left: 0;
border-left: none;
}
#tree .node a
{
cursor: pointer;
}
#tree .node a:hover
{
text-decoration: underline;
}
#tree .node a.current
{
font-weight: bold;
}
#tree_str
{
font-size:0.85em;
color:#666;
}
#refinements
{
/* display: inline-block; */
/* margin-left: 0.5em; */
margin-top: 0.3em;
border: 3px solid #e0e0e0;
padding: 5px;
}
#linearisations
{
/* background: rgba(170, 170, 170, 0.5); */
/* padding:0.5em; */
margin:0.5em 0;
}
#linearisations div
{
padding:0.2em;
}
#linearisations .lang
{
display: inline-block;
margin-right: 0.5em;
width: 3em;
font-weight: bold;
text-align: center;
}
#linearisations .lin
{
}
.refinement
{
margin: 0.1em;
display: inline-block;
cursor: pointer;
border: 1px solid;
padding: 0.2em;
font: 0.9em sans-serif;
background: white;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
.refinement.disabled
{
opacity: 0.5;
}
div#debug
{
font: 10px monospace;
white-space: pre-wrap;
color: #333;
margin: 1em 0;
border: 1px dashed #999;
padding: 1em;
}
/* From http://www.grammaticalframework.org/css/style.css */
.about table { border-collapse: collapse; }
.about th,
.about td { border: 1px solid #333; padding:0 5px; }
.about td { background: white; }
.about th { background: #9df; }

View File

@@ -0,0 +1,46 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="author" href="http://www.grammaticalframework.org/~john/" title="John J. Camilleri">
<title>Syntax Editor</title>
<link rel="stylesheet" type="text/css" href="../minibar/minibar.css" />
<link rel="stylesheet" type="text/css" href="editor.css" />
</head>
<body class="syntax-editor">
<h2>Syntax Editor</h2>
<div id="minibar"></div>
<div id="editor"></div>
<noscript>This page doesn't work unless JavaScript is enabled.</noscript>
<hr />
<small class="modtime">
John J. Camilleri, December 2012 </br>
[ <a href="about.html">About syntax editor</a>
| <a href="../minibar/minibar.html">Minibar</a> ]
</small>
<div id="debug" class="hidden"></div>
<!-- Common -->
<script type="text/javascript" src="../js/grammar_manager.js"></script>
<script type="text/javascript" src="../js/support.js"></script>
<script type="text/JavaScript" src="../js/localstorage.js"></script>
<script type="text/javascript" src="../js/pgf_online.js"></script>
<script type="text/JavaScript" src="../js/langcode.js"></script>
<!-- Editor -->
<script type="text/javascript" src="ast.js"></script>
<script type="text/javascript" src="editor_menu.js"></script>
<script type="text/javascript" src="editor.js"></script>
<!-- Minibar -->
<script type="text/JavaScript" src="../minibar/minibar.js"></script>
<script type="text/JavaScript" src="../minibar/minibar_input.js"></script>
<script type="text/JavaScript" src="../minibar/minibar_translations.js"></script>
<script type="text/JavaScript" src="../minibar/minibar_support.js"></script>
<!-- Get us rolling! -->
<script type="text/javascript" src="editor_online.js"></script>
</body>
</html>

View File

@@ -0,0 +1,588 @@
/* --- Main Editor object --------------------------------------------------- */
function Editor(gm,opts) {
var t = this;
/* --- Configuration ---------------------------------------------------- */
// default values for options:
this.options={
target: "editor",
initial: {
grammar: null,
startcat: null,
languages: null,
abstr: null,
node_id: null
},
show: {
grammar_menu: true,
startcat_menu: true,
to_menu: true,
random_button: true
}
}
// Apply supplied options
if(opts) for(var o in opts) this.options[o]=opts[o];
/* --- Creating UI components ------------------------------------------- */
this.container = document.getElementById(this.options.target);
this.container.classList.add("editor");
this.ui = {
menubar: div_class("menu"),
tree: div_id("tree"),
tree_str: div_id("tree_str"),
actionbar: div_id("actions"),
clear_button: button("Clear", function(){
t.clear_node();
}),
wrap_button: button("Wrap…", function(){
t.wrap_candidates();
}),
unwrap_button: button("Unwrap", function(){
t.unwrap();
}),
refinements: div_id("refinements"),
lin: div_id("linearisations")
};
this.ui.clear_button.title = "Clear current node and all its children";
this.ui.wrap_button.title = "Wrap the current node with a new function";
this.ui.unwrap_button.title = "Replace parent of current node with current node (if possible)";
appendChildren(this.container, [
t.ui.menubar,
t.ui.tree,
t.ui.tree_str,
t.ui.actionbar,
t.ui.lin
]);
appendChildren(this.ui.actionbar, [
t.ui.clear_button,
t.ui.wrap_button,
t.ui.unwrap_button,
t.ui.refinements
]);
/* --- Client state initialisation -------------------------------------- */
this.gm = gm;
this.server = gm.server;
this.ast = null;
this.clear_on_change_startcat = true; // temp. false when wrapping
/* --- Register Grammar Manager hooks ----------------------------------- */
this.hook_change_grammar = function(grammar){
debug("Editor: change grammar");
var args = {
format: "json"
};
var cont = function(data){
t.process_grammar_constructors(data);
t.start_fresh();
};
t.server.browse(args, cont);
};
this.hook_change_startcat = function(startcat){
debug("Editor: change startcat");
t.startcat = startcat;
if (t.clear_on_change_startcat)
t.start_fresh();
};
this.hook_change_languages = function(languages){
debug("Editor: change languages");
t.update_linearisation();
};
this.gm.register_action("change_grammar",this.hook_change_grammar);
this.gm.register_action("change_startcat",this.hook_change_startcat);
this.gm.register_action("change_languages",this.hook_change_languages);
/* --- Main program, this gets things going ----------------------------- */
this.menu = new EditorMenu(this, this.options);
/* --- Other basic stuff ------------------------------------------------ */
this.shutdown = function() {
t.gm.unregister_action("change_grammar",t.hook_change_grammar);
t.gm.unregister_action("change_startcat",t.hook_change_startcat);
t.gm.unregister_action("change_languages",t.hook_change_languages);
clear(t.container);
t.container.classList.remove("editor");
}
this.hide = function() {
t.container.style.display="none";
}
this.show = function() {
t.container.style.display="block";
}
}
/* --- Pre-processed grammar information ------------------------------------ */
Editor.prototype.process_grammar_constructors=function(data) {
var t = this;
t.grammar_constructors=data;
for (var fun in t.grammar_constructors.funs) {
var def = t.grammar_constructors.funs[fun].def;
var typeobj = AST.parse_type_signature(def);
t.grammar_constructors.funs[fun] = typeobj;
}
}
// Look up information for a category
Editor.prototype.lookup_cat = function(cat) {
var t = this;
return t.grammar_constructors.cats[cat];
}
// Look up information for a function
Editor.prototype.lookup_fun = function(fun) {
var t = this;
return t.grammar_constructors.funs[fun];
}
/* --- API for getting and setting state ------------------------------------ */
Editor.prototype.get_ast=function() {
return this.ast.toString();
}
Editor.prototype.get_startcat=function() {
return this.gm.startcat;
}
// Called from Minibar, for example
Editor.prototype.initialize_from=function(opts) {
var t = this;
if (opts.abstr) {
t.ast = new AST(null, t.get_startcat());
t.import_ast(opts.abstr);
}
}
// Called after changing grammar or startcat
Editor.prototype.start_fresh=function () {
var t = this;
t.ast = new AST(null, t.get_startcat());
if (t.options.initial.abstr) {
t.import_ast(t.options.initial.abstr);
t.options.initial.abstr = null; // don't use again
}
t.update_current_node();
clear(t.ui.lin);
}
/* --- Functions for handling tree manipulation ----------------------------- */
// Add refinement to UI, returning object
Editor.prototype.add_refinement=function(fun,opts) {
var t = this;
options = {
label: fun,
disable_destructive: false
};
if (opts) for (var o in opts) options[o] = opts[o];
var typeobj = t.lookup_fun(fun);
// hide refinement if identical to current fun?
var opt = span_class("refinement", text(options.label));
opt.title = typeobj.signature;
// If refinement would be destructive, disable it
if (options.disable_destructive) {
var blank = t.ast.is_writable();
var inplace = t.ast.fits_in_place(typeobj);
if (!blank && !inplace) {
opt.classList.add("disabled");
}
}
t.ui.refinements.appendChild(opt);
return opt;
}
// Add a literal refinement to UI, e.g. String
Editor.prototype.add_literal_refinement=function() {
var t = this;
t.ui.refinements.innerHTML = "Enter string: ";
var nde = t.ast.getCurrentNode();
var val = (nde.string) ? nde.string : "";
var input = node("input",{
type:"text",
value: val
},[]);
// TODO: Perhaps we should have an update button instead
input.onkeyup = function() {
nde.string = input.value;
t.redraw_tree();
t.update_linearisation();
}
t.ui.refinements.appendChild(input);
}
// Show refinements for given cat (usually that of current node)
Editor.prototype.get_refinements=function(cat) {
var t = this;
t.ui.refinements.innerHTML = "…";
if (cat == undefined) cat = t.ast.getCat();
// Special case when cat is "String"
if (cat == "String") {
t.add_literal_refinement();
return;
}
var args = {
id: cat,
format: "json"
};
var cont = function(data){
clear(t.ui.refinements);
// t.ui.refinements.innerHTML = "Refinements: ";
function addClickHandler(fun) {
return function() {
t.select_refinement.apply(t,[fun]);
}
}
for (var pi in data.producers) {
var fun = data.producers[pi];
var ref = t.add_refinement(fun, {
disable_destructive: true
});
ref.onclick = addClickHandler(fun);
}
};
var err = function(data){
clear(t.ui.refinements);
alert("Error");
};
t.server.browse(args, cont, err);
}
// Select refinement now by default replaces "in-place"
// Case 1: current node is blank/no kids
// Case 2: kids have all same types, perform an in-place replacement
// Case 3: kids have diff types/number, prevent replacement (must clear first)
Editor.prototype.select_refinement=function(fun) {
var t = this;
// Check if current node is blank or childless (case 1)
var blank = t.ast.is_writable();
// Check if we can replace in-place (case 2)
var typeobj = t.lookup_fun(fun);
var inplace = !blank && t.ast.fits_in_place(typeobj);
if (!blank && !inplace) {
alert("Use 'Clear' first if you want to replace the subtree.");
return;
}
t.ast.setFun(fun);
if (blank) {
t.ast.removeChildren();
// Add dependent type placeholders
if (typeobj.deps.length > 0) {
alert("the syntax editor current doesn't support dependent types");
// for (var i in typeobj.deps) {
// t.ast.addDep(typeobj.deps[i].id, typeobj.deps[i].type);
// }
}
// Add function argument placeholders
if (typeobj.args.length > 0) {
for (var i in typeobj.args) {
t.ast.add(null, typeobj.args[i]);
}
}
}
// Update ui
t.redraw_tree();
t.update_linearisation();
// Select next hole & get its refinements
t.ast.toNextHole();
t.update_current_node();
}
Editor.prototype.update_current_node=function(newID) {
with(this) {
if (newID)
ast.setCurrentID(newID);
redraw_tree();
get_refinements();
}
}
// Clear current node and all its children
Editor.prototype.clear_node = function() {
var t = this;
t.ast.removeChildren();
t.ast.setFun(null);
t.redraw_tree();
t.get_refinements();
}
// Show wrap candidates
Editor.prototype.wrap_candidates = function() {
var t = this;
// we need to end with this
var cat = t.ast.getCat();
var refinements = [];
for (var i in t.grammar_constructors.funs) {
var obj = t.grammar_constructors.funs[i];
if (elem(cat, obj.args)) {
// if no parent, then cat can be anything
// as long as the current tree fits somewhere
if (!t.ast.hasParent() || obj.ret==cat) {
refinements.push(obj);
}
}
}
if (refinements.length == 0) {
alert("No functions exist which can wrap the selected node.");
return;
}
// Display wrap refinements
function addClickHandler(fun, child_ix) {
return function() {
t.wrap.apply(t,[fun, child_ix]);
}
}
t.ui.refinements.innerHTML = "Wrap with: ";
for (var i in refinements) {
var typeobj = refinements[i];
var fun = typeobj.name;
// Find valid child ids
var child_ixs = [];
for (var a in typeobj.args) {
if (typeobj.args[a] == cat) {
child_ixs.push(a);
}
}
// if (child_ixs.length < 2) {
// var ref = t.add_refinement(fun);
// ref.onclick = addClickHandler(typeobj.name, a);
// } else {
// Show a refinement for each potential child position
for (var c in child_ixs) {
var id = child_ixs[c];
var label = fun;
for (var a in typeobj.args) {
if (a == id)
if (t.ast.currentNode.hasChildren())
label += " ("+t.ast.currentNode.fun+" …)";
else
label += " "+t.ast.currentNode.fun;
else
label += " ?";
}
var ref = t.add_refinement(typeobj.name, {label: label});
ref.onclick = addClickHandler(typeobj.name, id);
}
// }
}
}
// Wrap the current node inside another function
Editor.prototype.wrap = function(fun, child_ix) {
var t = this;
var typeobj = t.grammar_constructors.funs[fun];
// if we are at root node, potentially change startcat
if (t.ast.atRoot() && t.get_startcat() != typeobj.ret) {
var old_val = t.clear_on_change_startcat;
t.clear_on_change_startcat = false;
t.gm.change_startcat(typeobj.ret);
t.clear_on_change_startcat = old_val;
}
// do actual replacement
t.ast.wrap(typeobj, child_ix);
// refresh stuff
t.redraw_tree();
t.update_linearisation();
t.ast.toNextHole();
t.update_current_node();
}
// Unwrap a node by deleting a fun with same input/output category
Editor.prototype.unwrap = function() {
var t = this;
var fun = t.ast.getFun();
var typeobj = t.grammar_constructors.funs[fun];
// Cannot unwrap when at root
if (t.ast.atRoot()) {
alert("It is not possible to unwrap the top node");
return;
}
var child = t.ast.getCurrentNode();
var parent = t.ast.getParent();
// TODO: We can also unwrap when at level one and cats don't match
// Check if unwrap is possible
if (parent.children.length==1 &&
(parent.cat==child.cat || parent==t.ast.getRoot())
) {
// do actual unwrap
t.ast.unwrap();
// if root node changed, potentially change startcat
var rootcat = t.ast.getRoot().cat;
if (rootcat != t.get_startcat()) {
var old_val = t.clear_on_change_startcat;
t.clear_on_change_startcat = false;
t.gm.change_startcat(rootcat);
t.clear_on_change_startcat = old_val;
}
// refresh stuff
t.redraw_tree();
t.update_linearisation();
// t.ast.toNextHole();
// t.update_current_node();
} else {
alert("Cannot unwrap this node");
}
}
// Generate random subtree from current node
Editor.prototype.generate_random = function() {
var t = this;
t.ast.removeChildren();
var args = {
cat: t.ast.getCat(),
limit: 1
};
if (!args.cat) {
alert("Missing category at current node");
return;
}
var cont = function(data){
var tree = data[0].tree;
t.import_ast(tree);
};
var err = function(data){
alert("Error");
};
t.server.get_random(args, cont, err);
}
// Redraw tree
Editor.prototype.redraw_tree=function() {
var t = this;
var elem = node; // function from support.js
function visit(container, id, node) {
var container2 = empty_class("div", "node");
if (id.get().length == 1)
container2.classList.add("first");
// Special case for String literal
if (node.cat == "String") {
var label =
'"' + ((node.string) ? node.string : "") + '"' +
" : String";
} else {
var label =
((node.fun) ? node.fun : "?") + " : " +
((node.cat) ? node.cat : "?");
}
var current = id.equals(t.ast.getCurrentID());
var element = elem("a", {class:(current?"current":"")}, [text(label)]);
element.onclick = function() {
t.update_current_node(id);
}
container2.appendChild( element );
for (var i in node.children) {
var newid = new NodeID(id);
newid.add(parseInt(i));
visit(container2, newid, node.children[i]);
}
container.appendChild(container2);
}
with(this) {
clear(ui.tree);
ui.tree_str.innerText = ast.toString();
visit(ui.tree, new NodeID(), ast.root);
}
}
// Get and display linearisations for AST
Editor.prototype.update_linearisation=function(){
var t = this;
// langpart("FoodsEng","Foods") == "Eng"
function langpart(conc,abs) {
return hasPrefix(conc,abs) ? conc.substr(abs.length) : conc;
}
function row(lang, lin) {
var langname = langpart(lang, t.gm.grammar.name);
var btn = button(langname, function(){
bind(t.options.lin_action,t)(lin,lang);
});
if (t.options.lin_action_tooltip) {
btn.title = t.options.lin_action_tooltip;
}
var c1 = th(btn);
var c2 = td(text(lin));
var row = tr([c1,c2]);
return row;
}
var args = {
tree: t.ast.toString()
};
t.server.linearize(args, function(data){
clear(t.ui.lin);
var tbody=empty("tbody");
for (var i in data) {
var lang = data[i].to;
if (t.gm.languages.length < 1 || elem(lang, t.gm.languages)) {
tbody.appendChild(row(lang, data[i].text))
}
}
t.ui.lin.appendChild(wrap("table",tbody));
});
}
// Import AST from string representation, setting at current node
Editor.prototype.import_ast = function(abstr) {
var t = this;
var args = {
tree: abstr
};
var cont = function(tree){
// Build tree of just fun, then populate with cats
t.ast.setSubtree(tree);
/// TODO: traverse only subtree, not everything!
t.ast.traverse(function(node){
if (!node.fun) return;
var typeobj = t.lookup_fun(node.fun);
node.cat = typeobj.ret;
});
t.redraw_tree();
t.update_linearisation();
};
var err = function(tree){
alert("Invalid abstract syntax tree");
};
t.server.pgf_call("abstrjson", args, cont, err);
}

View File

@@ -0,0 +1,197 @@
/* --- Editor Menu object --------------------------------------------------- */
function EditorMenu(editor,opts) {
var t = this;
/* --- Configuration ---------------------------------------------------- */
// default values for options:
this.options={
target: "editor",
show_grammar_menu: true,
show_startcat_menu: true,
show_to_menu: true,
show_random_button: true,
show_import: true,
show_debug: false,
}
// Apply supplied options
if(opts) for(var o in opts) this.options[o]=opts[o];
/* --- Creating UI components ------------------------------------------- */
this.container = editor.ui.menubar;
this.ui = {
grammar_menu: empty_id("select","grammar_menu"),
startcat_menu: empty("select"),
to_toggle: button("Languages…", function(){
toggleHidden(t.ui.to_menu);
}),
to_menu: node("select",{
id: "to_menu",
multiple: "multiple",
class: "hidden"
}),
random_button: button("Random", function(){
t.editor.generate_random();
}),
import: {
toggle: button("Import…", function(){
toggleHidden(t.ui.import.panel);
}),
panel: node("div",{
id: "import",
class: "hidden"
}),
input: node("input",{type:"text"},[]),
button: button("Import", function(){
t.editor.import_ast(t.ui.import.input.value);
toggleHidden(t.ui.import.panel);
})
},
debug_toggle: button("⚙", function(){
toggleHidden(element("debug"));
})
};
this.ui.to_toggle.title = "Select languages to linearise to (use Ctrl/Shift to select multiple)";
this.ui.random_button.title = "Insert a randomly generated tree at the current node";
this.ui.import.toggle.title = "Import an abstract syntax tree from a string (replaces current tree)";
this.ui.debug_toggle.title = "Toggle the debug console";
if (t.options.show_grammar_menu) {
appendChildren(t.container, [text(" Grammar: "), t.ui.grammar_menu]);
t.ui.grammar_menu.onchange = function(){
var grammar_url = t.ui.grammar_menu.value;
t.gm.change_grammar(grammar_url);
}
}
if (t.options.show_startcat_menu) {
appendChildren(t.container, [text(" Start Category: "), t.ui.startcat_menu]);
t.ui.startcat_menu.onchange = function(){
var startcat = t.ui.startcat_menu.value;
t.gm.change_startcat(startcat);
}
}
if (t.options.show_to_menu) {
appendChildren(t.container, [text(" To: "), t.ui.to_toggle, t.ui.to_menu]);
t.ui.to_menu.onchange = function(){
var languages = multiMenuSelections(t.ui.to_menu)
t.gm.change_languages(languages);
}
}
if (t.options.show_random_button) {
appendChildren(t.container, [t.ui.random_button]);
}
if (t.options.show_import) {
appendChildren(t.container, [
t.ui.import.toggle,
t.ui.import.panel
]);
appendChildren(t.ui.import.panel, [
text("Import AST: "),
t.ui.import.input,
t.ui.import.button
]);
}
if (t.options.show_debug) {
appendChildren(t.container, [t.ui.debug_toggle]);
}
/* --- Client state initialisation -------------------------------------- */
this.editor = editor;
this.gm = editor.gm;
this.server = editor.server;
/* --- Register Grammar Manager hooks ----------------------------------- */
this.gm.register_action("onload", bind(this.hook_onload, this));
this.gm.register_action("change_grammar", bind(this.hook_change_grammar, this));
this.gm.register_action("change_startcat", bind(this.hook_change_startcat, this));
}
/* --- Grammar menu --------------------------------------------------------- */
// show the grammar list
EditorMenu.prototype.hook_onload=function(dir,grammar_names,dir_count) {
debug("EditorMenu: onload");
var t=this;
var first_time=t.ui.grammar_menu.options.length == 0;
if(first_time) {
t.grammars=[];
t.grammar_dirs=[];
}
t.grammar_dirs.push(dir);
t.grammars=t.grammars.concat(grammar_names.map(function(g){return dir+g}))
function glabel(g) {
return hasPrefix(dir,"/tmp/gfse.") ? "gfse: "+g : g
}
function opt(g) { return option(glabel(g),dir+g); }
appendChildren(t.ui.grammar_menu, map(opt, grammar_names));
function pick_first_grammar() {
if(t.timeout) clearTimeout(t.timeout),t.timeout=null;
var grammar0=t.gm.options.initial.grammar;
if(!grammar0) grammar0=t.grammars[0];
t.ui.grammar_menu.value=grammar0;
// t.change_grammar();
}
// Wait at most 1.5s before showing the grammar menu.
if(first_time) t.timeout=setTimeout(pick_first_grammar,1500);
if(t.grammar_dirs.length>=dir_count) pick_first_grammar();
}
// Copied from minibar.js
EditorMenu.prototype.hook_change_grammar=function(grammar) {
debug("EditorMenu: change grammar");
var t=this;
t.update_startcat_menu(grammar);
t.update_language_menu(t.ui.to_menu, grammar);
}
/* --- Start category menu -------------------------------------------------- */
// Called from hook_change_grammar
EditorMenu.prototype.update_startcat_menu=function(grammar) {
var t=this;
var menu=this.ui.startcat_menu;
menu.innerHTML="";
var cats=grammar.categories;
for(var cat in cats) menu.appendChild(option(cats[cat],cats[cat]))
var startcat0 = t.gm.options.initial.startcat;
if (elem(startcat0, cats))
menu.value = startcat0;
else
menu.value = grammar.startcat;
}
// If startcat changed externally, update menu
EditorMenu.prototype.hook_change_startcat=function(startcat) {
debug("EditorMenu: change startcat");
var t=this;
var menu=this.ui.startcat_menu;
menu.value = startcat;
}
/* --- Langugage (to) menu -------------------------------------------------- */
// Called from hook_change_grammar
EditorMenu.prototype.update_language_menu=function(menu,grammar) {
var t = this;
function langpart(conc,abs) { // langpart("FoodsEng","Foods") == "Eng"
return hasPrefix(conc,abs) ? conc.substr(abs.length) : conc;
}
// Replace the options in the menu with the languages in the grammar
var langs=grammar.languages;
menu.innerHTML="";
for(var i=0; i<langs.length; i++) {
var ln=langs[i].name;
if(!hasPrefix(ln,"Disamb")) {
var lp=langpart(ln,grammar.name);
var opt=option(lp,ln);
if (elem(ln, t.gm.languages)) {
opt.selected=true;
}
menu.appendChild(opt);
}
}
}

View File

@@ -0,0 +1,65 @@
var server_options = {
// grammars_url: "http://www.grammaticalframework.org/grammars/",
// grammars_url: "http://localhost:41296/grammars/",
}
var editor_options = {
target: "editor",
initial: {
// abstr: "PropOpenDate (SuperlPlace TheMostExpensive School) Tomorrow"
},
show: {
grammar_menu: true,
startcat_menu: true,
to_menu: true,
random_button: true
}
}
var gm_options = {
initial: {
// grammar: "http://localhost:41296/grammars/Smart.pgf",
// startcat: "Command",
// languages: ["Eng","Swe"]
}
}
if(window.Minibar) // Minibar loaded?
editor_options.lin_action_tooltip="Load sentence in Minibar";
editor_options.lin_action=function(s,langFrom) {
var editor=this;
var minibar_options = {
target: "minibar",
show_abstract: true,
show_trees: true,
show_grouped_translations: false,
show_brackets: true,
word_replacements: true,
initial_grammar: editor.menu.ui.grammar_menu.value, // hmm
initial: {
from: langFrom,
input: s.split(" "), // is it that easy?
startcat: editor.menu.ui.startcat_menu.value // hmm
},
initial_toLangs: multiMenuSelections(editor.menu.ui.to_menu), // hmm
// get us back to the editor!
abstract_action: function(tree) {
var opts = {
abstr: tree
}
editor.initialize_from(opts);
editor.minibar.hide();
editor.show();
}
}
editor.hide();
editor.minibar=new Minibar(server,minibar_options);
//editor.minibar.editor = editor; // :S
editor.minibar.show();
}
if(/^\?\/tmp\//.test(location.search)) {
var args=decodeURIComponent(location.search.substr(1)).split(" ")
if(args[0]) server_options.grammars_url=args[0];
}
var server = pgf_online(server_options);
var gm = new GrammarManager(server, gm_options);
var editor = new Editor(gm, editor_options);

View File

@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html> <head>
<title>About: Simple Translation Tool</title>
<link rel="stylesheet" type="text/css" href="../gfse/editor.css" title="Cloud">
<link rel="alternate stylesheet" type="text/css" href="../gfse/molto.css" title="MOLTO">
<link rel=author href="http://www.cse.chalmers.se/~hallgren/" title="Thomas Hallgren">
<meta name = "viewport" content = "width = device-width">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta charset="UTF-8">
</head>
<body>
<h1><a href="../"><img src="../P/gf-cloud.png" alt="" title="GF Cloud Service"></a>About the Simple Translation Tool</h1>
<p>
<a href="./">This</a> is a simple bilingual document editor. Documents consist
of a sequence of segments that are translated independently. The user can import
text in the source language and obtain automatically translated text in the
target language. Imported text can be segmented based on punctuation.
Optionally, one can also use line breaks or blank lines to indicate segmentation
in imported text. Text can be edited after it has been imported.
<p>
Through menu options, the user sets the source and target language
for the document and chooses which translation method to use by default.
The tool supports the following machine translation services:
<ul>
<li>The <a href="http://www.grammaticalframework.org/">GF</a> web service.
The user picks which GF grammar to use from a menu of available grammars.
<li>The <a href="http://www.apertium.org/">Apertium</a> web service.
<li>The GF Robust Parser. This is an exprimental service that supports
translation from English to a few other languages.
</ul>
<p>
If an unsatisfactory automatic translation is
obtained, the user can click on it and replace it with a manual translation.
If multiple translations are obtained, one of them is shown by default and
the other ones are available in a popup menu.
<p>
Source segments can also be edited. If a GF grammar is used for translation,
the <a href="../minibar/about.html">Minibar</a> and the
<a href="../syntax-editor/about.html">Syntax Editor</a> can be used.
A plain text box is also available, regardless of translation method.
<p>
The tool handles a set of documents. Documents can be named, saved,
closed and reopened later. Documents can be saved locally or in the cloud.
<h2>TODO</h2>
<ul>
<li>Text can be imported by copying and pasting and from local files
(in browsers that support it, currently Chrome, Firefox and Opera, but
not Safari), but other ways could be added.
<li>Text can be exported by copying and pasting, but other ways could
be added.
<li>GF's lexer/unlexer is used to allow for more natural looking text, but
the unlexer does the wrong thing if the first word of a sentence is supposed
to be capitalized, e.g. "I am ready." and "Spanish wine is good."
<li>Document sharing in the cloud.
<li>Interface to other translation services.
<li>Interface to the grammar editor for grammar extension.
<li>More browser compatibility testing (Chrome, Firefox, Safari &amp;
Opera Mobile tested so far).
<li>...
<li>...
</ul>
<hr>
<div class=modtime><small>
<!-- hhmts start -->Last modified: Fri Apr 19 15:58:50 CEST 2013 <!-- hhmts end -->
</small></div>
<address>
<a href="http://www.cse.chalmers.se/~hallgren/">TH</a>
<img src="http://www.altocumulus.org/~hallgren/online.cgi?icon" alt="">
</address>
</body>
</html>

View File

@@ -0,0 +1,110 @@
<!DOCTYPE html>
<html> <head>
<title>Simple Translation Tool</title>
<link rel="stylesheet" type="text/css" href="../gfse/editor.css" title="Cloud">
<link rel="stylesheet" type="text/css" href="translator.css" title="Cloud">
<link rel="stylesheet" type="text/css" href="../minibar/minibar.css">
<link rel=stylesheet type="text/css" href="../syntax-editor/editor.css">
<meta name = "viewport" content = "width = device-width">
<meta charset="UTF-8">
</head>
<body class=hover>
<div class=pagehead>
<h1><img src="../P/gf-cloud.png" alt="" title="GF Cloud Service">Simple Translation Tool</h1>
<form name=options>
<table class=menubar>
<tr><td><span onclick="">File</span>
<dl>
<dt onclick="translator.new(this)">New
<dt onclick="translator.browse(this)">Open...
<dt id="import_globalsight" onclick="translator.import_globalsight(this)">Import from GlobalSight...
<dt onclick="translator.save(this)">Save
<dt onclick="translator.saveAs(this)">Save As...
<!--
<dt onclick="translator.close(this)">Close
-->
<dt onclick="translator.open_in_wc(this)">Open in Wide Coverage Translation Demo
</dl>
<td><span onclick="">Edit</span>
<dl>
<dt id="edit_import" onclick="translator.import(this)">Import text...
<dt id="edit_add_segment" onclick="translator.add_segment(this)">Add a segment...
<dt id="edit_remove_segment" onclick="translator.remove(this)">Remove the last segment
</dl>
<td><span onclick="">View</span>
<dl>
<dt><label><input name=view value=segmentbysegment type=radio checked onchange="translator.change(this)">Segment by segment</label>
<dt><label><input name=view value=paralleltexts type=radio onchange="translator.change(this)">Parallel texts</label>
</dl>
<td><span onclick="">Options</span>
<dl>
<dt>
<table class=submenu>
<tr><td><span onclick="">Source Language</span>
<dl id=source>
<dt><label><input name=source value=Eng type=radio onchange="translator.change(this)">English</label>
<dt><label><input name=source value=Swe type=radio onchange="translator.change(this)">Swedish</label>
<dt><label><input name=source value=Ita type=radio onchange="translator.change(this)">Italian</label>
</dl>
</table>
<dt>
<table class=submenu>
<tr><td><span onclick="">Target Language</span>
<dl id=target>
<dt><label><input name=target value=Eng type=radio onchange="translator.change(this)">English</label>
<dt><label><input name=target value=Swe type=radio onchange="translator.change(this)">Swedish</label>
<dt><label><input name=target value=Ita type=radio onchange="translator.change(this)">Italian</label>
</dl>
</table>
<dt>
<table class=submenu>
<tr><td><span onclick="">Default translation method</span>
<dl id=methods>
<dt><label><input name=method value=Manual type=radio onchange="translator.change(this)">Manual</label>
<dt><label><input name=method value=GFRobust type=radio onchange="translator.change(this)">GF Wide Coverage Translation</label>
</dl>
</table>
<dt><label><input name=cloud type=checkbox onchange="translator.change(this)"> Save in the cloud</label>
</dl>
</table>
</form>
</div>
<div class=overlay><div id=filebox class=filebox></div></div>
<div id=document class=document>
...
<noscript>This document translation editor requires JavaScript to work
</noscript>
</div>
<hr>
<div class=modtime><small>HMTL
<!-- hhmts start -->Last modified: Fri Mar 6 12:46:23 CET 2015 <!-- hhmts end -->
</small></div>
<a href="about.html">About</a>
<script type="text/JavaScript" src="../js/grammar_manager.js"></script>
<script type="text/javascript" src="../js/support.js"></script>
<script type="text/javascript" src="../js/pgf_online.js"></script>
<script type="text/javascript" src="../js/gftranslate.js"></script>
<script type="text/javascript" src="../js/localstorage.js"></script>
<script type="text/javascript" src="../js/langcode.js"></script>
<script type="text/javascript" src="../gfse/localstorage.js"></script>
<script type="text/javascript" src="../gfse/cloud2.js"></script>
<script type="text/javascript" src="../minibar/minibar.js"></script>
<script type="text/javascript" src="../minibar/minibar_input.js"></script>
<script type="text/javascript" src="../minibar/minibar_translations.js"></script>
<script type="text/javascript" src="../minibar/minibar_support.js"></script>
<script type="text/javascript" src="../syntax-editor/ast.js"></script>
<script type="text/javascript" src="../syntax-editor/editor_menu.js"></script>
<script type="text/javascript" src="../syntax-editor/editor.js"></script>
<script type="text/javascript" src="translator.js"></script>
<script type="text/javascript" >
var translator = new Translator()
</script>
<script defer async type="text/javascript" src="http://api.apertium.org/JSLibrary.js" onload="translator.add_apertium()"></script>
</body>
</html>

View File

@@ -0,0 +1,20 @@
div.overlay {
display: none;
position: absolute;
z-index: 1;
top: 0;
left: 0;
width: 100%; height: 100%;
background: white; /* fallback */
background: rgba(0,0,0,0.3);
}
div.overlay > div {
margin: 5em 3em;
background: white; /* fallback */
background: rgba(255,255,255,0.95);
padding: 1em;
box-shadow: 4px 4px 12px rgba(0,0,0,0.33);
/*border-radius: 5px;*/
}

View File

@@ -0,0 +1,114 @@
@import url("overlay.css");
body { margin:0; padding: 5px; position: relative; }
h1 { float: right; margin: 0; font-size: 150%; }
h2 { font-size: 120%; }
h3 { font-size: 100%; }
div.pagehead {
font-family: sans-serif;
/*position: fixed; top: 5px; left: 5px; right: 5px; z-index: 2;*/
background-color: #d0d0d0;
padding: 1px 5px;
border-radius: 5px;
}
table.menubar td { padding: 5px; cursor: default; }
table.menubar dl, td.options > div > dl, dl.popupmenu {
z-index: 2;
display: none; position: absolute;
background: white; /* fallback */
background: rgba(255,255,255,0.95);
color: black;
border: 1px solid black;
margin: 0;
padding: 5px 0;
box-shadow: 4px 4px 12px rgba(0,0,0,0.33);
}
table.menubar td:hover > dl, :hover > dl.popupmenu { display: block; }
table.menubar dt, dl.popupmenu > dt { margin: 0; padding: 5px; }
table.submenu dt { padding: 0; }
table.menubar td:hover, table.menubar dt:hover, dl.popupmenu > dt:hover {
background-color: #36f; color: white;
}
table.menubar dt.disabled { color: #999; }
table.menubar dt.disabled:hover { background-color: white; }
table table dl { left: 6em; }
table.menubar dt { white-space: nowrap; }
dt.unsupported { color: #999; }
div.document {
/*margin-top: 7ex;*/
clear: both;
background: white;
border: 2px solid #009;
padding: 0.6ex;
}
div.document h2, div.document h3 { color: #009; }
table.segments { margin-left: auto; margin-right: auto; }
.current_segment, .segment:hover { background: #ff9; }
td.actions { padding: 1ex 1em 1ex 0.5em; }
td.options { padding: 1ex 1em; }
.segment td.source, .segment td.target {
padding: 1ex;
border-bottom: 2px solid #ccc;
vertical-align: baseline;
}
td.options > div, td.actions > div { position: relative; margin: 0; }
td.options:hover > div > form > dl,td.actions:hover > div > dl {
display: block;
}
td.options > div > form > dl, td.actions > div > dl {
left: 1em;
font-family: sans-serif;
white-space: nowrap;
}
td.source input[name=it], td.target input[name=it], textarea, input[name=punctchars] {
font-family: inherit; font-size: inherit;
}
td.source input[name=it], td.target input[name=it], textarea { width: 100%; }
table.paralleltexts td {
padding: 1ex;
vertical-align: baseline;
line-height: 130%;
}
table.paralleltexts td.source {
padding-right: 0.7em;
border-right: 2px solid #ccc;
}
table.paralleltexts td.target { padding-left: 0.7em; }
label { font-family: sans-serif; }
form { margin: 0; }
form.import {
min-width: 90%; /* extend to availiable width for Safari */
/*background: #eee;*/
margin: 10px; /* extend to available width for other browsers */
padding: 10px;
box-shadow: 4px 4px 12px rgba(0,0,0,0.33);
}
span.actions { visibility: hidden; }
tr:hover div > span.actions { visibility: visible; }
span.arrow, span.actions { color: blue; }
span.error { color: red; }
span.choices { color: blue; font-weight: bold; font-family: sans-serif; }
.default_quality { background-color: #ffc; background-color: rgba(255,255,0,0.20) }
.high_quality { background-color: #cfc; background-color: rgba(0,255,0,0.20); }
.low_quality { background-color: #fcc; background-color: rgba(255,0,0,0.20); }
.bad_quality { background-color: #f88; background-color: rgba(255,0,0,0.53); }
.manual_quality { background-color: #ccf; background-color: rgba(0,0,255,0.20); }
div#minibar, div#syntax_editor {
border: 1px solid black;
padding: 5px;
background: #ccc url("../minibar/brushed-metal.png");
}

File diff suppressed because it is too large Load Diff

138
src/compiler/www/wc.html Normal file
View File

@@ -0,0 +1,138 @@
<!DOCTYPE html>
<html> <head>
<title>Demo: GF Wide Coverage Translation</title>
<link rel="stylesheet" type="text/css" href="gfse/editor.css" title="Cloud">
<link rel="stylesheet" type="text/css" href="translator/overlay.css">
<meta name = "viewport" content = "width = device-width">
<meta charset="UTF-8">
<link rel=top href="../" title="GF">
<style>
div.center { text-align: center; }
table { border-collapse: collapse; table-layout: fixed; width: 100%; }
td { padding: 0 5px; vertical-align: top; }
td.output { background: #fcfcfc; border: 1px solid grey; }
td.input, td.output { font-family: sans-serif; font-size: 90%; }
div#output { white-space: pre-line; }
div.input { margin-right: 5px; }
textarea { font: inherit; }
body:target h1, body:target div.modtime { display: none; }
small { color: #666; }
#pick>* { padding: 0 0.5ex; }
#speak { display: none; }
.colors .default_quality { background-color: #ffb; }
.colors .high_quality { background-color: #bfb; }
.colors .low_quality { background-color: #fa7; }
.colors .bad_quality { background-color: #f89; }
.placeholder { color: #999; }
.error { color: #c00; }
div.e2 table { background: white; }
span.inflect { color: blue; }
.grammar_pick { margin: 5px; }
h2 > input { float: right; }
.node { cursor: pointer; }
/*.overlay { background-color: #eed; }*/
.node rect { fill: #fff; stroke: black; stroke-width: 1.5px; }
.node text { font-size: 10px; font-family: serif; }
.link { fill: none; stroke: #ccc; stroke-width: 1.5px; }
.speech_buttons > * { display: none; }
.speech_buttons > *:nth-child(1) { display: inline; }
.speech_buttons > *:nth-child(2) { display: inline; }
/* .speech_buttons:hover > * { display: inline; } */
</style>
</head>
<body id=embed>
<div>
<h1><a href="http://www.grammaticalframework.org/"><img class=nofloat src="P/gf-cloud.png" alt="GF"></a> Wide Coverage Translation Demo</h1>
</div>
<div class=overlay><div id=grammarbox class=grammarbox></div></div>
<form onsubmit="return wc.translate(true)" style="width: 100%">
<table>
<tr><td>
<select name=from>
<option value=Bul>Bulgarian</option>
<option value=Cat>Catalan</option>
<option value=Chi>Chinese</option>
<option value=Dut>Dutch</option>
<option value=Eng selected>English</option>
<option value=Est>Estonian</option>
<option value=Fin>Finnish</option>
<option value=Fre>French</option>
<option value=Ger>German</option>
<option value=Hin>Hindi</option>
<option value=Ita>Italian</option>
<option value=Jpn>Japanese</option>
<option value=Spa>Spanish</option>
<option value=Swe>Swedish</option>
<option value=Tha>Thai</option>
</select>
<input type=button value="Clear" onclick="wc.clear()">
<td style="text-align: right">
<input type=button name=swap onclick="wc.swap()" value="⇆">
<td>
<select name=to onchange="wc.translate()">
<option value=Bul>Bulgarian</option>
<option value=Cat>Catalan</option>
<option value=Chi>Chinese</option>
<option value=Dut>Dutch</option>
<option value=Eng>English</option>
<option value=Est>Estonian</option>
<option value=Fin>Finnish</option>
<option value=Fre>French</option>
<option value=Ger>German</option>
<option value=Hin>Hindi</option>
<option value=Ita>Italian</option>
<option value=Jpn>Japanese</option>
<option value=Spa>Spanish</option>
<option value=Swe selected>Swedish</option>
<option value=Tha>Thai</option>
</select>
<input name=colors type=checkbox checked onchange="wc.colors()"> Colors
<td><button name=translate type=submit><strong>Translate</strong></button>
<input type=button name=grammars onclick="wc.select_grammars()" value="Grammars...">
<tr><td class=input colspan=2>
<div class=input>
<textarea name=input rows=5 style="width: 100%" onkeyup="wc.delayed_translate()"></textarea>
</div>
<td class=output colspan=2>
<div id=output></div>
<tr><td colspan=2>
<small id=grammarinfo>Enter text to translate above.</small>
<td colspan=2>
<!--<small id=speak><input name=speak type=checkbox> Enable speech synthesis</small>-->
</table>
</form>
<div class=center>
<input type=button onclick="wc.try_google()" value="Try Google Translate">
<div id=pick></div>
<small id=extra></small>
</div>
<hr>
<div class=modtime><small>
<!-- hhmts start -->Last modified: Thu May 19 11:08:21 CEST 2016 <!-- hhmts end -->
</small></div>
<a href="http://www.grammaticalframework.org/demos/translation.html">About</a>
<script src="js/support.js"></script>
<script src="js/gftranslate.js"></script>
<script src="js/localstorage.js"></script>
<script src="js/langcode.js"></script>
<script src="js/pgf_online.js"></script>
<script src="minibar/minibar_support.js">/* speech functions */</script>
<script src="gfse/sort.js"></script>
<script src="js/wc.js"></script>
<script>
wc.initialize()
</script>
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="js/d3Tree.js"></script>
</body>
</html>