mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-04-28 14:02:50 -06:00
1271 lines
35 KiB
JavaScript
1271 lines
35 KiB
JavaScript
|
||
|
||
/* --- Translator object ---------------------------------------------------- */
|
||
|
||
function Translator() {
|
||
var t=this
|
||
|
||
t.local=appLocalStorage("gf.translator.")
|
||
t.view=element("document")
|
||
t.filebox=element("filebox")
|
||
if(!supports_html5_storage()) {
|
||
var warning=span_class("error",text("It appears that localStorage is unsupported or disabled in this browser. Local documents will not be preserved after you leave or reload this page!"))
|
||
insertAfter(warning,t.view)
|
||
}
|
||
if(!supports_local_files()) {
|
||
var item=element("import_globalsight")
|
||
if(item) item.style.display="none"
|
||
}
|
||
t.servers={}; //The API is stateful, use one pgf_online object per grammar
|
||
t.grammar_info={};
|
||
pgf_online({}).get_grammarlist(bind(t.extend_methods,t))
|
||
|
||
function update_language_menu(t,id) {
|
||
var dl=element(id);
|
||
clear(dl);
|
||
for(var i in languages) {
|
||
var l=languages[i]
|
||
dl.appendChild(dt(radiobutton(id,l.code,l.name,bind(t.change,t))))
|
||
}
|
||
}
|
||
|
||
update_language_menu(t,"source")
|
||
update_language_menu(t,"target")
|
||
if(window.apertium) t.add_apertium()
|
||
//initialize_sorting(["TR"],["segment"])
|
||
t.document=empty_document();
|
||
t.current=t.local.get("current")
|
||
if(t.current && t.current!="/") {
|
||
if(t.local.get("current_in_cloud")) t.open_from_cloud(t.current)
|
||
else t.open(t.current)
|
||
}
|
||
}
|
||
|
||
Translator.prototype.redraw=function() {
|
||
var t=this;
|
||
if(t.current=="/") t.browse()
|
||
else {
|
||
t.drawing=t.draw_document()
|
||
clear(t.view)
|
||
appendChildren(t.view,t.drawing.doc)
|
||
var o=t.document.options
|
||
var form=document.forms.options
|
||
update_radiobutton(form,"method",o.method)
|
||
update_radiobutton(form,"source",o.from)
|
||
update_radiobutton(form,"target",o.to)
|
||
update_radiobutton(form,"view",o.view || "segmentbysegment")
|
||
update_checkbox("cloud",o.cloud || false)
|
||
t.update_language_menus()
|
||
t.update_translations()
|
||
t.update_edit_menu()
|
||
}
|
||
}
|
||
|
||
Translator.prototype.switch_grammar=function(grammar,cont) {
|
||
var t=this
|
||
var gi=t.grammar_info[grammar]
|
||
if(gi) cont(gi)
|
||
else {
|
||
var pgf=t.servers[grammar]
|
||
if(pgf) pgf.waiting.push(cont)
|
||
else {
|
||
pgf=t.servers[grammar]=pgf_online({grammar_list:[grammar]})
|
||
pgf.waiting=[cont]
|
||
function cont2(gr_info) {
|
||
t.grammar_info[grammar]=gr_info
|
||
//console.log("Passing grammar info for "+grammar+" to "+pgf.waiting.length+" clients")
|
||
while(pgf.waiting.length>0) pgf.waiting.pop()(gr_info)
|
||
}
|
||
function cont1() { pgf.grammar_info(cont2)}
|
||
pgf.switch_grammar(grammar,cont1)
|
||
}
|
||
}
|
||
}
|
||
|
||
Translator.prototype.update_edit_menu=function() {
|
||
var t=this
|
||
function set_class(id,name) {
|
||
var e=element(id)
|
||
if(e) e.className=name
|
||
}
|
||
|
||
var able= t.document.globalsight ? "disabled" : ""
|
||
set_class("edit_import",able)
|
||
set_class("edit_add_segment",able)
|
||
set_class("edit_remove_segment",able)
|
||
}
|
||
|
||
Translator.prototype.update_language_menus=function() {
|
||
var t=this
|
||
var o=t.document.options
|
||
var method=o.method+(o.method=="Apertium"?"/"+o.from:"")
|
||
if(t.menus_for==method) return;
|
||
t.menus_for=method
|
||
function mark_menu(name,supported) {
|
||
var menu=document.forms.options[name]
|
||
for(var i=0;i<menu.length;i++)
|
||
menu[i].parentNode.parentNode.className=
|
||
(supported(menu[i].value)?"":"un")+"supported"
|
||
}
|
||
function mark_menus(ssupport,tsupport) {
|
||
mark_menu("source",ssupport)
|
||
mark_menu("target",tsupport)
|
||
}
|
||
function yes(code) { return true; }
|
||
function no(code) { return false; }
|
||
switch(o.method) {
|
||
case "Manual":
|
||
mark_menus(yes,yes)
|
||
break;
|
||
case "GFRobust":
|
||
if(window.gftranslate) gftranslate.get_support(mark_menus)
|
||
else mark_menus(no,no)
|
||
break;
|
||
case "Apertium":
|
||
function ssupport(code) {
|
||
return apertium.isTranslatable(alangcode(code))
|
||
}
|
||
function tsupport(code) {
|
||
return apertium.isTranslatablePair(alangcode(o.from),alangcode(code))
|
||
}
|
||
if(window.apertium) mark_menus(ssupport,tsupport)
|
||
else mark_menus(no,no)
|
||
break;
|
||
default: // GF
|
||
function cont() {
|
||
function gfsupport(code) { return t.gf_supported(o.method,code) }
|
||
mark_menus(gfsupport,gfsupport)
|
||
}
|
||
t.switch_grammar(o.method,cont)
|
||
}
|
||
}
|
||
|
||
Translator.prototype.gf_supported=function(grammar,langcode) {
|
||
var t=this;
|
||
var concname=t.grammar_info[grammar].name+langcode
|
||
var l=t.grammar_info[grammar].languages
|
||
for(var i in l) if(l[i].name==concname) return true
|
||
return false
|
||
}
|
||
|
||
Translator.prototype.update_translations=function() {
|
||
var t=this
|
||
for(var i in t.document.segments) t.update_translation(i)
|
||
}
|
||
|
||
Translator.prototype.update_translation=function(i) {
|
||
var t=this
|
||
var doc=t.document
|
||
var o=doc.options
|
||
var ss=doc.segments
|
||
var ds=t.drawing.segments
|
||
var ts=t.drawing.targets
|
||
var segment=ss[i]
|
||
|
||
function draw_update() {
|
||
if(ds) {
|
||
var sd=t.draw_segment(segment,i)
|
||
var old=ds[i]
|
||
ds[i]=sd
|
||
replaceNode(sd,old)
|
||
}
|
||
else if(ts) {
|
||
clear(ts[i])
|
||
ts[i].appendChild(qtext(segment.target))
|
||
ts[i].appendChild(text(" "))
|
||
}
|
||
}
|
||
function update_segment(m,txts) {
|
||
segment.target=txts[0];
|
||
segment.options={method:m,from:o.from,to:o.to} // no sharing!
|
||
if(txts.length>1) segment.choices=txts
|
||
else delete segment.choices
|
||
draw_update()
|
||
}
|
||
function update_apertium_translation() {
|
||
var afrom=alangcode(o.from), ato=alangcode(o.to)
|
||
|
||
function upd3(txts) { update_segment("Apertium",txts) }
|
||
|
||
function upd3s(txt) { upd3([{quality:"error",text:txt}]) }
|
||
|
||
function upd1(res) {
|
||
//console.log(translate_output)
|
||
if(res.translation) upd3([res.translation])
|
||
else upd3s("["+res.error.message+"]")
|
||
}
|
||
|
||
function upd0(source) { apertium.translate(source,afrom,ato,upd1) }
|
||
|
||
if(!window.apertium)
|
||
upd3s("[Apertium is not available]")
|
||
else if(apertium.isTranslatablePair(afrom,ato)) {
|
||
if(!eq_options(segment.options,o)) upd0(segment.source)
|
||
}
|
||
else
|
||
upd3s("[Apertium does not support "+show_translation(o)+"]")
|
||
}
|
||
function update_gfrobust_translation() {
|
||
function upd3(txts) {
|
||
update_segment("GFRobust",txts.map(trans_text_quality))
|
||
}
|
||
|
||
function upd3s(txt) {
|
||
update_segment("GFRobust",[{quality:"error",text:txt}])
|
||
}
|
||
|
||
function upd2(trans,punct) {
|
||
if(trans.length==0) upd3s("[no translation]")
|
||
else if(trans[0].error)
|
||
upd3s("[GF robust translation problem: "+trans[0].error+"]")
|
||
else {
|
||
var ts=[]
|
||
for(var i=0;i<trans.length;i++) {
|
||
var t=trans[i].linearizations[0].text
|
||
if(punct) t=t+" "+punct
|
||
if(!elem(t,ts)) ts.push(t)
|
||
}
|
||
//mapc(unlextext,ts,upd3)
|
||
upd3(ts)
|
||
}
|
||
}
|
||
function upd0(source,punct) {
|
||
function upd1(translate_output) {
|
||
//console.log(translate_output)
|
||
upd2(translate_output,punct)
|
||
}
|
||
gftranslate.translate(source,o.from,o.to,0,10,upd1)
|
||
}
|
||
if(!window.gftranslate)
|
||
upd3s("[GF robust parser is not available]")
|
||
else {
|
||
function check_support(ssupport,tsupport) {
|
||
var fls=ssupport(o.from)
|
||
var tls=tsupport(o.to)
|
||
if(fls && tls) {
|
||
var want={from:o.from, to:o.to, method:"GFRobust"}
|
||
if(!eq_options(segment.options,want)) {
|
||
//console.log("Updating "+i)
|
||
//lexgfrobust(segment.source,upd0)
|
||
upd0(segment.source,"")
|
||
}
|
||
//else console.log("No update ",want,segment.options)
|
||
}
|
||
else {
|
||
var fn=" from "+concname(o.from)
|
||
var tn=" to "+concname(o.to)
|
||
var msg="The GF robust translation service: not supported:"
|
||
if(!fls) msg+=fn+(tls ? "." : ", ")
|
||
if(!tls) msg+=tn+"."
|
||
upd3s("["+msg+"]")
|
||
}
|
||
}
|
||
gftranslate.get_support(check_support)
|
||
}
|
||
}
|
||
|
||
function update_gf_translation(grammar,gfrom,gto) {
|
||
var server=t.servers[grammar]
|
||
function upd3(txts) { update_segment(grammar,txts) }
|
||
function upd3s(txt) { upd3([{quality:"error",text:txt}]) }
|
||
function upd2(ts) {
|
||
switch(ts.length) {
|
||
case 0: upd3s("[no translation]");break;
|
||
default:
|
||
//mapc(unlextext,ts,upd3);
|
||
upd3(ts)
|
||
break;
|
||
}
|
||
}
|
||
function upd1(translate_output) {
|
||
//console.log(translate_output)
|
||
upd2(collect_texts(translate_output[0].translations))
|
||
}
|
||
function upd0(source) {
|
||
server.translate({from:gfrom,to:gto,lexer:"text",unlexer:"text",
|
||
input:source},upd1)
|
||
}
|
||
var fls=t.gf_supported(grammar,o.from)
|
||
var tls=t.gf_supported(grammar,o.to)
|
||
if(fls && tls) {
|
||
var want={from:o.from, to:o.to, method:grammar}
|
||
if(!eq_options(segment.options,want)) {
|
||
//console.log("Updating "+i)
|
||
//lextext(segment.source,upd0)
|
||
upd0(segment.source)
|
||
}
|
||
//else console.log("No update ",want,segment.options)
|
||
}
|
||
else {
|
||
var fn=concname(o.from)
|
||
var tn=concname(o.to)
|
||
var sup="supported by the grammar"
|
||
var isnot=" is not "+sup
|
||
var is=" is "+sup
|
||
var msg= fls ? tn+isnot : tls ? fn+isnot :
|
||
"Neither "+fn+" nor "+tn+is
|
||
upd3s("["+msg+"]")
|
||
}
|
||
}
|
||
|
||
var m=segment_method(doc,ss[i])
|
||
switch(m) {
|
||
case "Manual": /* Nothing to do */ break;
|
||
case "Apertium": update_apertium_translation(); break;
|
||
case "GFRobust": update_gfrobust_translation(); break;
|
||
default: // GF
|
||
function upd00(grammar_info) {
|
||
var gname=grammar_info.name
|
||
var gfrom=gname+o.from, gto=gname+o.to
|
||
update_gf_translation(m,gfrom,gto)
|
||
}
|
||
t.switch_grammar(m,upd00)
|
||
}
|
||
}
|
||
|
||
Translator.prototype.add_apertium=function() {
|
||
var dl=element("methods")
|
||
if(dl) add_apertium_to_menu(dl,bind(this.change,this))
|
||
}
|
||
|
||
function add_apertium_to_menu(dl,action) {
|
||
dl.appendChild(dt(radiobutton("method","Apertium","Apertium",action)))
|
||
}
|
||
|
||
Translator.prototype.extend_methods=function(grammars) {
|
||
this.grammars=grammars
|
||
var dl=element("methods")
|
||
if(dl) this.extend_methods_menu(dl,bind(this.change,this))
|
||
update_radiobutton(document.forms.options,"method",this.document.options.method)
|
||
this.redraw()
|
||
}
|
||
|
||
Translator.prototype.extend_methods_menu=function(dl,action) {
|
||
var grammars=this.grammars
|
||
if(dl) for(var i in grammars) {
|
||
dl.appendChild(dt(radiobutton("method",grammars[i],
|
||
"GF: "+grammars[i],
|
||
action)))
|
||
}
|
||
}
|
||
|
||
Translator.prototype.change=function(el) {
|
||
var t=this
|
||
var o=t.document.options;
|
||
function update(field) {
|
||
if(el.value!=o[field]) {
|
||
o[field]=el.value
|
||
t.redraw()
|
||
}
|
||
}
|
||
function update_checkbox(field) {
|
||
if(el.checked!=o[field]) {
|
||
o[field]=el.checked
|
||
t.redraw()
|
||
}
|
||
}
|
||
switch(el.name) {
|
||
case "method": update("method"); break;
|
||
case "source": update("from"); break;
|
||
case "target": update("to"); break;
|
||
case "view" : update("view"); break;
|
||
case "cloud" : update_checkbox("cloud"); break;
|
||
}
|
||
}
|
||
|
||
Translator.prototype.new=function(el) {
|
||
hide_menu(el);
|
||
this.current=null;
|
||
this.local.put("current",null)
|
||
this.document=empty_document()
|
||
this.redraw();
|
||
}
|
||
|
||
Translator.prototype.browse=function(el) {
|
||
hide_menu(el);
|
||
var t=this
|
||
function ls(files,open,del) {
|
||
var ul=empty_class("ul","files")
|
||
for(var i in files) {
|
||
var name=files[i]
|
||
var link=a(jsurl(open+"('"+name+"')"),[text(name)])
|
||
ul.appendChild(li(deletable(del(name),link)))
|
||
}
|
||
return ul
|
||
}
|
||
function delete_local(name) {
|
||
return function() {
|
||
if(confirm("Are you sure you want to delete the local document "+name+"?")) {
|
||
t.local.remove("/"+name)
|
||
browse();
|
||
}
|
||
}
|
||
}
|
||
function delete_from_cloud(name) {
|
||
return function() {
|
||
if(confirm("Are you sure you want to delete the cloud document "+name+"?")) {
|
||
gfcloud("rm",{file:name+cloudext},browse)
|
||
}
|
||
}
|
||
}
|
||
function browse() {
|
||
var list=empty("div")
|
||
clear(t.filebox)
|
||
t.filebox.appendChild(list)
|
||
list.appendChild(wrap("h2",text("Open...")))
|
||
var files=t.local.ls("/")
|
||
if(files.length>0) {
|
||
list.appendChild(wrap("h3",text("Local documents")))
|
||
list.appendChild(ls(files,"translator.open",delete_local))
|
||
}
|
||
function lscloud(result) {
|
||
var filenames=JSON.parse(result)
|
||
var files=map(strip_cloudext,filenames)
|
||
if(files.length>0) {
|
||
list.appendChild(wrap("h3",[text("Documents in the cloud "),
|
||
img("../P/cloud.png")]))
|
||
list.appendChild(ls(files,"translator.open_from_cloud",delete_from_cloud))
|
||
}
|
||
}
|
||
if(navigator.onLine) gfcloud("ls",{ext:cloudext},lscloud)
|
||
/*
|
||
t.current="/"
|
||
t.local.put("current","/")
|
||
*/
|
||
|
||
t.filebox.appendChild(button("Cancel",function () { t.hide_filebox() }))
|
||
t.show_filebox()
|
||
}
|
||
setTimeout(browse,100) // leave time to hide the menu first
|
||
}
|
||
|
||
Translator.prototype.open=function(name) {
|
||
var t=this
|
||
if(name) {
|
||
var path="/"+name
|
||
var document=t.local.get(path);
|
||
if(document) t.load(name,document,false)
|
||
else alert("No such document: "+name)
|
||
}
|
||
}
|
||
|
||
Translator.prototype.show_filebox=function() {
|
||
this.filebox.parentNode.style.display="block";
|
||
}
|
||
|
||
Translator.prototype.hide_filebox=function() {
|
||
var t=this
|
||
t.filebox.parentNode.style.display="";
|
||
clear(t.filebox)
|
||
}
|
||
|
||
Translator.prototype.load=function(name,document,in_cloud) {
|
||
var t=this
|
||
t.current=name;
|
||
t.current_in_cloud=in_cloud;
|
||
t.local.put("current",name)
|
||
t.local.put("current_in_cloud",in_cloud)
|
||
t.document=document;
|
||
t.hide_filebox();
|
||
t.redraw();
|
||
}
|
||
|
||
Translator.prototype.open_from_cloud=function(name) {
|
||
var t=this
|
||
var filename=name+cloudext
|
||
function ok(result) {
|
||
var document=JSON.parse(result)
|
||
if(document) t.load(name,document,true)
|
||
}
|
||
gfcloud("download",{file:encodeURIComponent(filename)},ok);
|
||
}
|
||
|
||
Translator.prototype.save=function(el) {
|
||
var t=this
|
||
hide_menu(el);
|
||
if(t.current!="/") {
|
||
if(t.current) {
|
||
var path="/"+t.current
|
||
if(t.document.options.cloud) {
|
||
function done() {
|
||
//t.local.remove(path)
|
||
t.local.put("current_in_cloud",true)
|
||
t.local.put("current",t.current)
|
||
}
|
||
save_in_cloud(t.current+cloudext,t.document,done)
|
||
}
|
||
else {
|
||
t.local.put(path,t.document)
|
||
t.local.put("current",t.current)
|
||
}
|
||
}
|
||
else t.saveAs()
|
||
}
|
||
}
|
||
|
||
Translator.prototype.saveAs=function(el) {
|
||
var t=this
|
||
hide_menu(el);
|
||
if(t.current!="/") {
|
||
var name=prompt("File name?")
|
||
if(name) {
|
||
t.current=t.document.name=name;
|
||
t.local.put("current",name)
|
||
t.save();
|
||
t.redraw();
|
||
}
|
||
}
|
||
}
|
||
|
||
Translator.prototype.close=function(el) {
|
||
hide_menu(el);
|
||
this.browse();
|
||
}
|
||
/*
|
||
Translator.prototype.add_segment1=function(el) {
|
||
hide_menu(el);
|
||
var t=this
|
||
function imp() {
|
||
var text=prompt("Text segment to import?")
|
||
if(text) {
|
||
t.document.segments.push(new_segment(text))
|
||
t.redraw();
|
||
}
|
||
}
|
||
setTimeout(imp,100)
|
||
}
|
||
*/
|
||
Translator.prototype.add_segment=function(el) {
|
||
var t=this
|
||
if(t.document.globalsight) return
|
||
hide_menu(el);
|
||
function imp() {
|
||
var n=t.document.segments.length
|
||
t.insert_segment(n)
|
||
}
|
||
if(t.current!="/") setTimeout(imp,100)
|
||
}
|
||
|
||
Translator.prototype.insert_segment=function(i) {
|
||
var t=this
|
||
function restore() {
|
||
t.redraw()
|
||
}
|
||
function done() {
|
||
var text=inp.value
|
||
if(text) {
|
||
var newseg=new_segment(text)
|
||
t.document.segments=insert_ix(t.document.segments,i,newseg)
|
||
}
|
||
restore()
|
||
return false
|
||
}
|
||
var inp=node("input",{name:"it",value:""})
|
||
var e=form({},[inp, submit(), button("Cancel",restore)])
|
||
var source=wrap_class("td","source",e)
|
||
var edit=wrap_class("tr","segment",[td([]),source])
|
||
|
||
var ss=t.drawing.segments
|
||
var n=ss ? ss.length : 0
|
||
if(n>0) {
|
||
if(i==0) insertBefore(edit,ss[0])
|
||
else insertAfter(edit,ss[i-1])
|
||
}
|
||
else t.view.appendChild(wrap_class("table","segments",edit))
|
||
|
||
e.onsubmit=done
|
||
inp.focus();
|
||
}
|
||
|
||
Translator.prototype.import=function(el) {
|
||
var t=this
|
||
if(t.document.globalsight) return // disabled
|
||
hide_menu(el);
|
||
function imp() {
|
||
function restore() {
|
||
t.redraw()
|
||
}
|
||
function done() {
|
||
function add_segments(text) {
|
||
var ls=window.lines(text)
|
||
var segs= punct.firstChild.checked
|
||
? split_punct(text,punctchars.value)
|
||
: paras.firstChild.checked
|
||
? join_paragraphs(ls)
|
||
: ls
|
||
for(var i in segs)
|
||
t.document.segments.push(new_segment(segs[i]))
|
||
}
|
||
function import_text(text2) {
|
||
var text=inp.value
|
||
add_segments(text2)
|
||
add_segments(text)
|
||
restore()
|
||
}
|
||
function import_file(ev) { import_text(ev.target.result) }
|
||
if(files.files && files.files.length>0) {
|
||
var file=files.files[0]
|
||
var r=new FileReader()
|
||
r.onload=import_file
|
||
r.readAsText(file)
|
||
}
|
||
else import_text("")
|
||
return false
|
||
}
|
||
var inp=node("textarea",{name:"it",value:"",rows:"10"})
|
||
var punct=radiobutton("separator","punct",
|
||
"Punctuation indicates where segments end: ",null,true)
|
||
var lines=radiobutton("separator","lines",
|
||
"Segments are separated by line breaks",null,false)
|
||
var paras=radiobutton("separator","paras",
|
||
"Segments are separated by blank lines",null,false)
|
||
var punctchars=node("input",{name:"punctchars",value:".?!",size:"5"})
|
||
var lang=concname(t.document.options.from)
|
||
var e=form({class:"import"},
|
||
[wrap("h3",text("Import text ("+lang+")")),
|
||
inp,
|
||
wrap("dl",[dt([punct,punctchars]),dt(lines),dt(paras)]),
|
||
submit(), button("Cancel",restore)])
|
||
|
||
if(supports_local_files()) {
|
||
// Allow import from local files, if the browers supports it.
|
||
var files=node("input",{name:"files","type":"file"})
|
||
var extra=wrap("label",[text("Choose a file: "),
|
||
files,
|
||
text(" and/or enter text to import below.")])
|
||
e.insertBefore(extra,inp)
|
||
}
|
||
t.view.appendChild(e)
|
||
e.onsubmit=done
|
||
inp.focus();
|
||
}
|
||
if(t.current!="/") setTimeout(imp,100)
|
||
}
|
||
|
||
Translator.prototype.export_globalsight=function() {
|
||
t=this
|
||
|
||
/*
|
||
Taken from this discussion on saving to local files:
|
||
http://stackoverflow.com/questions/2897619/using-html5-javascript-to-generate-and-save-a-file
|
||
*/
|
||
open("data:application/octet-stream,"
|
||
+encodeURIComponent(export_globalsight_download_file(t.document)),
|
||
t.document.name
|
||
/*,'toolbar=no,location=no,status=no'*/);
|
||
|
||
/*
|
||
Also consider: FileSaver saveAs()
|
||
|
||
Not directly support in browsers:
|
||
http://www.w3.org/TR/file-writer-api/#the-filesaver-interface
|
||
|
||
Alternative implementation: FileSaver.js:
|
||
https://github.com/eligrey/FileSaver.js
|
||
http://eligrey.com/blog/post/saving-generated-files-on-the-client-side
|
||
*/
|
||
}
|
||
|
||
Translator.prototype.import_globalsight=function(el) {
|
||
hide_menu(el);
|
||
var t=this
|
||
function imp() {
|
||
function done() {
|
||
function import_text(name,text) {
|
||
var ls=lines(text)
|
||
if(ls.length>0 && ls[0]=="# GlobalSight Download File") {
|
||
t.current=null;
|
||
t.local.put("current",null)
|
||
t.document=import_globalsight_download_file(ls)
|
||
t.current=t.document.name=name
|
||
t.hide_filebox();
|
||
t.redraw();
|
||
}
|
||
else alert("Not a GlobalSight Download File") // !! improve
|
||
}
|
||
function import_file(name,ev) { import_text(name,ev.target.result) }
|
||
if(files.files && files.files.length>0) {
|
||
var file=files.files[0]
|
||
var r=new FileReader()
|
||
r.onload=function(ev) { import_file(file.name,ev); }
|
||
r.readAsText(file)
|
||
}
|
||
return false
|
||
}
|
||
|
||
clear(t.filebox)
|
||
t.filebox.appendChild(wrap("h2",text("Import")))
|
||
function restore() { t.hide_filebox() }
|
||
var cancel_button=button("Cancel",restore)
|
||
if(supports_local_files()) {
|
||
// Allow import from local files, if the browers supports it.
|
||
var files=node("input",{name:"files","type":"file"})
|
||
var inp=wrap("p",wrap("label",[text("Choose a file: "),files]))
|
||
var e=form({class:"import"},
|
||
[wrap("h3",text("Import a GlobalSight Download File")),
|
||
inp, submit(), cancel_button])
|
||
t.filebox.appendChild(e)
|
||
e.onsubmit=done
|
||
}
|
||
else {
|
||
t.filebox.appendChild(wrap("p",[text("Support for readling local files is missing in this browser.")]))
|
||
t.filebox.appendChild(cancel_button)
|
||
}
|
||
t.show_filebox();
|
||
}
|
||
setTimeout(imp,100) // leave time to hide the menu first
|
||
}
|
||
|
||
Translator.prototype.remove=function(el) {
|
||
var t=this
|
||
if(t.document.globalsight) return
|
||
hide_menu(el);
|
||
function rm() {
|
||
if(t.document && t.document.segments.length>0) {
|
||
t.document.segments.pop();
|
||
t.redraw();
|
||
}
|
||
}
|
||
if(t.current!="/") setTimeout(rm,100)
|
||
}
|
||
|
||
Translator.prototype.remove_segment=function(i) {
|
||
var t=this
|
||
if(t.document && t.document.segments.length>i) {
|
||
t.document.segments=delete_ix(t.document.segments,i)
|
||
t.redraw();
|
||
}
|
||
}
|
||
|
||
Translator.prototype.replace_segment=function(i,sd) {
|
||
var t=this;
|
||
var ds=t.drawing.segments;
|
||
|
||
if(ds) {
|
||
var old=ds[i]
|
||
ds[i]=sd
|
||
replaceNode(sd,old)
|
||
}
|
||
}
|
||
|
||
Translator.prototype.pick_translation=function(i,txt) {
|
||
var t=this
|
||
var s=t.document.segments[i]
|
||
set_manual(s)
|
||
s.target=txt // side effect, updating the document in-place
|
||
t.replace_segment(i,t.draw_segment(s,i))
|
||
}
|
||
|
||
Translator.prototype.edit_source=function(source,i) {
|
||
var t=this
|
||
var doc=t.document
|
||
var s=doc.segments[i]
|
||
|
||
function restore() { t.replace_segment(i,t.draw_segment(s,i)) }
|
||
function change(str) {
|
||
s.source=str // side effect, updating the document in-place
|
||
restore();
|
||
if(s.options.method!="Manual") {
|
||
s.options.to="" // hack to force an update
|
||
t.update_translation(i)
|
||
}
|
||
}
|
||
function done() { change(inp.value); return false; }
|
||
|
||
function goto_minibar() {
|
||
function cont(grammar_info) {
|
||
var gname=grammar_info.name
|
||
var gfrom=gname+doc.options.from
|
||
var gto=gname+doc.options.to
|
||
var pgf_server=t.servers[grammarname]
|
||
function cont2(source) {
|
||
function ok() {
|
||
function cont(input) { unlextext(input,change) }
|
||
minibar.get_current_input(cont)
|
||
t.hide_filebox()
|
||
}
|
||
function cancel() {
|
||
restore()
|
||
t.hide_filebox()
|
||
}
|
||
var minibar_options= {
|
||
startcat_menu: false,
|
||
random_button: false,
|
||
try_google: false,
|
||
show_abstract: true,
|
||
show_trees: true,
|
||
show_grouped_translations: false,
|
||
word_replacements: true,
|
||
default_source_language: "Eng",
|
||
initial_grammar: pgf_server.current_grammar_url,
|
||
initial:{from:gfrom,
|
||
startcat:grammar_info.startcat,
|
||
input:source.split(" ")},
|
||
initial_toLangs: [gfrom,gto]
|
||
}
|
||
clear(t.filebox)
|
||
appendChildren(t.filebox,[empty_id("div","minibar"),
|
||
empty_id("div","syntax_editor")])
|
||
var minibar=new Minibar(pgf_server,minibar_options)
|
||
appendChildren(t.filebox,[button("OK",ok),
|
||
button("Cancel",cancel)])
|
||
t.show_filebox()
|
||
}
|
||
lextext(s.source,cont2)
|
||
|
||
}
|
||
t.switch_grammar(grammarname,cont)
|
||
}
|
||
|
||
var inp=node("input",{name:"it",value:s.source})
|
||
var e=form({},[inp, submit(), button("Cancel",restore),text(" ")])
|
||
var grammarname=uses_gf(doc,s)
|
||
if(grammarname) e.appendChild(button("Minibar",goto_minibar))
|
||
replaceChildren(source,e)
|
||
e.onsubmit=done
|
||
inp.focus()
|
||
}
|
||
|
||
Translator.prototype.edit_translation=function(i) {
|
||
var t=this
|
||
var s=t.document.segments[i]
|
||
|
||
function restore() { t.replace_segment(i,t.draw_segment(s,i)) }
|
||
function done() {
|
||
set_manual(s)
|
||
s.options.from=t.document.options.from
|
||
s.options.to=t.document.options.to
|
||
s.target={quality:"manual_quality",text:inp.value}
|
||
// side effect, updating the document in-place
|
||
restore();
|
||
return false;
|
||
}
|
||
|
||
var inp=node("input",{name:"it",value:qstring(s.target)})
|
||
var e=form({},[inp, submit(), button("Cancel",restore)])
|
||
var target=wrap_class("td","target",e)
|
||
var edit=t.draw_segment_given_target(s,target,i)
|
||
t.replace_segment(i,edit)
|
||
e.onsubmit=done
|
||
inp.focus();
|
||
}
|
||
|
||
function hide_menu(el) {
|
||
function disp(s) { el.parentNode.style.display=s; }
|
||
if(el) {
|
||
disp("none")
|
||
setTimeout(function(){disp("")},500)
|
||
}
|
||
}
|
||
|
||
/* --- Documents ------------------------------------------------------------ */
|
||
|
||
/*
|
||
type Document = { name:String, options:DocOptions, segments:[Segment],
|
||
globalsight : GlobalSight|null }
|
||
type Segment = { source:String, target:QText, options:Options }
|
||
& ( {} | { choices:[QText] } )
|
||
type QText = { quality:Quality, text:String }
|
||
| String // mostly for backward compatibility
|
||
type Quality = "default_quality" | "low_quality" | "high_quality"
|
||
| "manual_quality"
|
||
type DocOptions = Options & { view:View, cloud:Bool }
|
||
type Options = {from: Lang, to: Lang, method:Method}
|
||
type Lang = String // Eng, Swe, Ita, etc
|
||
type Method = "Manual" | "Apertium" | "GFRobust" | GFGrammarName
|
||
type View = "segmentbysegment" | "paralleltexts"
|
||
type GFGrammarName = String // e.g. "Foods.pgf"
|
||
|
||
type GlobalSight = { header: [String], segments:[[String]] }
|
||
*/
|
||
|
||
function eq_options(o1,o2) {
|
||
return o1.method==o2.method && o1.from==o2.from && o1.to==o2.to
|
||
}
|
||
|
||
Translator.prototype.draw_document=function() {
|
||
var t=this
|
||
var doc=t.document
|
||
var o=doc.options;
|
||
function draw_globalsight() {
|
||
function gs_export() { t.export_globalsight(); }
|
||
return doc.globalsight
|
||
? wrap("span",[text(" (from GlobalSight) "),
|
||
button("Export",gs_export)])
|
||
: text("")
|
||
}
|
||
var hdr=wrap("h2",[text(doc.name),text(" "),
|
||
wrap("small",[draw_translation(o),draw_globalsight()])])
|
||
if(doc.options.cloud) insertFirst(hdr,img("../P/cloud.png"))
|
||
switch(o.view || "segmentbysegment") {
|
||
case "paralleltexts":
|
||
function src(seg) { return seg.source }
|
||
function trg(seg) { return seg.target }
|
||
function fmt(txt,i) {
|
||
var sd=span_class("segment",[qtext(txt),text(" ")])
|
||
function setclass(cls) {
|
||
return function() {
|
||
targets[i].className=sources[i].className=cls
|
||
}
|
||
}
|
||
sd.onmouseover=setclass("current_segment")
|
||
sd.onmouseout=setclass("segment")
|
||
return sd
|
||
}
|
||
var sources=mapix(fmt,map(src,doc.segments))
|
||
var targets=mapix(fmt,map(trg,doc.segments))
|
||
var drawing=[hdr,wrap_class("table","paralleltexts",
|
||
tr([wrap_class("td","source",sources),
|
||
wrap_class("td","target",targets)]))]
|
||
return {doc:drawing,sources:sources,targets:targets}
|
||
default:
|
||
var segments=mapix(bind(t.draw_segment,t),doc.segments)
|
||
var drawing=[hdr,wrap_class("table","segments",segments)]
|
||
return {doc:drawing,segments:segments}
|
||
}
|
||
}
|
||
|
||
Translator.prototype.draw_segment=function(s,i) {
|
||
var t=this
|
||
var dopt=t.document.options
|
||
var opt=s.options
|
||
var txt=qtext(s.target)
|
||
if(opt.from!=dopt.from || opt.to!=dopt.to) txt=span_class("error",txt)
|
||
var target=wrap_class("td","target",txt)
|
||
function edit() { t.edit_translation(i) }
|
||
function pick(txt) { t.pick_translation(i,txt) }
|
||
target.onclick=edit
|
||
if(s.choices) appendChildren(target,draw_choices(s.choices,pick))
|
||
return t.draw_segment_given_target(s,target,i)
|
||
}
|
||
|
||
function qtext(t) {
|
||
switch(typeof(t)) {
|
||
case "string": return text(t)
|
||
default: return span_class(t.quality||"",text(t.text))
|
||
}
|
||
}
|
||
|
||
function qstring(t) {
|
||
switch(typeof(t)) {
|
||
case "string": return t
|
||
default: return t.text
|
||
}
|
||
}
|
||
|
||
function draw_choices(txts,onclick) {
|
||
function opt(txt) {
|
||
var o=dt(qtext(txt))
|
||
o.onclick=function(ev) { ev.stopPropagation(); onclick(txt) }
|
||
return o
|
||
}
|
||
return [span_class("choices",text("+")),
|
||
wrap_class("dl","popupmenu",map(opt,txts))]
|
||
}
|
||
|
||
Translator.prototype.draw_segment_given_target=function(s,target,i) {
|
||
var t=this
|
||
|
||
function draw_options2() {
|
||
var o=s.options
|
||
function change(el) {
|
||
s.use_default = el.value=="Default"
|
||
var m = s.use_default ? t.document.options.method : el.value
|
||
if(m!=o.method) {
|
||
o.method=m // side effect, updating the document in-place
|
||
o.to="" // hack to force an update
|
||
//console.log("Method changed to "+m)
|
||
t.update_translation(i)
|
||
}
|
||
}
|
||
var autoB=radiobutton("method","Default","Default",change)
|
||
var manualB=radiobutton("method","Manual","Manual",change)
|
||
var gfrobustB=radiobutton("method","GFRobust","GF Wide Coverage Translation",change)
|
||
var dl=wrap_class("dl","popupmenu",
|
||
[dt(autoB),
|
||
dt([manualB,text(" "),draw_translation(o)]),
|
||
dt(gfrobustB)])
|
||
if(window.apertium) add_apertium_to_menu(dl,change)
|
||
t.extend_methods_menu(dl,change)
|
||
var form=wrap("form",dl)
|
||
var d = s.use_default
|
||
var m = d || d==null ? "Default" : o.method || "Default"
|
||
update_radiobutton(form,"method",m)
|
||
return form
|
||
}
|
||
function draw_options() {
|
||
return wrap("div",[span_class("arrow",hovertext(" ⇒ ")),draw_options2()])
|
||
}
|
||
if(t.document.globalsight)
|
||
var actions=empty_class("td","actions")
|
||
else {
|
||
var insertB=dt(text("Insert a new segment"))
|
||
insertB.onclick=function() { t.insert_segment(i) }
|
||
var removeB=dt(text("Remove this segment"))
|
||
removeB.onclick=function() { t.remove_segment(i) }
|
||
var actions=wrap_class("td","actions",
|
||
wrap("div",[span_class("actions",hovertext("◊")),
|
||
wrap_class("dl","popupmenu",
|
||
[insertB,removeB])]))
|
||
}
|
||
var txt=wrap("span",text(s.source))
|
||
var source=wrap_class("td","source",txt)
|
||
if(!t.document.globalsight)
|
||
txt.onclick=function() { t.edit_source(source,i); }
|
||
/*
|
||
if(window.gftranslate && segment_method(t.document,s)=="GFRobust") {
|
||
function add_button(src) {
|
||
var btn=img("../minibar/tree-btn.png")
|
||
btn.className="right"
|
||
btn.other=gftranslate.parsetree_url(src)
|
||
btn.title="Click to toggle parse tree view."
|
||
btn.onclick=function() { toggle_img(btn) }
|
||
source.appendChild(btn)
|
||
}
|
||
lexgfrobust(s.source,add_button)
|
||
}
|
||
*/
|
||
var options=wrap_class("td","options",draw_options())
|
||
|
||
return node("tr",{"class":"segment",id:i},[actions,source,options,target])
|
||
}
|
||
|
||
/* --- Document constructor ------------------------------------------------- */
|
||
|
||
function empty_document() {
|
||
return { name:"Unnamed",
|
||
options: {from:"Eng", to:"Swe", method:"Manual"},
|
||
segments:[] }
|
||
}
|
||
|
||
/* --- Segments ------------------------------------------------------------- */
|
||
|
||
function new_segment(source) {
|
||
return { source:source, target:"", options:{} } // leave options empty
|
||
}
|
||
|
||
function set_manual(segment) {
|
||
segment.options.method="Manual"
|
||
segment.use_default=false
|
||
}
|
||
|
||
// Return the name of the grammar if the segment uses GF for translation
|
||
function uses_gf(doc,segment) {
|
||
var m= segment_method(doc,segment)
|
||
return /\.pgf$/.test(m) ? m : null
|
||
}
|
||
|
||
// Return the translation method use by a segment
|
||
function segment_method(doc,segment) {
|
||
var m= segment.options.method || doc.options.method
|
||
var d=segment.use_default
|
||
if(d || d==null) m=doc.options.method
|
||
return m
|
||
}
|
||
|
||
/* -------------------------------------------------------------------------- */
|
||
|
||
function draw_translation(o) { return text(show_translation(o)) }
|
||
|
||
function show_translation(o) {
|
||
return "("+concname(o.from||"?")+" → "+concname(o.to||"?")+")"
|
||
}
|
||
|
||
/* --- Auxiliary functions -------------------------------------------------- */
|
||
|
||
// Collect alternative texts in the output from PGF service translate command
|
||
function collect_texts(ts) {
|
||
var list=[]
|
||
for(var i in ts)
|
||
for(var j in ts[i].linearizations) {
|
||
var t=ts[i].linearizations[j].text
|
||
if(!elem(t,list)) list.push(t) // avoid duplicates
|
||
}
|
||
return list
|
||
}
|
||
|
||
function mapix(f,xs) {
|
||
var ys=[];
|
||
for(var i in xs) ys.push(f(xs[i],i))
|
||
return ys;
|
||
}
|
||
|
||
function delete_ix(old,ix) {
|
||
var a=[];
|
||
for(var i in old) if(i!=ix) a.push(old[i]);
|
||
return a;
|
||
}
|
||
|
||
function insert_ix(old,ix,x) {
|
||
var a=[];
|
||
var done=false;
|
||
for(var i in old) {
|
||
if(i==ix) { a.push(x); done=true; }
|
||
a.push(old[i])
|
||
}
|
||
if(!done) a.push(x)
|
||
return a;
|
||
}
|
||
|
||
// Convert array of lines to array of paragraphs
|
||
function join_paragraphs(lines) {
|
||
var paras=[]
|
||
var current="";
|
||
for(var i in lines)
|
||
if(lines[i]=="") paras.push(current),current=""
|
||
else current+=" "+lines[i]
|
||
if(current) paras.push(current)
|
||
return paras
|
||
}
|
||
|
||
function split_punct(text,punct) {
|
||
var ss=text.split(new RegExp("(["+punct+"])"))
|
||
var segs=[];
|
||
for(var i=0;i<ss.length;i+=2) segs.push((ss[i]+(ss[i+1]||"")).trim())
|
||
if(segs.length>0 && segs[segs.length-1]=="") segs.pop();
|
||
return segs
|
||
}
|
||
|
||
// Break a string into lines
|
||
function lines(text) {
|
||
var ls=text.split("\r\n") // DOS
|
||
if(ls.length<=1) ls=text.split(/\n|\r/) // Unix | Mac
|
||
return ls
|
||
}
|
||
|
||
function supports_local_files() {
|
||
// See http://www.html5rocks.com/en/tutorials/file/dndfiles/
|
||
return window.File && window.FileList && window.FileReader
|
||
}
|
||
|
||
/* --- GlobalSight support -------------------------------------------------- */
|
||
|
||
function import_globalsight_download_file(ls) {
|
||
var doc=empty_document()
|
||
doc.globalsight={header:[],segments:[]}
|
||
var i=0;
|
||
|
||
// Scan header and pick up source & target locale
|
||
for(;i<ls.length && ls[i][0]=="#";i++) {
|
||
doc.globalsight.header.push(ls[i])
|
||
var hdr=ls[i].split(":");
|
||
if(hdr.length==2) {
|
||
function setlang(opt) {
|
||
var code2=hdr[1].trim().split("_")[0] // pick en from en_UK
|
||
var code3=langcode3[code2]
|
||
if(code3) doc.options[opt]=code3;
|
||
// Should notify the user about unsupported language codes!!
|
||
}
|
||
switch(hdr[0]) {
|
||
case "# Source Locale": setlang("from"); break;
|
||
case "# Target Locale": setlang("to");break;
|
||
}
|
||
}
|
||
}
|
||
// skip blank lines
|
||
for(;i<ls.length && ls[i].length==0;i++)
|
||
doc.globalsight.header.push(ls[i])
|
||
|
||
while(i<ls.length) {
|
||
var seghdr=[]
|
||
// skip segment header
|
||
for(;i<ls.length && ls[i][0]=="#";i++) seghdr.push(ls[i])
|
||
var seg=""
|
||
// extract segment text
|
||
for(;i<ls.length && ls[i][0]!="#";i++)
|
||
if(ls[i]!="") seg = seg=="" ? ls[i] : seg+" "+ls[i];
|
||
if(seg!="") doc.segments.push(new_segment(seg.trim()))
|
||
doc.globalsight.segments.push(seghdr)
|
||
}
|
||
return doc;
|
||
}
|
||
|
||
function export_globalsight_download_file(doc) {
|
||
var ls=[].concat(doc.globalsight.header)
|
||
|
||
var gs=doc.globalsight.segments
|
||
var ss=doc.segments
|
||
for(var i=0;i<gs.length && i<ss.length;i++)
|
||
ls=ls.concat(gs[i],qstring(ss[i].target))
|
||
|
||
ls.push("")
|
||
ls.push("# END GlobalSight Download File")
|
||
ls.push("")
|
||
return ls.join("\n")
|
||
}
|
||
|
||
|
||
/* --- Cloud Support -------------------------------------------------------- */
|
||
|
||
|
||
var cloudext=".gfstdoc"
|
||
function strip_cloudext(s) { return s.substr(0,s.length-cloudext.length) }
|
||
|
||
function save_in_cloud(filename,document,cont) {
|
||
function save(dir) {
|
||
var form={dir:dir}
|
||
form[filename]=JSON.stringify(document);
|
||
ajax_http_post("/cloud","command=upload"+encodeArgs(form),cont)
|
||
}
|
||
with_dir(save)
|
||
}
|
||
|
||
/*
|
||
// Like lextext, but separate punctuation from the end
|
||
function lexgfrobust(txt,cont) {
|
||
function rmpunct(txt) {
|
||
var ts=gf_lex(txt)
|
||
var n=ts.length
|
||
var punct=""
|
||
if(n>0 && /[.?!]/.test(ts[n-1])) { punct=ts[n-1]; ts.pop()}
|
||
cont(gf_unlex(ts),punct)
|
||
}
|
||
lextext(txt,rmpunct)
|
||
}
|
||
*/
|
||
/* --- DOM Support ---------------------------------------------------------- */
|
||
|
||
function a(url,linked) { return node("a",{href:url},linked); }
|
||
function jsurl(js) { return "javascript:"+js; }
|
||
|
||
function replaceNode(node,ref) { ref.parentNode.replaceChild(node,ref) }
|
||
|
||
function radiobutton(name,value,label,change,checked) {
|
||
var b=node("input",{type:"radio",name:name,value:value})
|
||
if(change) b.onchange=function(event) { return change(event.target) }
|
||
if(checked) b.checked=true
|
||
return wrap("label",[b,text(label)])
|
||
}
|
||
|
||
function update_radiobutton(form,name,value) {
|
||
var bs=form[name]
|
||
if(bs.length)
|
||
for(var i in bs) if(bs[i].value==value) bs[i].checked=true
|
||
}
|
||
|
||
function update_checkbox(name,checked) {
|
||
document.forms.options[name].checked=checked
|
||
}
|
||
|
||
function form(attrs,fields) { return node("form",attrs,fields) }
|
||
|
||
function submit(label) {
|
||
return node("input",{type:"submit",value:label||"OK"})
|
||
}
|
||
|
||
|
||
function deletable(del,el,hint) {
|
||
var b=delete_button(del,hint)
|
||
return node("span",{"class":"deletable"},[b,el])
|
||
}
|
||
|
||
function delete_button(action,hint) {
|
||
var b=node("span",{"class":"delete",title:hint || "Delete"},[text("×")])
|
||
b.onclick=action;
|
||
return b;
|
||
}
|
||
|
||
function hovertext(txt) {
|
||
return node("span",{onclick:""},[text(txt)])
|
||
}
|