mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-04-09 04:59:31 -06:00
Adding a Simple Translation Tool
It is part of the cloud services available with gf -server.
This commit is contained in:
3
gf.cabal
3
gf.cabal
@@ -27,6 +27,9 @@ data-files: www/index.html
|
||||
www/TransQuiz/*.css
|
||||
www/TransQuiz/*.js
|
||||
www/TransQuiz/*.png
|
||||
www/translator/*.html
|
||||
www/translator/*.css
|
||||
www/translator/*.js
|
||||
|
||||
source-repository head
|
||||
type: darcs
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<li><a href="minibar/minibar.html">Minibar</a>
|
||||
<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>
|
||||
</ul>
|
||||
|
||||
<h2>Some Documentation</h2>
|
||||
|
||||
55
src/www/translator/about.html
Normal file
55
src/www/translator/about.html
Normal file
@@ -0,0 +1,55 @@
|
||||
<!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>About the Simple Translation Tool</h1>
|
||||
|
||||
<p>
|
||||
This is a simple bilingual document editor. Documents consist of a sequence
|
||||
of segments that are translated independently. The user can add segments
|
||||
in the source language and obtain automatically translated segments in
|
||||
the target language. If an unsatisfactory automatic translation is
|
||||
obtained, the user can click on it and replace it with a manual translation.
|
||||
|
||||
<p>
|
||||
The GF web service is used for automatic translation. The user picks which
|
||||
grammar to use from a menu of available grammars. Through menu options,
|
||||
the user also sets the source and target language for the document.
|
||||
|
||||
<p>
|
||||
The tool handles a set of documents. Documents can be named, saved (locally),
|
||||
closed and reopened later.
|
||||
|
||||
<h2>TODO</h2>
|
||||
<ul>
|
||||
<li>Test for browser compatibility (Safari & Firefox tested so far).
|
||||
<li>Use GF lexer/unlexer to allow for more natural looking text.
|
||||
<li>Import/export text.
|
||||
<li>Cloud service.
|
||||
<li>Interface to other translation services.
|
||||
<li>Interface to grammar editor for grammar extension.
|
||||
<li>...
|
||||
<li>...
|
||||
</ul>
|
||||
|
||||
|
||||
<hr>
|
||||
<div class=modtime><small>
|
||||
<!-- hhmts start --> Last modified: Tue May 15 17:35:39 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>
|
||||
85
src/www/translator/index.html
Normal file
85
src/www/translator/index.html
Normal file
@@ -0,0 +1,85 @@
|
||||
<!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">
|
||||
<meta name = "viewport" content = "width = device-width">
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class=pagehead>
|
||||
<h1>Simple Translation Tool</h1>
|
||||
<form name=options>
|
||||
<table class=menubar>
|
||||
<tr><td>File
|
||||
<dl>
|
||||
<dt onclick="translator.new(this)">New
|
||||
<dt onclick="translator.browse(this)">Open...
|
||||
<dt onclick="translator.save(this)">Save
|
||||
<dt onclick="translator.saveAs(this)">Save As...
|
||||
<dt onclick="translator.close(this)">Close
|
||||
</dl>
|
||||
<td>Edit
|
||||
<dl>
|
||||
<dt onclick="translator.import(this)">Add a segment...
|
||||
<dt onclick="translator.remove(this)">Remove the last segment
|
||||
</dl>
|
||||
<td>View
|
||||
<dl>
|
||||
<dt><label><input type=radio checked>Segment by segment</label>
|
||||
</dl>
|
||||
<td>Options
|
||||
<dl>
|
||||
<dt>
|
||||
<table class=submenu>
|
||||
<tr><td>Source Language
|
||||
<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>Target Language
|
||||
<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>Default translation method
|
||||
<dl id=methods>
|
||||
<dt><label><input name=method value=Manual type=radio onchange="translator.change(this)">Manual</label>
|
||||
</dl>
|
||||
</table>
|
||||
</dl>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id=document class=document>
|
||||
...
|
||||
<noscript>This document translation editor requires JavaScript to work
|
||||
</noscript>
|
||||
</div>
|
||||
<hr>
|
||||
<div class=modtime><small>
|
||||
<!-- hhmts start --> Last modified: Tue May 15 16:17:32 CEST 2012 <!-- hhmts end -->
|
||||
</small></div>
|
||||
<a href="about.html">About</a>
|
||||
|
||||
<script type="text/javascript" src="../minibar/support.js"></script>
|
||||
<script type="text/javascript" src="../minibar/pgf_online.js"></script>
|
||||
<!--
|
||||
<script type="text/javascript" src="../gfse/cloud2.js"></script>
|
||||
-->
|
||||
<script type="text/javascript" src="translator.js"></script>
|
||||
<script type="text/javascript" >
|
||||
var translator = new Translator()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
54
src/www/translator/translator.css
Normal file
54
src/www/translator/translator.css
Normal file
@@ -0,0 +1,54 @@
|
||||
body { margin: 5px; }
|
||||
h1 { float: right; margin: 0; font-size: 150%; }
|
||||
h2 { font-size: 120%; }
|
||||
div.pagehead { font-family: sans-serif;
|
||||
background-color: #ccc;
|
||||
}
|
||||
table.menubar td { padding: 5px; }
|
||||
table.menubar dl, td.options > div > dl {
|
||||
z-index: 1;
|
||||
display: none; position: absolute;
|
||||
background: white; color: black;
|
||||
border: 1px solid black;
|
||||
margin: 0;
|
||||
box-shadow: 5px 5px 5px rgba(0,0,0,0.25);
|
||||
}
|
||||
table.menubar td:hover > dl { display: block; }
|
||||
table.menubar dt { margin: 0; padding: 5px; }
|
||||
table.submenu dt { padding: 0; }
|
||||
table.menubar td:hover, table.menubar dt:hover { background-color: #36f; color: white; }
|
||||
table table dl { left: 6em; }
|
||||
table.menubar dt { white-space: nowrap; }
|
||||
div.document {
|
||||
clear: both;
|
||||
background: white;
|
||||
border: 2px solid #009;
|
||||
padding: 0.6ex;
|
||||
}
|
||||
|
||||
div.document h2 { color: #009; }
|
||||
|
||||
table.segments { margin-left: auto; margin-right: auto; }
|
||||
tr.segment:hover { background: #ffc; }
|
||||
|
||||
td.source, td.options, td.target {
|
||||
padding: 1ex;
|
||||
}
|
||||
td.source, td.target {
|
||||
border-bottom: 2px solid #ccc;
|
||||
}
|
||||
td.options > div { position: relative; margin: 0; }
|
||||
td.options:hover > div > dl { display: block; }
|
||||
td.options > div > dl {
|
||||
left: 0.8em;
|
||||
padding: 0.6ex;
|
||||
font-family: sans-serif;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
td.target input[name=it] {
|
||||
width: 100%; font-family: inherit; font-size: inherit;
|
||||
}
|
||||
|
||||
span.arrow { color: blue; }
|
||||
span.error { color: red; }
|
||||
429
src/www/translator/translator.js
Normal file
429
src/www/translator/translator.js
Normal file
@@ -0,0 +1,429 @@
|
||||
|
||||
|
||||
/* --- Translator object ---------------------------------------------------- */
|
||||
|
||||
function Translator() {
|
||||
this.local=tr_local();
|
||||
this.view=element("document")
|
||||
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)
|
||||
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
|
||||
|
||||
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(sd) {
|
||||
var old=ds[i]
|
||||
ds[i]=sd
|
||||
replaceNode(sd,old)
|
||||
}
|
||||
function upd2(ts) {
|
||||
switch(ts.length) {
|
||||
case 1: segment.target=ts[0]; break;
|
||||
case 0: segment.target="[no translation]";break;
|
||||
default: segment.target="[ambiguous translation]"
|
||||
}
|
||||
segment.options=JSON.parse(JSON.stringify(o)) // no sharing!
|
||||
replace(t.draw_segment(segment,i))
|
||||
}
|
||||
function upd(translate_output) {
|
||||
//console.log(translate_output)
|
||||
var ts=collect_texts(translate_output[0].translations)
|
||||
upd2(ts)
|
||||
}
|
||||
var fs=supported(gfrom)
|
||||
var ts=supported(gto)
|
||||
if(fs && ts) {
|
||||
if(segment.options.method!="Manual"
|
||||
&& JSON.stringify(segment.options)!=JSON.stringify(o))
|
||||
t.server.translate({from:gfrom,to:gto,input:segment.source},upd)
|
||||
}
|
||||
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= fs ? tn+unsup : ts ? fn+unsup :
|
||||
"Neither "+fn+" nor "+tn+sup
|
||||
upd2(["["+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;
|
||||
}
|
||||
}
|
||||
|
||||
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 ul=empty_class("ul","files")
|
||||
var pre=t.local.prefix+"/"
|
||||
for(var i in localStorage) {
|
||||
if(hasPrefix(i,pre)) {
|
||||
var name=i.substr(pre.length)
|
||||
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.import=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.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.edit_translation=function(i) {
|
||||
var t=this
|
||||
var ds=t.drawing.segments
|
||||
|
||||
function replace_segment(sd) {
|
||||
var old=ds[i]
|
||||
ds[i]=sd
|
||||
replaceNode(sd,old)
|
||||
}
|
||||
|
||||
function edit_segment(s) {
|
||||
function restore() { replace_segment(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)
|
||||
replace_segment(edit)
|
||||
e.onsubmit=done
|
||||
inp.focus();
|
||||
}
|
||||
edit_segment(t.document.segments[i])
|
||||
}
|
||||
|
||||
function hide_menu(el) {
|
||||
function disp(s) { el.parentNode.style.display=s||""; }
|
||||
if(el) {
|
||||
disp("none")
|
||||
setTimeout(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"
|
||||
*/
|
||||
|
||||
Translator.prototype.draw_document=function() {
|
||||
var t=this
|
||||
var doc=t.document
|
||||
var o=doc.options;
|
||||
var segments=mapix(bind(t.draw_segment,t),doc.segments)
|
||||
var drawing=[node("h2",{},[text(doc.name),text(" "),
|
||||
wrap("small",draw_translation(o))]),
|
||||
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) }
|
||||
target.onclick=edit
|
||||
return t.draw_segment_given_target(s,target)
|
||||
}
|
||||
|
||||
Translator.prototype.draw_segment_given_target=function(s,target) {
|
||||
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))])
|
||||
}
|
||||
|
||||
return wrap_class("tr","segment",
|
||||
[wrap_class("td","source",text(s.source)),
|
||||
wrap_class("td","options",draw_options(s.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() {
|
||||
var prefix="gf.translator."
|
||||
function dummy() {
|
||||
return {
|
||||
prefix: prefix,
|
||||
get: function(name,def) { return def },
|
||||
put: function(name,value) { }
|
||||
}
|
||||
}
|
||||
function real() {
|
||||
return {
|
||||
prefix: prefix,
|
||||
get: function (name,def) {
|
||||
var id=prefix+name
|
||||
return localStorage[id] ? JSON.parse(localStorage[id]) : def;
|
||||
},
|
||||
put: function (name,value) {
|
||||
var id=prefix+name;
|
||||
localStorage[id]=JSON.stringify(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return window.localStorage ? real() : dummy()
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/* --- DOM Support ---------------------------------------------------------- */
|
||||
|
||||
function a(url,linked) { return node("a",{href:url},linked); }
|
||||
function li(xs) { return wrap("li",xs); }
|
||||
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"})
|
||||
}
|
||||
Reference in New Issue
Block a user