/* --- Translator object ---------------------------------------------------- */ function Translator() { this.local=tr_local(); this.view=element("document") if(!supports_html5_storage()) { var warning=span_class("error",text("It appears that localStorage is unsupported or disabled in this browser. Documents will not be preserved after you leave or reload this page!")) insertAfter(warning,this.view) } this.current=this.local.get("current") this.document=this.current && this.current!="/" && this.local.get("/"+this.current) || empty_document() this.server=pgf_online({}) this.server.get_grammarlist(bind(this.extend_methods,this)) update_language_menu(this,"source") update_language_menu(this,"target") this.redraw(); } function update_language_menu(t,id) { var dl=element(id); clear(dl); for(var i in languages) { var l=languages[i] dl.appendChild(wrap("dt",radiobutton(id,l.code,l.name,bind(t.change,t)))) } } 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 update_radiobutton("method",o.method) update_radiobutton("source",o.from) update_radiobutton("target",o.to) update_radiobutton("view",o.view || "segmentbysegment") if(o.method!="Manual") { function cont2(gr_info) { t.grammar_info=gr_info t.update_translations() } function cont1() { t.server.grammar_info(cont2)} t.server.switch_grammar(o.method,cont1) } else t.update_translations() } } Translator.prototype.update_translations=function() { 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 function supported(concname) { var l=t.grammar_info.languages for(var i in l) if(l[i].name==concname) return true return false } function update_translation(i) { var segment=ss[i] function replace() { 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(text(segment.target+" ")) } } function upd3(txts) { segment.target=txts[0]; segment.options={method:o.method,from:o.from,to:o.to} // no sharing! if(txts.length>1) segment.choices=txts else delete segment.choices replace() } function upd2(ts) { function unlex(txt,cont) { gfshell('ps -unlextext "'+txt+'"',cont) } switch(ts.length) { case 0: upd3(["[no translation]"]);break; default: mapc(unlex,ts,upd3); break; } } function upd1(translate_output) { //console.log(translate_output) upd2(collect_texts(translate_output[0].translations)) } function upd0(source) { t.server.translate({from:gfrom,to:gto,input:source},upd1) } var fls=supported(gfrom) var tls=supported(gto) if(fls && tls) { if(segment.options.method!="Manual" && !eq_options(segment.options,o)) gfshell('ps -lextext "'+segment.source+'"',upd0) } else { var fn=concname(o.from) var tn=concname(o.to) var unsup=" is not supported by the grammar" var sup=" is supported by the grammar" var msg= fls ? tn+unsup : tls ? fn+unsup : "Neither "+fn+" nor "+tn+sup upd3("["+msg+"]") } } if(doc.options.method!="Manual") { var gname=t.grammar_info.name var gfrom=gname+o.from var gto=gname+o.to for(var i in ss) update_translation(i) } } Translator.prototype.extend_methods=function(grammars) { this.grammars=grammars var dl=element("methods") if(dl) for(var i in grammars) { dl.appendChild(wrap("dt",radiobutton("method",grammars[i], "GF: "+grammars[i], bind(this.change,this)))) } update_radiobutton("method",this.document.options.method) } 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() } } switch(el.name) { case "method": update("method"); break; case "source": update("from"); break; case "target": update("to"); break; case "view": update("view"); 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 browse() { var files=t.local.ls("/") var ul=empty_class("ul","files") for(var i in files) { var name=files[i] var link=a(jsurl("translator.open('"+name+"')"),[text(name)]) ul.appendChild(li(link)) } clear(t.view) t.view.appendChild(wrap("h2",text("Your translator documents"))) t.view.appendChild(ul) t.current="/" t.local.put("current","/") } 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.current=name; t.local.put("current",name) t.document=document; t.redraw(); } else alert("No such document: "+name) } } Translator.prototype.save=function(el) { hide_menu(el); if(this.current!="/") { if(this.current) this.local.put("/"+this.current,this.document) else this.saveAs() } } Translator.prototype.saveAs=function(el) { hide_menu(el); if(this.current!="/") { var name=prompt("File name?") if(name) { this.current=this.document.name=name; this.local.put("current",name) this.save(); this.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) { hide_menu(el); var t=this function imp() { function restore() { t.redraw() } function done() { var text=inp.value if(text) t.document.segments.push(new_segment(text)) restore() return false } var inp=node("input",{name:"it",value:""}) var e=wrap("form",[inp, submit(), button("Cancel",restore)]) var source=wrap_class("td","source",e) var edit=wrap_class("tr","segment",source) var ss=t.drawing.segments var n=ss ? ss.length : 0 if(n>0) insertAfter(edit,ss[n-1]) else t.view.appendChild(wrap_class("table","segments",edit)) e.onsubmit=done inp.focus(); } setTimeout(imp,100) } Translator.prototype.import=function(el) { hide_menu(el); var t=this function imp() { function restore() { t.redraw() } function done() { var text=inp.value var ls=text.split("\n") 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])) restore() 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=node("form",{class:"import"}, [wrap("h3",text("Import text ("+lang+")")), inp, wrap("dl",[dt([punct,punctchars]),dt(lines),dt(paras)]), submit(), button("Cancel",restore)]) t.view.appendChild(e) e.onsubmit=done inp.focus(); } setTimeout(imp,100) } Translator.prototype.remove=function(el) { hide_menu(el); var t=this function rm() { if(t.document && t.document.segments.length>0) { t.document.segments.pop(); t.redraw(); } } setTimeout(rm,100) } 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] s.options.method="Manual" 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 s=t.document.segments[i] function restore() { t.replace_segment(i,t.draw_segment(s,i)) } function done() { s.source=inp.value // side effect, updating the document in-place restore(); if(s.options.method!="Manual") { s.options.to="" // hack to force an update t.update_translations() } return false; } var inp=node("input",{name:"it",value:s.source}) var e=wrap("form",[inp, submit(), button("Cancel",restore)]) clear(source) source.appendChild(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() { s.options.method="Manual" s.options.from=t.document.options.from s.options.to=t.document.options.to s.target=inp.value // side effect, updating the document in-place restore(); return false; } var inp=node("input",{name:"it",value:s.target}) var e=wrap("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: Options, segments:[Segment] } type Segment = { source:String, target:String, options:Options } type Options = {from: Lang, to: Lang, method:Method} type Lang = String // Eng, Swe, Ita, etc type Method = "Manual" | GrammarName type GrammarName = String // e.g. "Foods.pgf" */ 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; var hdr=wrap("h2",[text(doc.name),text(" "), wrap("small",draw_translation(o))]) 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",text(txt+" ")) 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([td(sources),td(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=text(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 draw_choices(txts,onclick) { function opt(txt) { var o=dt(text(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(o) { function change(el) { o.method=el.value // side effect, updating the document in-place t.update_translations() } var manual=o.method=="Manual" var autoB=radiobutton("method","Auto","Auto",change,!manual) var manualB=radiobutton("method","Manual","Manual",change,manual) return wrap("form", [wrap("dt",autoB), wrap("dt",manualB), wrap("dt",draw_translation(o))]) } function draw_options(o) { return wrap("div",[span_class("arrow",text(" ⇒ ")), wrap("dl",draw_options2(o))]) } var source=wrap_class("td","source",text(s.source)) source.onclick=function() { t.edit_source(source,i); } var options=wrap_class("td","options",draw_options(s.options)) return wrap_class("tr","segment",[source,options,target]) } function empty_document() { return { name:"Unnamed", options: {from:"Eng", to:"Swe", method:"Manual"}, segments:[] } } function new_segment(source) { return { source:source, target:"", options:{} } // leave options empty } function draw_translation(o) { return text("("+concname(o.from||"?")+" → "+concname(o.to||"?")+")") } /* --- Auxiliary functions -------------------------------------------------- */ function lang(code,name) { return { code:code, name:name} } function lang1(name) { var ws=name.split("/"); return ws.length==1 ? lang(name.substr(0,3),name) : lang(ws[0],ws[1]); } var languages = map(lang1,"Amharic Arabic Bulgarian Catalan Danish Dutch English Finnish French German Hindi Ina/Interlingua Italian Japanese Latin Norwegian Polish Ron/Romanian Russian Spanish Swedish Thai Turkish Urdu".split(" ")); var langname={}; for(var i in languages) langname[languages[i].code]=languages[i].name function concname(code) { return langname[code] || code; } function tr_local() { /* function dummy() { return { get: function(name,def) { return def }, put: function(name,value) { } ls: function() { return [] } } } */ function real(storage) { var appPrefix="gf.translator." return { get: function (name,def) { var id=appPrefix+name return storage[id] ? JSON.parse(storage[id]) : def; }, put: function (name,value) { var id=appPrefix+name; storage[id]=JSON.stringify(value); }, 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 } } } return supports_html5_storage() ? real(localStorage) : real([]) } // 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; } // 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;i0 && segs[segs.length-1]=="") segs.pop(); return segs } /* --- 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(name,value) { var bs=document.forms.options[name] if(bs.length) for(var i in bs) if(bs[i].value==value) bs[i].checked=true } function submit(label) { return node("input",{type:"submit",value:label||"OK"}) }