From 44d1a5a9f71b03d9aceeccd760a63fcdc45f8bad Mon Sep 17 00:00:00 2001 From: hallgren Date: Wed, 12 Oct 2011 17:03:54 +0000 Subject: Improvements of "gf -server" mode and related setup "gf -server" mode now contains everything needed to run the minibar and the grammar editor (including example-based grammar writing). The Setup.hs script installs the required files where gf -server can find them. These files have been moved to a new directory: src/www. The separate server program pgf-http is now obsolete. --- src/www/minibar/about.html | 180 ++++++++++++++++ src/www/minibar/align-btn.png | Bin 0 -> 138 bytes src/www/minibar/brushed-metal.png | Bin 0 -> 36233 bytes src/www/minibar/example.html | 57 ++++++ src/www/minibar/feedback.cgi | 44 ++++ src/www/minibar/feedback.html | 48 +++++ src/www/minibar/gf-web-api-examples.html | 151 ++++++++++++++ src/www/minibar/minibar-api.html | 235 +++++++++++++++++++++ src/www/minibar/minibar.css | 53 +++++ src/www/minibar/minibar.html | 41 ++++ src/www/minibar/minibar.js | 176 ++++++++++++++++ src/www/minibar/minibar_input.js | 277 +++++++++++++++++++++++++ src/www/minibar/minibar_online.js | 25 +++ src/www/minibar/minibar_support.js | 46 +++++ src/www/minibar/minibar_translations.js | 162 +++++++++++++++ src/www/minibar/pgf_offline.js | 96 +++++++++ src/www/minibar/pgf_online.js | 52 +++++ src/www/minibar/phrasebook.html | 56 +++++ src/www/minibar/saldotest.html | 30 +++ src/www/minibar/saldotest.js | 340 +++++++++++++++++++++++++++++++ src/www/minibar/support.js | 300 +++++++++++++++++++++++++++ src/www/minibar/tree-btn.png | Bin 0 -> 149 bytes 22 files changed, 2369 insertions(+) create mode 100644 src/www/minibar/about.html create mode 100644 src/www/minibar/align-btn.png create mode 100644 src/www/minibar/brushed-metal.png create mode 100644 src/www/minibar/example.html create mode 100644 src/www/minibar/feedback.cgi create mode 100644 src/www/minibar/feedback.html create mode 100644 src/www/minibar/gf-web-api-examples.html create mode 100644 src/www/minibar/minibar-api.html create mode 100644 src/www/minibar/minibar.css create mode 100644 src/www/minibar/minibar.html create mode 100644 src/www/minibar/minibar.js create mode 100644 src/www/minibar/minibar_input.js create mode 100644 src/www/minibar/minibar_online.js create mode 100644 src/www/minibar/minibar_support.js create mode 100644 src/www/minibar/minibar_translations.js create mode 100644 src/www/minibar/pgf_offline.js create mode 100644 src/www/minibar/pgf_online.js create mode 100644 src/www/minibar/phrasebook.html create mode 100644 src/www/minibar/saldotest.html create mode 100644 src/www/minibar/saldotest.js create mode 100644 src/www/minibar/support.js create mode 100644 src/www/minibar/tree-btn.png (limited to 'src/www/minibar') diff --git a/src/www/minibar/about.html b/src/www/minibar/about.html new file mode 100644 index 000000000..69fba5a9d --- /dev/null +++ b/src/www/minibar/about.html @@ -0,0 +1,180 @@ + + +About Minibar + + + + + +

About Minibar

+ +Minibar is an alternative implementation of the +GF web app +Fridge Poetry. +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 :-) + +

+Some implementation details: + +

+ +
+ + Last modified: Mon Aug 22 19:31:37 CEST 2011 + +
+TH +
+ + diff --git a/src/www/minibar/align-btn.png b/src/www/minibar/align-btn.png new file mode 100644 index 000000000..ca6a391c1 Binary files /dev/null and b/src/www/minibar/align-btn.png differ diff --git a/src/www/minibar/brushed-metal.png b/src/www/minibar/brushed-metal.png new file mode 100644 index 000000000..c2f03fe7d Binary files /dev/null and b/src/www/minibar/brushed-metal.png differ diff --git a/src/www/minibar/example.html b/src/www/minibar/example.html new file mode 100644 index 000000000..7d78a7ef6 --- /dev/null +++ b/src/www/minibar/example.html @@ -0,0 +1,57 @@ + + +PGF online server example + + + + + + + + +

PGF online server example

+ +
+Input: + + + +

+Output: + +

+ + +

Documentation

+ + +
+
+ Last modified: Wed Aug 3 16:52:51 CEST 2011 +
+
TH
+ diff --git a/src/www/minibar/feedback.cgi b/src/www/minibar/feedback.cgi new file mode 100644 index 000000000..0d382f22d --- /dev/null +++ b/src/www/minibar/feedback.cgi @@ -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 from-url <"$PATH_TRANSLATED" | plain2html + end + pageend +} + +case "$PATH_TRANSLATED" in + "") save_feedback ;; + *) view_feedback +esac diff --git a/src/www/minibar/feedback.html b/src/www/minibar/feedback.html new file mode 100644 index 000000000..94b15a482 --- /dev/null +++ b/src/www/minibar/feedback.html @@ -0,0 +1,48 @@ + + +Feedback + + + + + + + + + +

Feedback

+ +
+ + +

+ ... input: + ... + +

+

... translation: + ... + +

+

+ +

+ +

+ + + + + + + + + + + +

+ + + diff --git a/src/www/minibar/gf-web-api-examples.html b/src/www/minibar/gf-web-api-examples.html new file mode 100644 index 000000000..194967de0 --- /dev/null +++ b/src/www/minibar/gf-web-api-examples.html @@ -0,0 +1,151 @@ + + + +GF web services API examples + + + + + +

GF web services API examples

+ +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. + +

+The +GF +Web Service API page 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 +pgf_online.js. + +

+Note that pgf_online.js 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. + +

+
These boxes show what the calls look like in the JavaScript + API defined in pgf_online.js. +
These boxes show the corresponding URLs sent to the PGF server. +
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. +
+ +

Initialization

+
+
+ // Select which server and grammars to use: +
var server_options = { +
  grammars_url: "http://www.grammaticalframework.org/grammars/", +
  grammar_list: ["Foods.pgf"] // It's ok to skip this +
} +
var server = pgf_online(server_options); +
+ +

Examples

+ +
+
// Get the list of available grammars +
server.get_grammarlist(callback) +
http://localhost:41296/grammars/grammars.cgi +
["Foods.pgf","Phrasebook.pgf"] +
// Select which grammar to use +
server.switch_grammar("Foods.pgf") +
// Get list of concrete languages and other grammar info +
server.grammar_info(callback) +
http://localhost:41296/grammars/Foods.pgf +
{"name":"Foods", + "userLanguage":"FoodsEng", + "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"}, + ...] +} +
// Get a random syntax tree +
server.get_random({},callback) +
http://localhost:41296/grammars/Foods.pgf?command=random +
[{"tree":"Pred (That Pizza) (Very Boring)"}] +
// Linearize a syntax tree +
server.linearize({tree:"Pred (That Pizza) (Very Boring)",to:"FoodsEng"},callback) +
http://localhost:41296/grammars/Foods.pgf?command=linearize&tree=Pred+(That+Pizza)+(Very+Boring)&to=FoodsEng +
[{"to":"FoodsEng","text":"that pizza is very boring"}] +
server.linearize({tree:"Pred (That Pizza) (Very Boring)"},callback) +
http://localhost:41296/grammars/Foods.pgf?command=linearize&tree=Pred+(That+Pizza)+(Very+Boring) +
[{"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"}, + ... +] +
// Parse a string +
server.parse({from:"FoodsEng",input:"that pizza is very boring"},callback) +
http://localhost:41296/grammars/Foods.pgf?command=parse&input=that+pizza+is+very+boring&from=FoodsEng +
[{"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)"]}] +
// Translate to all available languages +
server.translate({from:"FoodsEng",input:"that pizza is very boring"},callback) +
... +
// Translate to one language +
server.translate({input:"that pizza is very boring", from:"FoodsEng", to:"FoodsSwe"}, callback) +
http://localhost:41296/grammars/Foods.pgf?command=translate&input=that+pizza+is+very+boring&from=FoodsEng&to=FoodsSwe +
[{"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"}]}]}] +
// Get completions (what words could come next) +
server.complete({from:"FoodsEng",input:"that pizza is very "},callback) +
http://localhost:41296/grammars/Foods.pgf?command=complete&input=that+pizza+is+very+&from=FoodsEng +
[{"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":""}] +
+
+
+ Last modified: Sun Aug 21 10:52:43 CEST 2011 +
+
TH
+ \ No newline at end of file diff --git a/src/www/minibar/minibar-api.html b/src/www/minibar/minibar-api.html new file mode 100644 index 000000000..2c5a8ecc0 --- /dev/null +++ b/src/www/minibar/minibar-api.html @@ -0,0 +1,235 @@ + + + +GF web services API examples + + + + + +

Minibar API

+ +The Minibar web app consists of the following objects: + + + +They are described below. + +

The Minibar object

+ +

+This object implements the complete Minibar web app. It is defined in +minibar.js. It also uses the Input +and Translations objects described below, and some auxiliary +functions defined in minibar_support.js +and support.js, so to use it in an +HTML file, you would normally include at least the following: + +

+<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="support.js"></script>
+
+ +

+For an example, see minibar.html. + +

Constructor

+ +var minibar=new Minibar(server,options,target) + + + +

Methods

+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. + +

The Input object

+ +This object handles user input. Text can be entered by typing or by clicking +on the "refrigerator magnets". +

+It is defined in +minibar_input.js. +It also uses some auxiliary functions defined +in minibar_support.js +and support.js, so to use it in an +HTML file, you would normally include at least the following: + +

+<script type="text/JavaScript" src="minibar_input.js"></script>
+<script type="text/JavaScript" src="minibar_support.js"></script>
+<script type="text/JavaScript" src="support.js"></script>
+
+ +

Constructor

+ +var input=new Input(server,translations,options) + + + +

Properties and user interface

+ +The input object created by the Input constructor +contains two field that the caller should add to the user interface: + + +

Methods

+ + + +

The Translations object

+ +This object display translations. It is defined in +minibar_translations.js. +It also uses some auxiliary functions defined +in minibar_support.js +and support.js, so to use it in an +HTML file, you would normally include at least the following: + +
+<script type="text/JavaScript" src="minibar_input.js"></script>
+<script type="text/JavaScript" src="minibar_support.js"></script>
+<script type="text/JavaScript" src="support.js"></script>
+
+ +

Constructor

+var translations=new Translations(server,options) + + +

Properties and user interface

+ + +The translations object created by the +Translations constructor contains two field that the caller +should add to the user interface: + + +

Methods

+ + + +
+
+ +HTML Last modified: Sun Aug 21 19:11:35 CEST 2011 + +
+ +
+TH +
diff --git a/src/www/minibar/minibar.css b/src/www/minibar/minibar.css new file mode 100644 index 000000000..4d2094066 --- /dev/null +++ b/src/www/minibar/minibar.css @@ -0,0 +1,53 @@ +body { + background: #ccc url("brushed-metal.png"); +} + +h1, h2, h3, small, th { font-family: sans-serif; } + +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 div, 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; +} + +.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; } \ No newline at end of file diff --git a/src/www/minibar/minibar.html b/src/www/minibar/minibar.html new file mode 100644 index 000000000..086b8fad6 --- /dev/null +++ b/src/www/minibar/minibar.html @@ -0,0 +1,41 @@ + + + +Minibar + + + + + + + + +

Minibar online

+
+ + + +
+ + +[About Minibar +| Original Fridge Poetry +& Translator] + + +HTML Last modified: Mon Aug 8 18:04:22 CEST 2011 + +
+TH +
+ + + + + + + + + + + diff --git a/src/www/minibar/minibar.js b/src/www/minibar/minibar.js new file mode 100644 index 000000000..778a3957b --- /dev/null +++ b/src/www/minibar/minibar.js @@ -0,0 +1,176 @@ +/* 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 + } + + // Apply supplied options + if(opts) for(var o in opts) this.options[o]=opts[o]; + + /* --- 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=empty("div"); + 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,input.main,translations.main,extra]); + append_extra_buttons(extra,options); + } + + /* --- Minibar client state initialisation ------------------------------ */ + this.grammar=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)); + } +} + +Minibar.prototype.show_grammarlist=function(grammars) { + this.grammar_menu=empty_id("select","grammar_menu"); + with(this) { + if(grammars.length>1) { + function opt(g) { return option(g,g); } + appendChildren(grammar_menu,map(opt,grammars)); + grammar_menu.onchange= + bind(function() { select_grammar(grammar_menu.value); },this); + insertFirst(menubar,grammar_menu); + insertFirst(menubar,text("Grammar: ")); + } + 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 change_grammar() { + t.server.grammar_info(bind(t.change_grammar,t)); + } + t.server.switch_grammar(grammar_name,change_grammar); +} + +Minibar.prototype.change_grammar=function(grammar_info) { + var t=this; + with(t) { + //debug("show_languages "); + grammar=grammar_info; + + input.change_grammar(grammar) + translations.change_grammar(grammar) + } +} + +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=translations.target_lang(); + var s=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() { + 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:input.current, + to:translations.to_menu.value, + translations:translations.translations}; + 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",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 +*/ diff --git a/src/www/minibar/minibar_input.js b/src/www/minibar/minibar_input.js new file mode 100644 index 000000000..8075eb8ea --- /dev/null +++ b/src/www/minibar/minibar_input.js @@ -0,0 +1,277 @@ + +/* --- Input object --------------------------------------------------------- */ + +function Input(server,translations,opts) { // Input object constructor + this.server=server; + this.translations=translations; + + // Default values for options: + this.options={ + delete_button_text: "⌫", + default_source_language: null, + random_button: true, + } + + // Apply supplied options + if(opts) for(var o in opts) this.options[o]=opts[o]; + + // User interface elements + this.main=empty("div"); + this.menus=empty("span"); + this.buttons=empty("span"); + this.surface=div_id("surface"); + this.words=div_id("words"); + this.from_menu=empty("select"); + + with(this) { + appendChildren(main,[surface,words]); + appendChildren(menus,[text(" From: "),from_menu]) + appendChildren(buttons, + [button(options.delete_button_text,bind(delete_last,this),"H"), + button("Clear",bind(clear_all,this),"L")]); + if(options.random_button) + buttons.appendChild(button("Random",bind(generate_random,this),"R")); + } + + /* --- Input client state initialization --- */ + this.current={from: null, input: ""}; + this.previous=null; + + this.from_menu.onchange=bind(this.change_language,this); +} + +Input.prototype.change_grammar=function (grammar) { + update_language_menu(this.from_menu,grammar); + set_initial_language(this.options,this.from_menu,grammar); + this.change_language(); +} + +Input.prototype.change_language=function () { + this.current.from=this.from_menu.value; + this.clear_all(); +} + + +Input.prototype.clear_all1=function() { + with(this) { + remove_typed_input(); + current.input=""; + previous=null; + surface.innerHTML=""; + translations.clear(); + } +} + +Input.prototype.clear_all=function() { + with(this) { + clear_all1(); + get_completions(); + } +} + +Input.prototype.get_completions=function() { + with(this) { + //debug("get_completions "); + words.innerHTML="..."; + server.complete({from:current.from,input:current.input}, + bind(show_completions,this)); + } +} + +Input.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*/) translations.translateFrom(current); + else translations.clear(); + 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;i0) { + 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;i0) + 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_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(random[0].text); + } + + function lin_random(abs) { + t.server.linearize({tree:abs[0].tree,to:t.current.from},show_random); + } + t.server.get_random({},lin_random); +} + +Input.prototype.add_words=function(s) { + with(this) { + var ws=s.split(" "); + for(var i=0;i0 && s2[0]==" ") s2=s2.substr(1); + surface.typed.value=s2; + } + else surface.typed.value=""; + } + get_completions(); + } +} + +Input.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); + } +} + +Input.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.clear(); + get_completions(); + } + } +} + +/* --- Auxiliary functions -------------------------------------------------- */ + +function set_initial_language(options,menu,grammar) { + if(grammar.userLanguage) menu.value=grammar.userLanguage; + else if(options.default_source_language) { + for(var i=0;i1 ? ""+cnt+" translations:": + "One translation:"))); + */ + for(p=0;p 1) tbody.appendChild(tr([td(text(lin[i].tree))])); + } + trans.appendChild(wrap("table",tbody)); + } + } + } +} + + +function abstree_button(abs) { + var i=button_img(tree_icon,"toggle_img(this)"); + i.title="Click to display abstract syntax tree" + i.other=server.current_grammar_url+"?command=abstrtree&tree="+encodeURIComponent(abs); + return i; +} + +function alignment_button(abs) { + var i=button_img(alignment_icon,"toggle_img(this)"); + i.title="Click to display word alignment" + i.other=server.current_grammar_url+"?command=alignment&tree="+encodeURIComponent(abs); + return i; +} + +function parsetree_button(abs,lang) { + var i=button_img(tree_icon,"toggle_img(this)"); + i.title="Click to display parse tree" + i.other=server.current_grammar_url + +"?command=parsetree&from="+lang+"&tree="+encodeURIComponent(abs); + return i; +} diff --git a/src/www/minibar/pgf_offline.js b/src/www/minibar/pgf_offline.js new file mode 100644 index 000000000..cd2d40b1c --- /dev/null +++ b/src/www/minibar/pgf_offline.js @@ -0,0 +1,96 @@ +// Assumes that Services.js has been loaded + +function pgf_offline(options) { + var server = { + // State variables (private): + grammars_url: "", + grammar_list: ["Foods.pgf"], + + current_grammar_url: null, + pgf : null, + + // Methods: + switch_grammar: function(grammar_url,cont) { + //debug("switch_grammar "); + var new_grammar_url=this.grammars_url+grammar_url; + 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; + cont(); + } + ajax_http_get_binary(new_grammar_url,update_pgf); + }, + get_grammarlist: function(cont) { cont([this.grammar_list]); }, + + 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;i0) + server.switch_grammar(server.grammar_list[0]); + return server; +} \ No newline at end of file diff --git a/src/www/minibar/phrasebook.html b/src/www/minibar/phrasebook.html new file mode 100644 index 000000000..78f5c353f --- /dev/null +++ b/src/www/minibar/phrasebook.html @@ -0,0 +1,56 @@ + + + +Phrasebook + + + + + + + +
+ +
+ + + +Powered by GF, +see doc. + + + + + + + + + + + + + + + + + + diff --git a/src/www/minibar/saldotest.html b/src/www/minibar/saldotest.html new file mode 100644 index 000000000..179c42e4d --- /dev/null +++ b/src/www/minibar/saldotest.html @@ -0,0 +1,30 @@ + + +Saldotest + + + + + + + + +

Vilket ord ska bort?

+
+
+ +

Hel- och halvspöke

+
+
+ +
+ +[Baserad på SALDOs nättjänster] + + +HTML Last modified: Thu May 27 14:02:42 CEST 2010 + +
TH
+ + + diff --git a/src/www/minibar/saldotest.js b/src/www/minibar/saldotest.js new file mode 100644 index 000000000..47cb95047 --- /dev/null +++ b/src/www/minibar/saldotest.js @@ -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;i0 ? " "+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;i0) { + var p=s.substr(0,s.length-1); + with_completions(p,function(c) { + if(count_wordforms(c.a)>0) trans.innerHTML+="
"+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=""; + 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 -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) +{ + 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,callback,errorcallback) { + var http=GetXmlHttpObject() + if (!http) { + var errortext="Browser does not support HTTP Request"; + if(errorcallback) errorcallback(errortext,500) + else alert(errortext) + } + else { + var statechange=function() { + if (http.readyState==4 || http.readyState=="complete") { + if(http.status<300) callback(http.responseText,http.status); + else if(errorcallback) errorcallback(http.responseText,http.status); + else alert("Request for "+url+" failed: " + +http.status+" "+http.statusText); + } + } + http.onreadystatechange=statechange; + http.open(method,url,true) + http.send(body) + } + return http +} + +function ajax_http_get(url,callback,errorcallback) { + ajax_http("GET",url,null,callback,errorcallback) +} + +function ajax_http_post(url,formdata,callback,errorcallback) { + ajax_http("POST",url,formdata,callback,errorcallback) + // See https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest#Using_FormData_objects +} + +// JSON via AJAX +function ajax_http_get_json(url,cont) { + ajax_http_get(url,function(txt) { cont(eval("("+txt+")")); }); +} + +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) { + if(sameOrigin(url)) ajax_http_get_json(url,cont); + else jsonpf(url,cont); +} + +/* --- 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=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); } + +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 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] + "
"; + } + 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