move gf.cabal and all compiler dependent files into src/compiler
BIN
src/compiler/www/P/cloud.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src/compiler/www/P/gf-cloud.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src/compiler/www/TransQuiz/brushed-metal.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
95
src/compiler/www/TransQuiz/minibar_quiz.css
Normal 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; }
|
||||
|
||||
640
src/compiler/www/TransQuiz/minibar_quiz.js
Normal 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
|
||||
*/
|
||||
56
src/compiler/www/TransQuiz/pgf_online.js
Normal 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;
|
||||
}
|
||||
50
src/compiler/www/TransQuiz/quiz_about.html
Normal 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 GF’s (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>
|
||||
46
src/compiler/www/TransQuiz/quiz_help.html
Normal 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>• 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>• 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 "⌫" (Delete last) <!--img border="0" src="Delete_button.jpg" alt="⌫" 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>
|
||||
|
||||
64
src/compiler/www/TransQuiz/quiz_pre_start.js
Normal 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);
|
||||
}
|
||||
|
||||
248
src/compiler/www/TransQuiz/quiz_support.js
Normal 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;
|
||||
}
|
||||
285
src/compiler/www/TransQuiz/support.js
Normal 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;
|
||||
}
|
||||
165
src/compiler/www/TransQuiz/translation_quiz.html
Normal 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>
|
||||
766
src/compiler/www/TransQuiz/translation_quiz.js
Normal 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();
|
||||
}
|
||||
20
src/compiler/www/cloud.css
Normal 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; }
|
||||
160
src/compiler/www/gf-cloud-api.html
Normal 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>&command=i+Foods.pgf</code>
|
||||
<dd>
|
||||
<dt><code>/gfshell?dir=</code>...<code>&command=gr</code>
|
||||
<dd class=response><code>Pred (That Pizza) (Very Boring)</code>
|
||||
<dt><code>/gfshell?dir=</code>...<code>&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>&command=<strong>upload</strong>&</code><var>path<sub>1</sub></var><code>=</code><var>source<sub>1</sub></var><code>&</code><var>path<sub>2</sub></var><code>=</code><var>source<sub>2</sub></var><code>&</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>&command=<strong>make</strong>&</code><var>path<sub>1</sub></var><code>=</code><var>source<sub>1</sub></var><code>&</code><var>path<sub>2</sub></var><code>=</code><var>source<sub>2</sub></var><code>&</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>&command=<strong>remake</strong>&</code><var>path<sub>1</sub></var><code>=</code><var>source<sub>1</sub></var><code>&</code><var>path<sub>2</sub></var><code>=</code><var>source<sub>2</sub></var><code>&</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>&command=<strong>download</strong>&file=</code><var>path</var>
|
||||
<dd>Download the specified file.
|
||||
|
||||
<dt><code>/cloud?dir=</code>...<code>&command=<strong>ls</strong>&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>&command=<strong>rm</strong>&file=</code><var>path</var>
|
||||
<dd>Remove the specified file.
|
||||
|
||||
<dt><code>/cloud?dir=</code>...<code>&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>
|
||||
183
src/compiler/www/gf-web-api-examples.html
Normal 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> grammars_url: "http://www.grammaticalframework.org/grammars/",
|
||||
<br> 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&tree=Pred+(That+Pizza)+(Very+Boring)&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&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&input=that+pizza+is+very+boring&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&input=that+pizza+is+very+boring&from=FoodsEng&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&input=that+pizza+is+very+&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&id=Kind&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&id=This&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&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&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&input=fish&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>
|
||||
358
src/compiler/www/gf-web-api.t2t
Normal 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]
|
||||
112
src/compiler/www/gfmorpho/GFMorpho.hs
Normal 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>"
|
||||
]
|
||||
|
||||
23
src/compiler/www/gfmorpho/README
Normal 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.
|
||||
|
||||
7
src/compiler/www/gfmorpho/gfmorpho.cgi
Normal 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"
|
||||
112
src/compiler/www/gfmorpho/gfmorpho.html
Normal 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>
|
||||
107
src/compiler/www/gfmorpho/index.html
Normal 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>
|
||||
42
src/compiler/www/gfmorpho/morpho.js
Normal 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()
|
||||
}
|
||||
7
src/compiler/www/gfse/Makefile
Normal 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
|
||||
BIN
src/compiler/www/gfse/P/1306856253_weather_06.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
src/compiler/www/gfse/P/1307545089_weather_04.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
src/compiler/www/gfse/P/1343909216_weather_03.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
src/compiler/www/gfse/P/w1s.jpg
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
src/compiler/www/gfse/P/w2s.jpg
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
src/compiler/www/gfse/P/w3s.jpg
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
src/compiler/www/gfse/P/w4s.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
44
src/compiler/www/gfse/TODO
Normal 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.
|
||||
309
src/compiler/www/gfse/about.html
Normal 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 & 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>
|
||||
148
src/compiler/www/gfse/cloud.js
Normal 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)
|
||||
}
|
||||
278
src/compiler/www/gfse/cloud2.js
Normal 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) }
|
||||
152
src/compiler/www/gfse/editor.css
Normal 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;
|
||||
}
|
||||
2147
src/compiler/www/gfse/editor.js
Normal file
190
src/compiler/www/gfse/example_based.js
Normal 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)
|
||||
}
|
||||
345
src/compiler/www/gfse/gf_abs.js
Normal 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;
|
||||
}
|
||||
4
src/compiler/www/gfse/gfse.manifest
Normal file
@@ -0,0 +1,4 @@
|
||||
CACHE MANIFEST
|
||||
# 5
|
||||
NETWORK:
|
||||
*
|
||||
82
src/compiler/www/gfse/index.html
Normal 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>
|
||||
4
src/compiler/www/gfse/localstorage.js
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
//Needs ../js/localstorage.js
|
||||
|
||||
var local=appLocalStorage("gf.editor.simple.grammar")
|
||||
105
src/compiler/www/gfse/molto.css
Normal 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");
|
||||
}
|
||||
28
src/compiler/www/gfse/share.html
Normal 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>
|
||||
86
src/compiler/www/gfse/slideshow.js
Normal 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);
|
||||
}
|
||||
}
|
||||
164
src/compiler/www/gfse/sort.js
Normal 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
|
||||
43
src/compiler/www/index.html
Normal 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
@@ -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)");
|
||||
}
|
||||
43
src/compiler/www/js/gfrobust.js
Normal 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
|
||||
}
|
||||
167
src/compiler/www/js/gftranslate.js
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
180
src/compiler/www/js/grammar_manager.js
Normal 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);
|
||||
}
|
||||
|
||||
70
src/compiler/www/js/langcode.js
Normal 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
|
||||
}
|
||||
}
|
||||
60
src/compiler/www/js/localstorage.js
Normal 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())
|
||||
}
|
||||
80
src/compiler/www/js/pgf_online.js
Normal 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;
|
||||
}
|
||||
431
src/compiler/www/js/support.js
Normal 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
@@ -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()
|
||||
}
|
||||
267
src/compiler/www/minibar/about.html
Normal 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 & 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><input type=button></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 α 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>
|
||||
BIN
src/compiler/www/minibar/align-btn.png
Normal file
|
After Width: | Height: | Size: 138 B |
BIN
src/compiler/www/minibar/brushed-metal.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
57
src/compiler/www/minibar/example.html
Normal 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>
|
||||
44
src/compiler/www/minibar/feedback.cgi
Normal 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="<- 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
|
||||
49
src/compiler/www/minibar/feedback.html
Normal 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>
|
||||
259
src/compiler/www/minibar/minibar-api.html
Normal 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>
|
||||
<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/support.js"></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><div id="minibar"></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>
|
||||
<script type="text/JavaScript" src="minibar_input.js"></script>
|
||||
<script type="text/JavaScript" src="minibar_support.js"></script>
|
||||
<script type="text/JavaScript" src="../js/support.js"></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>
|
||||
<script type="text/JavaScript" src="minibar_translations.js"></script>
|
||||
<script type="text/JavaScript" src="minibar_support.js"></script>
|
||||
<script type="text/JavaScript" src="../js/support.js"></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>
|
||||
93
src/compiler/www/minibar/minibar.css
Normal 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; }
|
||||
52
src/compiler/www/minibar/minibar.html
Normal 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>
|
||||
& <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>
|
||||
377
src/compiler/www/minibar/minibar.js
Normal 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
|
||||
*/
|
||||
562
src/compiler/www/minibar/minibar_input.js
Normal 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;
|
||||
}
|
||||
67
src/compiler/www/minibar/minibar_offline.html
Normal 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>
|
||||
18
src/compiler/www/minibar/minibar_offline.js
Normal 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);
|
||||
4
src/compiler/www/minibar/minibar_offline.manifest
Normal file
@@ -0,0 +1,4 @@
|
||||
CACHE MANIFEST
|
||||
# 12
|
||||
NETWORK:
|
||||
*
|
||||
40
src/compiler/www/minibar/minibar_online.js
Normal 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);
|
||||
117
src/compiler/www/minibar/minibar_support.js
Normal 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)
|
||||
}
|
||||
332
src/compiler/www/minibar/minibar_translations.js
Normal 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
|
||||
}
|
||||
117
src/compiler/www/minibar/pgf_offline.js
Normal 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("");
|
||||
}
|
||||
59
src/compiler/www/minibar/phrasebook.html
Normal 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>
|
||||
|
||||
|
||||
|
||||
30
src/compiler/www/minibar/saldotest.html
Normal 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>
|
||||
340
src/compiler/www/minibar/saldotest.js
Normal 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=" ";
|
||||
//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(" ");
|
||||
}
|
||||
BIN
src/compiler/www/minibar/tree-btn.png
Normal file
|
After Width: | Height: | Size: 149 B |
4
src/compiler/www/robots.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
User-agent: *
|
||||
Disallow: /grammars
|
||||
Disallow: /robust
|
||||
Disallow: /*.pgf
|
||||
2
src/compiler/www/syntax-editor/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
html:
|
||||
pandoc --from=markdown+pipe_tables --to=html --output=about.html README.md
|
||||
76
src/compiler/www/syntax-editor/README.md
Normal 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>
|
||||
129
src/compiler/www/syntax-editor/about.html
Normal 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>"http://localhost:41296/grammars/Foods.pgf"</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>["Eng","Swe","Mlt"]</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">"editor"</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td align="left"><code>initial.abstr</code></td>
|
||||
<td align="left">Initial abstract tree (as string), e.g. <code>"Pred (That Fish) Expensive"</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>
|
||||
|
||||
|
||||
366
src/compiler/www/syntax-editor/ast.js
Normal 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;
|
||||
}
|
||||
|
||||
140
src/compiler/www/syntax-editor/editor.css
Normal 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; }
|
||||
46
src/compiler/www/syntax-editor/editor.html
Normal 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>
|
||||
588
src/compiler/www/syntax-editor/editor.js
Normal 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);
|
||||
}
|
||||
197
src/compiler/www/syntax-editor/editor_menu.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/compiler/www/syntax-editor/editor_online.js
Normal 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);
|
||||
|
||||
83
src/compiler/www/translator/about.html
Normal 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 &
|
||||
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>
|
||||
110
src/compiler/www/translator/index.html
Normal 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>
|
||||
20
src/compiler/www/translator/overlay.css
Normal 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;*/
|
||||
}
|
||||
114
src/compiler/www/translator/translator.css
Normal 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");
|
||||
}
|
||||
1321
src/compiler/www/translator/translator.js
Normal file
138
src/compiler/www/wc.html
Normal 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>
|
||||