move gf.cabal and all compiler dependent files into src/compiler
@@ -0,0 +1,7 @@
|
||||
|
||||
save: save.hs
|
||||
ghc --make save.hs
|
||||
|
||||
install::
|
||||
@make save
|
||||
rsync -avz --exclude .DS_Store P *.html *.css *.js *.cgi *.manifest save www.grammaticalframework.org:/usr/local/www/GF/demos/gfse
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,44 @@
|
||||
+ Safety question before deleting a grammar
|
||||
+ Check identifier syntax
|
||||
+ Allow lincat for deleted cat to be deleted
|
||||
+ Allow lin for deleted fun to be deleted
|
||||
+ Apply category alpha conversion in concrete syntax
|
||||
+ Remove a concrete syntax
|
||||
+ Apply function alpha conversion in concrete syntax
|
||||
+ Change lhs of lins when function type is changed
|
||||
|
||||
+ Allow languages other than the ones in the given list to be added
|
||||
+ Export as plain text
|
||||
+ Allow definitions to be reordered
|
||||
|
||||
+ 1. possibility to compile the grammar set, returning a URL to a translator app
|
||||
+ 2a. possibility to import modules - resource libraries
|
||||
- 2b. possibility to import modules - user's own auxiliaries
|
||||
- 3. possibility to upload own modules
|
||||
+ 4. access to the created files in an on-line shell (making testing possible)
|
||||
- 5. rule-to-rule type checking and guidance (e.g. with library oper
|
||||
suggestions)
|
||||
+ Try grammars in the Translation Quiz
|
||||
+ Show lincat and lin before params and opers below
|
||||
+ Create a new concrete syntax by copying an existing one.
|
||||
- Easy access to compute_concrete from the editor
|
||||
- Instead of starting with an empty grammar, start a new grammar by copying
|
||||
an example.
|
||||
+ Cloning grammars
|
||||
- Allow grammars to contain a resoure module. Create the resource module by
|
||||
factoring out common parts of the concrete syntaxes.
|
||||
- Integrate example-based concrete syntax construction (using Ramona's tool)
|
||||
- Open lexicon modules? They are not installed by default!
|
||||
DictEng, MorphalouFre, DictSwe, DictTur, DictBul, DictFun, DictLav
|
||||
|
||||
+ compile only the uploaded grammar even if other grammars are present
|
||||
+ 'flags startcat' is needed for grammars with only one category (since the
|
||||
default startcat is S, even if it doesn't exist)
|
||||
|
||||
- Bug! After adding a 2nd def of a fun with a different type and then deleting
|
||||
the old fun, the corresponding lin will have the wrong lhs.
|
||||
|
||||
+ Bug! The startcat menu shows the first category by default, but the startcat
|
||||
flag is actually not set until a selection is made from the menu.
|
||||
|
||||
+ Always open a resizable text box when editing concrete syntax.
|
||||
@@ -0,0 +1,309 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>About: GF online editor for simple multilingual grammars</title>
|
||||
<link rel="stylesheet" type="text/css" href="editor.css" title="Cloud">
|
||||
<link rel="alternate stylesheet" type="text/css" href="molto.css" title="MOLTO">
|
||||
|
||||
<link rel=author href="http://www.cse.chalmers.se/~hallgren/" title="Thomas Hallgren">
|
||||
|
||||
<meta name = "viewport" content = "width = device-width">
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||||
<meta charset="UTF-8">
|
||||
|
||||
<script type="text/javascript" src="slideshow.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1><a href="../"><img src="../P/gf-cloud.png" alt="" title="GF Cloud Service"></a>GF online editor for simple multilingual grammars</h1>
|
||||
|
||||
<div class=right>
|
||||
<div class=slideshow>
|
||||
<img onload="start_slideshow(this,{delay:5,fade:0.3})" src="P/w1s.jpg" alt="[GF online editor screen shoot]">
|
||||
<img class=hidden src="P/w2s.jpg" alt="[GF online editor screen shoot]">
|
||||
<img class=hidden src="P/w3s.jpg" alt="[GF online editor screen shoot]">
|
||||
<img class=hidden src="P/w4s.jpg" alt="[GF online editor screen shoot]">
|
||||
</div>
|
||||
</div>
|
||||
<h2>Introduction</h2>
|
||||
|
||||
Traditionally, <a href="http://www.grammaticalframework.org/">GF</a>
|
||||
grammars are created in a text editor and tested in the
|
||||
GF shell. Text editors know very little (if anything) about the syntax of
|
||||
GF grammars, and thus provide little guidance for novice GF users. Also, the
|
||||
grammar author has to download and install the GF software on his/her own
|
||||
computer.
|
||||
|
||||
<p>
|
||||
In contrast, the
|
||||
<a href="."><em>GF online editor for simple multilingual grammars</em></a>
|
||||
is available online, making it easier to get started. All that
|
||||
is needed is a reasonably modern web browser. Even Android and iOS devices
|
||||
can be used.
|
||||
<p>
|
||||
The editor
|
||||
also guides the grammar author by showing a skeleton grammar file and
|
||||
hinting how the parts should be filled in. When a new part is added to the
|
||||
grammar, it is immediately checked for errors.
|
||||
|
||||
<p>
|
||||
Editing operations are accessed by clicking on editing symbols embedded
|
||||
in the grammar display:
|
||||
<span class=more>+</span>=Add an item,
|
||||
<span class=delete>×</span>=Delete an item,
|
||||
<span class=edit>%</span>=Edit an item.
|
||||
These are revealed when hovering over items. On touch devices, hovering is
|
||||
in some cases simulated by tapping, but there is also a button at the bottom
|
||||
of the display to "Enable editing on touch devices" that reveals all editing
|
||||
symbols.
|
||||
|
||||
<p>
|
||||
In spite of its name, the core of the editor runs in the web
|
||||
browser, so once you have opened the web page, you can
|
||||
<strong>continue editing</strong> grammars even while you are
|
||||
<strong>offline</strong>. Grammar compilation & testing and some
|
||||
error checking is done by the GF server and is not available while offline.
|
||||
|
||||
<h3>Current status</h3>
|
||||
|
||||
<p>
|
||||
At the moment, the editor supports only a small subset of the GF grammar
|
||||
notation.
|
||||
Proper error checking is done for abstract syntax, but not (yet) for concrete
|
||||
syntax.
|
||||
|
||||
<p>
|
||||
The grammars created with this editor always consists of one file for the
|
||||
abstract syntax, and one file for each concrete syntax.
|
||||
|
||||
<h4>Abstract syntax</h4>
|
||||
|
||||
The supported abstract syntax corresponds to context-free grammars
|
||||
(no dependent types). The definition of an abstract syntax is limited to
|
||||
<ul>
|
||||
<li>a list of inherited abstract syntaxes,
|
||||
<li>a list of <em>category names</em>,
|
||||
<var>Cat<sub>1</sub></var> ; ... ; <var>Cat<sub>n</sub></var>,
|
||||
<li>a list of <em>functions</em> of the form
|
||||
<var>Fun</var> : <var>Cat<sub>1</sub></var> -> ... ->
|
||||
<var>Cat<sub>n</sub></var>,
|
||||
<li>and a <em>start category</em>.
|
||||
</ul>
|
||||
|
||||
Available editing operations:
|
||||
<ul>
|
||||
<li>Inherited abstract syntaxes can be added and removed.
|
||||
<li>Categories can be added, removed and renamed. When renaming a category,
|
||||
occurences of it in function types will be updated accordingly.
|
||||
<li>Functions can be added, removed and edited. Concrete syntaxes are updated
|
||||
to reflect changes.
|
||||
<li>Functions can be reordered using drag-and-drop.
|
||||
</ul>
|
||||
|
||||
Error checks:
|
||||
|
||||
<ul>
|
||||
<li>Syntactically incorrect function definitions are refused.
|
||||
<li>Semantic problem such as duplicated definitions or references to
|
||||
undefined categories, are highlighted.
|
||||
</ul>
|
||||
|
||||
<h4>Concrete syntax</h4>
|
||||
|
||||
At the moment, the concrete syntax for a language <var>L</var> is limited to
|
||||
<ul>
|
||||
<li>inheriting the concrete syntax <var>G<sub>i</sub>L</var>
|
||||
for each <var>G<sub>i</sub></var> inherited by the abstract syntax.
|
||||
<li>opening a selection of the Resource Grammar Library modules
|
||||
<code>Syntax</code><var>L</var>, <code>Paradigms</code><var>L</var>,
|
||||
<code>Lexicon</code><var>L</var>, <code>Symbolic</code><var>L</var>
|
||||
and <code>Extra</code><var>L</var>,
|
||||
<li><em>linearization types</em> for the categories in the abstract syntax,
|
||||
<li><em>linearizations</em> for the functions in the abstract syntax,
|
||||
<li><em>parameter type definitions</em>,
|
||||
<var>P</var> = <var>C<sub>1</sub></var> | ... |<var>C<sub>n</sub></var>,
|
||||
<li>and <em>operation definitions</em>, <var>op</var> = <var>expr</var>,
|
||||
<var>op</var> : <var>type</var> = <var>expr</var>,
|
||||
</ul>
|
||||
|
||||
Available editing operations:
|
||||
<ul>
|
||||
<li>Modules can be added and removed from the list of opened Resource Grammar
|
||||
Library modules.
|
||||
<li>The LHSs of the linearization types and linearizations are determined by
|
||||
the abstract syntax and do not need to be entered manually.
|
||||
The RHSs can
|
||||
be edited.
|
||||
<li>Parameter types can be added, removed and edited.
|
||||
<li>Operation definitons can be added, removed and edited.
|
||||
<li>Definitions can be reordered (using drag-and-drop).
|
||||
</ul>
|
||||
Also,
|
||||
|
||||
<ul>
|
||||
<li>When a new concrete syntax is added to the grammar, a copy of the
|
||||
currently open concrete syntax is created, since copying and modifying
|
||||
is usually easier than creating something new from scratch.
|
||||
(If the abstract syntax is currently open, the new conrete syntax will
|
||||
start out empty.)
|
||||
<li>When adding a new concrete syntax, you normally pick one of the supported
|
||||
languages from a list. The language code and the file name is determined
|
||||
automatically. But you can also pick <em>Other</em> from the list and change
|
||||
the language code afterwards to add a concrete syntax for a language
|
||||
that is not in the list.
|
||||
</ul>
|
||||
|
||||
Error checks:
|
||||
<ul>
|
||||
<li>The RHSs in the concrete syntax are checked
|
||||
for syntactic correctness by the editor as they are entered.
|
||||
(TODO: the syntax of parameter types is not check at the moment.)
|
||||
<li>Duplicated definitions are highlighted. Checks for other
|
||||
semantic errors are delayed until the grammar is compiled.
|
||||
</ul>
|
||||
|
||||
<h3>Grammar views</h3>
|
||||
|
||||
The editor supports three ways to view a grammar:
|
||||
|
||||
<ul>
|
||||
<li><strong>Column view</strong>:
|
||||
this is the traditional view where you see one complete grammar module at a
|
||||
time. i.e. either the abstract syntax or one of the concrete syntaxes in the
|
||||
grammar.
|
||||
<li><strong>Matrix view</strong>:
|
||||
this presents an overview of the grammar where columns
|
||||
correspond to the abstract and concrete syntax modules and rows correspond
|
||||
to the categories and functions in the grammar.
|
||||
<li><strong>Row view</strong>:
|
||||
this view shows one function form the abstract syntax and
|
||||
the corresponding linearization functions from all the concrete syntaxes.
|
||||
</ul>
|
||||
|
||||
<h3>Compiling and testing grammars</h3>
|
||||
|
||||
When pressing the <strong>Compile</strong> button, the grammar will be compiled
|
||||
with GF, and any errors not detected by the editor will be reported.
|
||||
The grammar will also be compiled when pressing the <strong>Minibar</strong>
|
||||
and <strong>Quiz</strong> buttons before opening the grammar for testing
|
||||
in the Minibar or the Translation Quiz, respectively.
|
||||
|
||||
<h3><img class=right src="P/1307545089_weather_04.png" alt="">
|
||||
<img class=right src="P/1306856253_weather_06.png" alt="">Grammars in the
|
||||
cloud</h3>
|
||||
|
||||
While the editor normally stores grammars locally in the browser, it is also
|
||||
possible to store grammars in the cloud. Grammars can be stored in the cloud
|
||||
just for backup, or to make them accessible from multiple devices.
|
||||
|
||||
<p>
|
||||
There is no automatic synchronization between local grammars and the cloud.
|
||||
Instead, the user should press
|
||||
<img src="P/1306856253_weather_06.png" alt="[Cloud Upload]">
|
||||
to upload the grammars to the cloud, and press
|
||||
<img src="P/1307545089_weather_04.png" alt="[Cloud download]">
|
||||
to download grammars from the cloud. In both cases, complete grammars
|
||||
are copied and older versions at the destination will be overwritten.
|
||||
When a grammar is deleted, both the local copy and the copy in the cloud
|
||||
is deleted.
|
||||
|
||||
<p>
|
||||
Each device is initially assigned to its own unique cloud. Each device can thus
|
||||
have its own set of grammars that are not available on other devices. It is
|
||||
also possible to merge clouds and share a common set of grammars between
|
||||
multiple devices: when uploading grammars to the cloud, a link to this grammar
|
||||
cloud appears. Accessing this link from another device will cause the clouds of
|
||||
the two devices to be merged. After this, grammars uploaded from one of the
|
||||
devices can be downloaded on the other devices. Any number devices can join the
|
||||
same grammar cloud in this way.
|
||||
|
||||
<p>
|
||||
<strong>Note</strong> that while it is possible to copy grammars between
|
||||
multiple devices, there is no way to merge concurrent edits from multiple
|
||||
devices. If the same grammar is uploaded to the
|
||||
cloud from multiple devices, the last upload wins. Thus the current
|
||||
implementation is suitable for a single user switching between different
|
||||
devices, but not recommended for sharing grammars between multiple users.
|
||||
|
||||
<p>
|
||||
Also <strong>note</strong> that each grammar is assigned a unique identity
|
||||
when it is first created. Renaming a grammar does not change its identity.
|
||||
This means that name changes are propagated between devices like other changes.
|
||||
|
||||
<h4 id=public>Public grammars</h4>
|
||||
<blockquote>
|
||||
[October 2012: this is an <strong>experimental feature</strong> that
|
||||
might be replaced by an incompatible grammar sharing mechanism in the future.]
|
||||
</blockquote>
|
||||
<p>
|
||||
The grammar cloud also includes a list of public grammars.
|
||||
Grammars can be added to the public list by pressing the
|
||||
<strong>Publish</strong> button shown next to the grammars in the list of
|
||||
your grammars.
|
||||
|
||||
<p>
|
||||
The <strong>Publish</strong> button creates <em>copy</em> of your grammar.
|
||||
If you continue to edit your grammar, the changes will be local only.
|
||||
You can press the <strong>Publish</strong> button again to update the public
|
||||
copy.
|
||||
|
||||
<p>
|
||||
You can remove a grammar from the public list by pressing the
|
||||
<span class=delete>×</span> button next to the grammar in the public list.
|
||||
You can <em>not</em> remove grammars published by other users.
|
||||
|
||||
<p>
|
||||
When you open a public grammar published by another user, a <em>copy</em> of
|
||||
the grammar is added to the list of your grammars. Any changes will be made in
|
||||
your own copy of the grammar. If you publish your copy of the grammar, it will
|
||||
appear separately in the list of public grammars. You can not
|
||||
overwrite grammars published by other users, even if they have the same name.
|
||||
|
||||
<p>
|
||||
TODO: Publishing grammars that inherit form other grammars is not recommended.
|
||||
There is no way to indicate which of several grammars with the same name is
|
||||
being inherited.
|
||||
|
||||
<p>
|
||||
TODO: There should probably be a way to identify who published a grammar and
|
||||
when. Maybe the publish button should be restricted to registered users...
|
||||
|
||||
|
||||
<h3>Example-based grammar writing</h3>
|
||||
|
||||
We experimented with this in 2011. It is currently not included, but it
|
||||
might return in a future version...
|
||||
|
||||
<h3>Future work</h3>
|
||||
|
||||
This prototype gives an idea of how a web based GF grammar editor could work.
|
||||
While this editor is implemented in JavaScript and runs in the web browser,
|
||||
we do not expect to create a full implementation of GF that runs in the
|
||||
web browser, but let the editor communicate with a server running GF.
|
||||
<p>
|
||||
By developing a GF server with an appropriate API, it should
|
||||
be possible to extend the editor to support a larger fragment of GF,
|
||||
to do proper error checking and make more of the existing GF shell functionality
|
||||
accessible directly from the editor.
|
||||
<p>
|
||||
The current grammar cloud service is very primitive. In particular, it is not
|
||||
suitable for multiple users developing a grammar in collaboration.
|
||||
|
||||
<h3>Related documents</h3>
|
||||
<ul>
|
||||
<li><a href="http://www.grammaticalframework.org/~hallgren/Talks/GF/gf-ide.html">GF Grammar Development Tools</a>. Slides from a presentation at the MOLTO meeting in Göteborg, March 2011.
|
||||
<li><a href="http://www.grammaticalframework.org/grammar-ide/">The GF Grammar IDE</a>. MOLTO deliverable.
|
||||
<li><a href="http://www.grammaticalframework.org/compiler-api">The GF Grammar
|
||||
Compiler API</a>. MOLTO deliverable.
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
<div class=modtime><small>
|
||||
<!-- hhmts start -->Last modified: Tue Oct 9 16:52:47 CEST 2012 <!-- hhmts end -->
|
||||
</small></div>
|
||||
<address>
|
||||
<a href="http://www.cse.chalmers.se/~hallgren/">TH</a>
|
||||
<img src="http://www.altocumulus.org/~hallgren/online.cgi?icon" alt="">
|
||||
</address>
|
||||
</body> </html>
|
||||
@@ -0,0 +1,148 @@
|
||||
|
||||
function with_dir(cont) {
|
||||
var dir=local.get("dir","");
|
||||
if(dir) cont(dir);
|
||||
else ajax_http_get("upload.cgi?dir",
|
||||
function(dir) {
|
||||
local.put("dir",dir);
|
||||
cont(dir);
|
||||
});
|
||||
}
|
||||
|
||||
function remove_cloud_grammar(g) {
|
||||
var dir=local.get("dir")
|
||||
if(dir && g.unique_name) {
|
||||
var path=dir+"/"+g.unique_name+".json"
|
||||
ajax_http_get("upload.cgi?rm="+encodeURIComponent(path),debug);
|
||||
}
|
||||
}
|
||||
|
||||
// Upload the grammar to the server and check it for errors
|
||||
function upload(g) {
|
||||
function upload2(dir) {
|
||||
var form=node("form",{method:"post",action:"upload.cgi"+dir},
|
||||
[hidden(g.basename+".gf",show_abstract(g))])
|
||||
for(var i in g.concretes)
|
||||
form.appendChild(hidden(g.basename+g.concretes[i].langcode+".gf",
|
||||
show_concrete(g)(g.concretes[i])));
|
||||
editor.appendChild(form);
|
||||
form.submit();
|
||||
form.parentNode.removeChild(form);
|
||||
}
|
||||
|
||||
with_dir(upload2);
|
||||
}
|
||||
|
||||
// Upload the grammar to store it in the cloud
|
||||
function upload_json(cont) {
|
||||
function upload3(resptext,status) {
|
||||
local.put("json_uploaded",Date.now());
|
||||
//debug("Upload complete")
|
||||
if(cont) cont();
|
||||
else {
|
||||
var sharing=element("sharing");
|
||||
if(sharing) sharing.innerHTML=resptext;
|
||||
}
|
||||
}
|
||||
function upload2(dir) {
|
||||
var prefix=dir.substr(10)+"-" // skip "/tmp/gfse."
|
||||
//debug("New form data");
|
||||
//var form=new FormData(); // !!! Doesn't work on Android 2.2!
|
||||
var form="",sep="";
|
||||
//debug("Preparing form data");
|
||||
for(var i=0;i<local.count;i++) {
|
||||
var g=local.get(i,null);
|
||||
if(g) {
|
||||
if(!g.unique_name) {
|
||||
g.unique_name=prefix+i;
|
||||
save_grammar(g)
|
||||
}
|
||||
//form.append(g.unique_name+".json",JSON.stringify(g));
|
||||
form+=sep+encodeURIComponent(g.unique_name+".json")+"="+
|
||||
encodeURIComponent(JSON.stringify(g))
|
||||
sep="&"
|
||||
}
|
||||
}
|
||||
//debug("Upload to "+prefix);
|
||||
ajax_http_post("upload.cgi"+dir,form,upload3,cont)
|
||||
}
|
||||
|
||||
with_dir(upload2);
|
||||
}
|
||||
|
||||
function download_json() {
|
||||
var dir=local.get("dir");
|
||||
var index=grammar_index();
|
||||
var downloading=0;
|
||||
|
||||
function get_list(ok,err) {
|
||||
ajax_http_get("upload.cgi?ls="+dir,ok,err);
|
||||
}
|
||||
|
||||
function get_file(file,ok,err) {
|
||||
downloading++;
|
||||
ajax_http_get("upload.cgi?download="+encodeURIComponent(dir+"/"+file),ok,err);
|
||||
}
|
||||
|
||||
function file_failed(errormsg,status) {
|
||||
debug(errormsg)
|
||||
downloading--;
|
||||
}
|
||||
function file_downloaded(grammar) {
|
||||
downloading--;
|
||||
var newg=JSON.parse(grammar);
|
||||
debug("Downloaded "+newg.unique_name)
|
||||
var i=index[newg.unique_name];
|
||||
if(i!=undefined) merge_grammar(i,newg)
|
||||
else {
|
||||
debug("New")
|
||||
newg.index=null;
|
||||
save_grammar(newg);
|
||||
}
|
||||
if(downloading==0) done()
|
||||
}
|
||||
|
||||
function done() {
|
||||
setTimeout(function(){location.href="."},2000);
|
||||
}
|
||||
|
||||
function download_files(ls) {
|
||||
local.put("current",0);
|
||||
if(ls) {
|
||||
//debug("Downloading "+ls);
|
||||
var files=ls.split(" ");
|
||||
cleanup_deleted(files);
|
||||
for(var i in files) get_file(files[i],file_downloaded,file_failed);
|
||||
}
|
||||
else {
|
||||
debug("No grammars in the cloud")
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
get_list(download_files);
|
||||
}
|
||||
|
||||
function link_directories(newdir,cont) {
|
||||
with_dir(function(olddir) {
|
||||
ajax_http_get("upload.cgi?rmdir="+olddir+"&newdir="+newdir,cont)
|
||||
})
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
// Send a command to the GF shell
|
||||
function gfshell(cmd,cont) {
|
||||
alert("gfshell(...) not implmemented!!!")
|
||||
}
|
||||
|
||||
// Check the syntax of an expression
|
||||
function check_exp(s,cont) {
|
||||
function check(gf_message) {
|
||||
//debug("cc "+s+" = "+gf_message);
|
||||
cont(/(parse|syntax) error/.test(gf_message) ? "syntax error" : null);
|
||||
}
|
||||
if(navigator.onLine)
|
||||
ajax_http_get("upload.cgi?cc="+encodeURIComponent(s),check)
|
||||
else cont(null)
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
|
||||
function with_dir(cont) {
|
||||
function have_dir(dir) {
|
||||
var unique_id=local.get("unique_id")
|
||||
if(!unique_id) {
|
||||
unique_id=dir.substr(10) // skip "/tmp/gfse."
|
||||
local.put("unique_id",unique_id)
|
||||
}
|
||||
cont(dir,unique_id)
|
||||
}
|
||||
var dir=local.get("dir","");
|
||||
if(/^\/tmp\//.test(dir)) have_dir(dir);
|
||||
else ajax_http_get("/new",
|
||||
function(dir) {
|
||||
local.put("dir",dir);
|
||||
have_dir(dir);
|
||||
});
|
||||
}
|
||||
|
||||
function remove_cloud_grammar(g) {
|
||||
var dir=local.get("dir")
|
||||
if(dir && g.unique_name) {
|
||||
var path=g.unique_name+".json"
|
||||
gfcloud("rm",{file:path},debug);
|
||||
}
|
||||
}
|
||||
|
||||
// Upload the grammar to the server and check it for errors
|
||||
function old_upload(g) {
|
||||
function upload2(dir) {
|
||||
var form=node("form",{method:"post",action:"/cloud"},
|
||||
[hidden("dir",dir),hidden("command","make"),
|
||||
hidden(g.basename+".gf",show_abstract(g))])
|
||||
//var files = [g.basename+".gf"]
|
||||
for(var i in g.concretes) {
|
||||
var cname=g.basename+g.concretes[i].langcode+".gf";
|
||||
//files.push(cname);
|
||||
form.appendChild(hidden(cname,
|
||||
show_concrete(g)(g.concretes[i])));
|
||||
}
|
||||
editor.appendChild(form);
|
||||
form.submit();
|
||||
form.parentNode.removeChild(form);
|
||||
}
|
||||
|
||||
function upload3(message) { if(message) alert(message); }
|
||||
|
||||
with_dir(upload2)
|
||||
}
|
||||
|
||||
// Upload the grammar to the server and check it for errors
|
||||
function upload_grammars(gs,cont) {
|
||||
function upload2(dir) {
|
||||
var pre="dir="+encodeURIComponent(dir)
|
||||
var form= {command:"make"}
|
||||
for(var aix in gs) {
|
||||
var g=gs[aix]
|
||||
form[encodeURIComponent(g.basename+".gf")]=show_abstract(g)
|
||||
var cnc=g.concretes
|
||||
for(var i in cnc) {
|
||||
var cname=g.basename+cnc[i].langcode+".gf";
|
||||
form[encodeURIComponent(cname)]=show_concrete(g)(cnc[i]);
|
||||
}
|
||||
}
|
||||
ajax_http_post("/cloud",pre+encodeArgs(form),upload3)
|
||||
}
|
||||
|
||||
function upload3(json) {
|
||||
var res=JSON.parse(json)
|
||||
if(cont) cont(res)
|
||||
else alert(res.errorcode+"\n"+res.command+"\n\n"+res.output);
|
||||
}
|
||||
|
||||
if(navigator.onLine) with_dir(upload2)
|
||||
else cont({errorcode:"Offline",command:"",output:""})
|
||||
}
|
||||
|
||||
function assign_unique_name(g,unique_id) {
|
||||
if(!g.unique_name) {
|
||||
g.unique_name=unique_id+"-"+g.index;
|
||||
save_grammar(g)
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
// Upload all grammars to the cloud
|
||||
function upload_json(cont) {
|
||||
function upload2(dir,unique_id) {
|
||||
function upload3(resptext,status) {
|
||||
local.put("json_uploaded",Date.now());
|
||||
//debug("Upload complete")
|
||||
if(cont) cont();
|
||||
else {
|
||||
var sharing=element("sharing");
|
||||
if(sharing) {
|
||||
if(status==204) {
|
||||
var a=empty("a");
|
||||
a.href="share.html#"+dir.substr(5) // skip "/tmp/"
|
||||
a.innerHTML=a.href;
|
||||
sharing.innerHTML="";
|
||||
sharing.appendChild(text("Use the following link for shared access to your grammars from multiple devices: "))
|
||||
sharing.appendChild(a)
|
||||
}
|
||||
else
|
||||
sharing.innerHTML=resptext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//debug("New form data");
|
||||
//var form=new FormData(); // !!! Doesn't work on Android 2.2!
|
||||
var form={dir:dir};
|
||||
//debug("Preparing form data");
|
||||
for(var i=0;i<local.count;i++) {
|
||||
var g=local.get(i,null);
|
||||
if(g) {
|
||||
g=assign_unique_name(g,unique_id)
|
||||
//form.append(g.unique_name+".json",JSON.stringify(g));
|
||||
form[encodeURIComponent(g.unique_name+".json")]=JSON.stringify(g)
|
||||
}
|
||||
}
|
||||
ajax_http_post("/cloud","command=upload"+encodeArgs(form),upload3,cont)
|
||||
}
|
||||
|
||||
with_dir(upload2);
|
||||
}
|
||||
|
||||
function remove_public(name,cont,err) {
|
||||
gfcloud_public_post("rm",{file:name},cont,err)
|
||||
}
|
||||
|
||||
// Publish a single grammar
|
||||
function publish_json(g,cont) {
|
||||
function publish2(dir,unique_id) {
|
||||
var oldname=g.publishedAs
|
||||
|
||||
function publish3(resptext,status) {
|
||||
console.log("publish3")
|
||||
if(oldname && oldname!=g.basename) {
|
||||
console.log("old name="+oldname)
|
||||
var name=oldname+"-"+g.unique_name+".json"
|
||||
remove_public(name,cont,cont)
|
||||
}
|
||||
else cont()
|
||||
}
|
||||
g.publishedAs=g.basename;
|
||||
save_grammar(g);
|
||||
g=assign_unique_name(g,unique_id)
|
||||
var name=g.basename+"-"+g.unique_name
|
||||
var ix=g.index;
|
||||
delete g.publishedAs
|
||||
delete g.unique_name
|
||||
delete g.index
|
||||
var form={}
|
||||
form[encodeURIComponent(name+".json")]=JSON.stringify(g)
|
||||
g=reget_grammar(ix)
|
||||
gfcloud_public_post("upload",form,publish3,cont)
|
||||
}
|
||||
with_dir(publish2);
|
||||
}
|
||||
|
||||
function download_json() {
|
||||
var dir=local.get("dir");
|
||||
var downloading=0;
|
||||
|
||||
function get_list(ok,err) { gfcloud("ls",{},ok,err) }
|
||||
|
||||
function get_file(file,ok,err) {
|
||||
downloading++;
|
||||
gfcloud("download",{file:file},ok,err);
|
||||
}
|
||||
|
||||
function file_failed(errormsg,status) {
|
||||
debug(errormsg)
|
||||
downloading--;
|
||||
}
|
||||
function file_downloaded(grammar) {
|
||||
downloading--;
|
||||
var newg=JSON.parse(grammar);
|
||||
debug("Downloaded "+newg.unique_name)
|
||||
var i=my_grammar(newg.unique_name);
|
||||
if(i!=null) merge_grammar(i,newg)
|
||||
else {
|
||||
debug("New")
|
||||
newg.index=null;
|
||||
save_grammar(newg);
|
||||
}
|
||||
if(downloading==0) done()
|
||||
}
|
||||
|
||||
function done() {
|
||||
setTimeout(function(){location.href="."},2000);
|
||||
}
|
||||
|
||||
function download_files(ls) {
|
||||
local.put("current",0);
|
||||
if(ls) {
|
||||
//debug("Downloading "+ls);
|
||||
var files=JSON.parse(ls);
|
||||
cleanup_deleted(files);
|
||||
for(var i in files) get_file(files[i],file_downloaded,file_failed);
|
||||
}
|
||||
else {
|
||||
debug("No grammars in the cloud")
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
get_list(download_files);
|
||||
}
|
||||
|
||||
function link_directories(newdir,cont) {
|
||||
gfcloud("link_directories",{newdir:newdir},cont)
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
var public_dir="/tmp/public"
|
||||
|
||||
// Request GF cloud service in the public directory (using GET)
|
||||
function gfcloud_public_json(cmd,args,cont,err) {
|
||||
var enc=encodeURIComponent;
|
||||
var url="/cloud?dir="+public_dir+"&command="+enc(cmd)+encodeArgs(args)
|
||||
http_get_json(url,cont,err)
|
||||
}
|
||||
|
||||
// Request GF cloud service in the public directory (using POST)
|
||||
function gfcloud_public_post(cmd,args,cont,err) {
|
||||
var enc=encodeURIComponent;
|
||||
var req="dir="+public_dir+"&command="+enc(cmd)+encodeArgs(args)
|
||||
ajax_http_post("/cloud",req,cont,err)
|
||||
}
|
||||
|
||||
// Request GF cloud service (using GET, for idempotent requests)
|
||||
function gfcloud(cmd,args,cont,err) {
|
||||
with_dir(function(dir) {
|
||||
var enc=encodeURIComponent;
|
||||
var url="/cloud?dir="+enc(dir)+"&command="+enc(cmd)+encodeArgs(args)
|
||||
ajax_http_get(url,cont,err)
|
||||
})
|
||||
}
|
||||
|
||||
// Reqest GF cloud service (using POST, for state changing requests)
|
||||
function gfcloud_post(cmd,args,cont,err) {
|
||||
with_dir(function(dir) {
|
||||
var enc=encodeURIComponent;
|
||||
var req="dir="+enc(dir)+"&command="+enc(cmd)+encodeArgs(args)
|
||||
ajax_http_post("/cloud",req,cont,err)
|
||||
})
|
||||
}
|
||||
|
||||
// Send a command to the GF shell
|
||||
function gfshell(cmd,cont) {
|
||||
with_dir(function(dir) {
|
||||
var enc=encodeURIComponent;
|
||||
ajax_http_get("/gfshell?dir="+enc(dir)+"&command="+enc(cmd),cont)
|
||||
})
|
||||
}
|
||||
|
||||
// Check the syntax of a source module
|
||||
function check_module(path,source,cont) {
|
||||
var enc=encodeURIComponent;
|
||||
//http_get_json("/parse?"+enc(path)+"="+enc(source),cont)
|
||||
ajax_http_post_json("/parse",enc(path)+"="+enc(source),cont)
|
||||
}
|
||||
|
||||
// Check the syntax of an expression
|
||||
function check_exp(s,cont) {
|
||||
function check(gf_message) {
|
||||
//debug("cc "+s+" = "+gf_message);
|
||||
cont(/(parse|syntax) error/.test(gf_message) ? "syntax error" : null);
|
||||
}
|
||||
gfshell("cc "+s,check);
|
||||
}
|
||||
|
||||
// Lexing/unlexing text
|
||||
function lextext(txt,cont) { gfshell('ps -lextext "'+txt+'"',cont) }
|
||||
function unlextext(txt,cont) { gfshell('ps -bind -unlextext "'+txt+'"',cont) }
|
||||
@@ -0,0 +1,152 @@
|
||||
@import url(../cloud.css);
|
||||
|
||||
#editor {
|
||||
/* This allows the div to grow wider than the window if necessary to
|
||||
accommodate the contents. Otherwise, wide things inside can poke
|
||||
through the border. */
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.home, div.grammar { border: 1px solid black; background: #9df; }
|
||||
div.home { padding: 5px; }
|
||||
div.files { margin: 0 8px 8px 8px; position: relative; }
|
||||
td.public_grammars { padding-left: 2em; }
|
||||
.no_publish .publish { display: none; }
|
||||
|
||||
div#file, table.extension td, table.extension th { border: 2px solid #009; }
|
||||
div#file { border-top-width: 0; }
|
||||
pre.plain { border: 2px solid #009; }
|
||||
div#file, pre.plain, table.matrixview td { background: white; padding: 0.6ex; }
|
||||
|
||||
table.extension { border-collapse: collapse; background: white; }
|
||||
table.extension td, table.extension th { padding: 1ex; }
|
||||
table.extension th { border-right-width: 0; color: #009; }
|
||||
table.extension td { border-left-width: 0; min-width: 30em; }
|
||||
|
||||
.slideshow .hidden { display: none; }
|
||||
|
||||
table.grammar_list { border-collapse: collapse; margin-left: 1.0em; }
|
||||
table.grammar_list td { padding: 0.4ex 0.25em; }
|
||||
|
||||
div.namebar { background: #9df; }
|
||||
div.namebar table { width: 100%; }
|
||||
.namebar h3, .sheet h3 { margin: 0; color: #009; }
|
||||
.home h3 { margin-top: 0; color: #009; }
|
||||
|
||||
div.comment:before { content: "-- "; }
|
||||
div.comment { color: #009; margin-left: 1em; }
|
||||
|
||||
td.right { text-align: right; }
|
||||
td.center { text-align: center; }
|
||||
|
||||
.kw { font-weight: bold; font-family: sans-serif; color: #009; }
|
||||
.sep { font-weight: bold; color: #009; }
|
||||
|
||||
div.indent { padding-left: 1em; min-width: 1em; min-height: 1em; }
|
||||
|
||||
div.fun:hover, div.param:hover, div.lincat:hover, div.oper:hover, div.lin:hover,
|
||||
div.template:hover
|
||||
{ background: #def;}
|
||||
|
||||
.exb_linbuttons input[type=button] { float: right; clear: right; margin: 0; }
|
||||
.exb_output { background: #dfd; float: right; margin: 0 10px; }
|
||||
|
||||
.more, .delete { font-weight: bold; font-family: sans-serif; }
|
||||
.more, .delete, .edit, *[onclick], .onclick { cursor: pointer; }
|
||||
.onclick:hover, .ident[onclick]:hover { text-decoration: underline; }
|
||||
|
||||
.hover .more, .hover .delete, .hover .edit { visibility: hidden }
|
||||
|
||||
.hover .hidden, .nohover .ifhover { display: none; }
|
||||
|
||||
.editable:hover, .deletable:hover { background: #ff9; }
|
||||
|
||||
.extensible:hover .more,.editable:hover > .edit ,.deletable:hover > .delete,
|
||||
tr.deletable:hover .delete
|
||||
{ visibility: visible; }
|
||||
|
||||
.editable { white-space: pre; }
|
||||
div.lin span.editable { display: inline-block; vertical-align: top; }
|
||||
|
||||
.more { color: green; }
|
||||
.edit { color: orange; }
|
||||
.delete { color: red; }
|
||||
.error_message,.inError { color: red; }
|
||||
.template, .template .sep, .unimportant { color: #999; }
|
||||
form { display: inline-block; }
|
||||
|
||||
table.tabs {
|
||||
width: 100%;
|
||||
border-width: 0; border-spacing: 0; empty-cells: show;
|
||||
}
|
||||
|
||||
table.tabs td { text-align: center; border: 2px solid #009; padding: 2px; white-space: nowrap; }
|
||||
table.tabs td.active { background: white; border-bottom-width: 0; }
|
||||
table.tabs td.inactive {
|
||||
background: #cef;
|
||||
border-top-color: #66c; border-left-color: #66c; border-right-color: #66c;
|
||||
}
|
||||
|
||||
table.matrixview th { background: #cef; color: #009; }
|
||||
|
||||
table.tabs td.gap
|
||||
{ border-top-width: 0; border-left-width: 0; border-right-width: 0; }
|
||||
|
||||
table.tabs input[type=button], table.matrixview th input[type=button] {
|
||||
border: 0;
|
||||
background: inherit;
|
||||
color: #009;
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
/*text-decoration: underline;*/
|
||||
}
|
||||
|
||||
.string_edit { font-family: inherit; font-size: inherit; }
|
||||
textarea.string_edit { vertical-align: top; }
|
||||
|
||||
ul.languages { -moz-column-width: 10em; }
|
||||
|
||||
#sharing h1, #sharing .footer { display: none; }
|
||||
|
||||
div.compiler_output .back_to_editor { display: none; }
|
||||
|
||||
textarea.text_mode {
|
||||
/*font-family: inherit; font-size: inherit;*/
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
div#minibar, div#syntax_editor {
|
||||
border: 1px solid black;
|
||||
padding: 5px;
|
||||
background: #ccc url("../minibar/brushed-metal.png");
|
||||
}
|
||||
|
||||
table.page_overlay {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: rgba(0,0,0,0.5);
|
||||
}
|
||||
table.page_overlay>tr>td { text-align: center; vertical-align: middle; }
|
||||
|
||||
div.grammar_extension {
|
||||
display: inline-block;
|
||||
border: 1px solid black;
|
||||
background: #9df;
|
||||
padding: 2ex;
|
||||
margin: 2ex;
|
||||
box-shadow: 10px 10px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
div.sheet {
|
||||
position: relative;
|
||||
background: white;
|
||||
padding: 1ex;
|
||||
margin: 2ex;
|
||||
box-shadow: 5px 5px 15px rgba(0,0,0,0.5);
|
||||
transition: all 0.5s ease-in-out;
|
||||
-o-transition: all 0.5s ease-in-out;
|
||||
-moz-transition: all 0.5s ease-in-out;
|
||||
-webkit-transition: all 0.5s ease-in-out;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
|
||||
var example_based=[];
|
||||
|
||||
/*
|
||||
-- cat lincat fun lin fun cat cat
|
||||
environ :: ([(CId, CId)],[(CId, Expr)],[((CId, CId), [CId])]) -> Environ
|
||||
*/
|
||||
function exb_state(g,ci) {
|
||||
var conc=g.concretes[ci]
|
||||
function show_list(show1,xs) {
|
||||
return "["+map(show1,xs).join(",")+"]";
|
||||
}
|
||||
function show_fun(fun) {
|
||||
var t=fun.type
|
||||
var res=t[t.length-1]
|
||||
var args=t.slice(0,length-1);
|
||||
return "(("+fun.name+","+res+"),["+args.join(",")+"])"
|
||||
}
|
||||
function show_lincat(lincat) {
|
||||
return "("+lincat.cat+","+lincat.type+")"
|
||||
}
|
||||
function show_lin(lin) {
|
||||
return "("+lin.fun+","+(lin.eb_lin||"?")+")"
|
||||
}
|
||||
function show_funs(funs) { return show_list(show_fun,funs) }
|
||||
function show_lincats(lincats) { return show_list(show_lincat,lincats); }
|
||||
function show_lins(lins) { return show_list(show_lin,lins) }
|
||||
return "("+show_lincats(conc.lincats)
|
||||
+","+show_lins(conc.lins)
|
||||
+","+show_funs(g.abstract.funs)+")"
|
||||
}
|
||||
|
||||
function exb_call(g,ci,command,args,cont) {
|
||||
var url=window.exb_url || "exb/exb.fcgi";
|
||||
var q=encodeArgs(args);
|
||||
var cmd="?command="+command+"&state="+encodeURIComponent(exb_state(g,ci))+q;
|
||||
http_get_json(url+cmd,cont)
|
||||
}
|
||||
|
||||
function ask_possibilities(g,ci) {
|
||||
var conc=g.concretes[ci];
|
||||
|
||||
function show_poss(poss) {
|
||||
//debug("possibilities: "+JSON.stringify(poss))
|
||||
var exready={}
|
||||
for(var i in poss[0]) exready[poss[0][i]]=true;
|
||||
var testable={}
|
||||
for(var i in poss[1]) testable[poss[1][i]]=true;
|
||||
example_based[ci]={exready:exready,testable:testable}
|
||||
conc.example_based=true;
|
||||
if(!conc.example_lang) conc.example_lang=g.concretes[0].langcode;
|
||||
reload_grammar(g);
|
||||
}
|
||||
var exci=conc_index(g,conc.example_lang);
|
||||
if(!conc.example_lang || !exci)
|
||||
conc.example_lang=g.concretes[ci==0 ? 1 : 0].langcode;
|
||||
var exci=conc_index(g,conc.example_lang);
|
||||
exb_call(g,ci,"possibilities",{example_state:exb_state(g,exci)},show_poss)
|
||||
}
|
||||
|
||||
var parser = { Eng: "ParseEngAbs.pgf",
|
||||
Swe: "ParseSwe.pgf"
|
||||
}
|
||||
|
||||
function exb_extra(g,ci) {
|
||||
var conc=g.concretes[ci];
|
||||
function stop_exb() {
|
||||
conc.example_based=false;
|
||||
reload_grammar(g);
|
||||
}
|
||||
|
||||
function exblangmenu() {
|
||||
function opt(conc) {
|
||||
return option(concname(conc.langcode),conc.langcode);
|
||||
}
|
||||
function skip_target(c) { return c.langcode!=conc.langcode; }
|
||||
var m =node("select",{},map(opt,filter(skip_target,g.concretes)));
|
||||
if(conc.example_lang) m.value=conc.example_lang;
|
||||
m.onchange=function() { conc.example_lang=m.value; save_grammar(g); }
|
||||
return m
|
||||
}
|
||||
|
||||
function ask_poss() { ask_possibilities(g,ci) }
|
||||
|
||||
if(navigator.onLine && g.concretes.length>1 && conc.example_based && !example_based[ci]) ask_poss();
|
||||
var sb=button("Start",ask_poss)
|
||||
if(parser[conc.langcode] && g.concretes.length>1)
|
||||
return indent([wrap("small",text("Example-based editing: ")),
|
||||
conc.example_based
|
||||
? node("span",{},
|
||||
[button("Stop",stop_exb),
|
||||
wrap("small",text(" Example language: ")),
|
||||
exblangmenu()
|
||||
])
|
||||
: sb])
|
||||
else {
|
||||
sb.disabled=true;
|
||||
var why= g.concretes.length>1
|
||||
? " ("+concname(conc.langcode)+" is not supported yet)"
|
||||
: " (Add another language to take examples from first.)"
|
||||
return indent([unimportant("Example-based editing: "), sb,
|
||||
unimportant(why)])
|
||||
}
|
||||
}
|
||||
|
||||
function fun_lincat(g,conc,fun) {
|
||||
var t=function_type(g,fun);
|
||||
var abscat=t[t.length-1]
|
||||
return cat_lincat(conc,abscat)
|
||||
}
|
||||
|
||||
function exb_linbuttons(g,ci,f) {
|
||||
var conc=g.concretes[ci];
|
||||
var fun=f.fun;
|
||||
var eb=example_based[ci];
|
||||
var exb_output;
|
||||
function fill_example(maybetree) {
|
||||
var tree=maybetree.Just
|
||||
if(tree) {
|
||||
if(f.template)
|
||||
conc.lins.push({fun:f.fun,args:f.args,
|
||||
lin:tree[0],eb_lin:tree[1]});
|
||||
else {
|
||||
f.lin=tree[0];
|
||||
f.eb_lin=tree[1];
|
||||
}
|
||||
ask_possibilities(g,ci)
|
||||
}
|
||||
else exb_output.innerHTML="Bug: no tree found"
|
||||
}
|
||||
function show_example(example){
|
||||
exb_output.innerHTML="";
|
||||
var s=prompt(example[1]);
|
||||
if(s) {
|
||||
var cat=fun_lincat(g,conc,fun)
|
||||
exb_output.innerHTML="...";
|
||||
//server.parse({from:"ParseEng",cat:cat,input:s},fill_example)
|
||||
exb_call(g,ci,"abstract_example",
|
||||
{cat:cat,input:s,
|
||||
parser:parser[conc.langcode],
|
||||
params:"["+f.args.join(",")+"]",
|
||||
"abstract":example[0]},
|
||||
fill_example)
|
||||
}
|
||||
}
|
||||
function by_example() {
|
||||
var dir=local.get("dir")
|
||||
if(dir) {
|
||||
if(exb_output) {
|
||||
exb_output.innerHTML="...";
|
||||
exb_call(g,ci,"provide_example",
|
||||
{lang:g.basename+conc.example_lang,
|
||||
parser:parser[conc.langcode],
|
||||
fun:fun,
|
||||
grammar:dir+"/"+g.basename+".pgf"},
|
||||
show_example)
|
||||
}
|
||||
}
|
||||
else exb_output.innerHTML="Compile the grammar first!"
|
||||
}
|
||||
function show_test(txt) {
|
||||
exb_output.innerHTML="";
|
||||
exb_output.appendChild(text(txt))
|
||||
}
|
||||
function test_it(b) {
|
||||
if(exb_output) {
|
||||
exb_output.innerHTML="...";
|
||||
exb_call(g,ci,"test_function",
|
||||
{fun:fun,parser:parser[conc.langcode]},
|
||||
show_test)
|
||||
}
|
||||
}
|
||||
var buttons=[];
|
||||
if(conc.example_based && eb) {
|
||||
if(f.template) {
|
||||
var byex=button("By example",by_example);
|
||||
if(!(eb.exready[fun] && fun_lincat(g,conc,fun)))
|
||||
byex.disabled=true
|
||||
buttons.push(byex)
|
||||
}
|
||||
else {
|
||||
var b=button("Test it",test_it);
|
||||
if(!eb.testable[fun] || !f.eb_lin) b.disabled=true;
|
||||
buttons.push(b)
|
||||
}
|
||||
var exb_output=node("span",{"class":"exb_output"},[]);
|
||||
buttons.push(exb_output)
|
||||
}
|
||||
return node("span",{"class":"exb_linbuttons"},buttons)
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
/* Abstract syntax for a small subset of GF grammars in JavaScript */
|
||||
|
||||
/*
|
||||
type UniqueId = String --persistent unique grammar identifier used by the editor
|
||||
type Id = String -- all sorts of identifiers in grammars
|
||||
type ModId = Id -- module name
|
||||
type Cat = Id -- category name
|
||||
type FunId = Id -- function name
|
||||
type Type = [Cat] -- [Cat_1,...,Cat_n] means Cat_1 -> ... -> Cat_n
|
||||
|
||||
type Grammar = { basename: ModId,
|
||||
comment: String,
|
||||
extends: [ModId], -- in 1-to-1 correspondence with uextends
|
||||
uextends: [UniqueId], -- added 2012-10-16
|
||||
abstract: Abstract,
|
||||
concretes: [Concrete] }
|
||||
|
||||
type Abstract = { startcat: Cat, cats: [Cat], funs: [Fun] }
|
||||
type Fun = { name: FunId, type: Type }
|
||||
|
||||
type Concrete = { langcode: Id,
|
||||
opens: [ModId],
|
||||
params: [{name: Id, rhs: String}],
|
||||
lincats : [{ cat: Cat, type: Term}],
|
||||
opers: [{name: Lhs, rhs: Term}],
|
||||
lins: [Lin]
|
||||
}
|
||||
type Lin = {fun: FunId, args: [Id], lin: Term}
|
||||
|
||||
type Lhs = String -- name and type of oper,
|
||||
-- e.g "regN : Str -> { s:Str,g:Gender} ="
|
||||
type Term = String -- arbitrary GF term (not parsed by the editor)
|
||||
type LinType = [Term]
|
||||
*/
|
||||
|
||||
// locally_defined_cats :: Grammar -> {Cat=>ModId} -> {Cat=>ModId} // destr upd
|
||||
function locally_defined_cats(g,dc) {
|
||||
with(g.abstract)
|
||||
for(var i in cats) dc[cats[i]]=g.basename;
|
||||
return dc;
|
||||
}
|
||||
|
||||
// predefined_cats :: () -> {Cat=>ModId}
|
||||
function predefined_cats() {
|
||||
var pd = "Predef"
|
||||
return { "Int":pd, "Float":pd, "String":pd}
|
||||
}
|
||||
|
||||
// all_defined_cats :: Grammar -> [Grammar] -> {Cat=>ModId}
|
||||
function all_defined_cats(g,igs) {
|
||||
return all_inherited_cats(igs,locally_defined_cats(g,predefined_cats()))
|
||||
}
|
||||
// all_inherited_cats :: [Grammar] -> {Cat=>ModId} -> {Cat=>ModId} // destr upd
|
||||
function all_inherited_cats(igs,dc) {
|
||||
for(var i in igs) dc=locally_defined_cats(igs[i],dc)
|
||||
return dc;
|
||||
}
|
||||
|
||||
// locally_defined_funs :: [Grammar] -> {FunId=>ModId} -> {Id=>ModId} // destr upd
|
||||
function locally_defined_funs(g,df) {
|
||||
with(g.abstract)
|
||||
for(var i in funs) df[funs[i].name]=g.basename;
|
||||
return df;
|
||||
}
|
||||
|
||||
// all_defined_funs :: Grammar -> [Grammar] -> {FunId=>ModId}
|
||||
function all_defined_funs(g,igs) {
|
||||
return all_inherited_funs(igs,locally_defined_funs(g,{}))
|
||||
}
|
||||
// all_inherited_funs :: [Grammar] -> {FunId=>ModId} -> {FunId=>ModId} // destr upd
|
||||
function all_inherited_funs(igs,df) {
|
||||
for(var i in igs) df=locally_defined_funs(igs[i],df)
|
||||
return df;
|
||||
}
|
||||
|
||||
// Return the index of the function the given name in the abstract syntax
|
||||
// fun_index :: Grammar -> FunId -> (Int|null)
|
||||
function fun_index(g,fun) {
|
||||
with(g.abstract)
|
||||
for(var i in funs) if(funs[i].name==fun) return i
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return the type of a named function in the abstract syntax
|
||||
// function_type :: Grammar -> FunId -> (Type|null)
|
||||
function function_type(g,fun) {
|
||||
var ix=fun_index(g,fun)
|
||||
return ix==null ? null : g.abstract.funs[ix].type
|
||||
}
|
||||
|
||||
// Return the lincat defined in a given concrete syntax for an abstract category
|
||||
// cat_lincat :: Concrete -> Cat -> (Term|null)
|
||||
function cat_lincat(conc,cat) {
|
||||
with(conc)
|
||||
for(var i in lincats) if(lincats[i].cat==cat) return lincats[i].type
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return the index of the lin in a given concrete syntax for an abstract function
|
||||
// lin_index :: Concrete -> FunId -> (Int|null)
|
||||
function lin_index(conc,fun) {
|
||||
with(conc) for(var i in lins) if(lins[i].fun==fun) return i
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return the lin defined in a given concrete syntax for an abstract function
|
||||
// fun_lin :: Concrete -> FunId -> (Lin|null)
|
||||
function fun_lin(conc,fun) {
|
||||
var i=lin_index(conc,fun)
|
||||
if(i!=null) return conc.lins[i]
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return the index of the concrete syntax with a given langcode
|
||||
// conc_index :: Grammar -> Id -> (Int|null)
|
||||
function conc_index(g,langcode) {
|
||||
var c=g.concretes;
|
||||
for(var ix=0;ix<c.length;ix++)
|
||||
if(c[ix].langcode==langcode) return ix
|
||||
return null;
|
||||
}
|
||||
|
||||
// rename_category :: Grammar -> Cat -> Cat -> Grammar // destructive update
|
||||
function rename_category(g,oldcat,newcat) {
|
||||
function rename_cats(cats) {
|
||||
for(var i in cats) if(cats[i]==oldcat) cats[i]=newcat;
|
||||
}
|
||||
function rename_type(t) {
|
||||
for(var i in t) if(t[i]==oldcat) t[i]=newcat;
|
||||
}
|
||||
function rename_funs(funs) {
|
||||
for(var i in funs) rename_type(funs[i].type)
|
||||
}
|
||||
function rename_abstract(a) {
|
||||
rename_cats(a.cats);
|
||||
rename_funs(a.funs);
|
||||
}
|
||||
function rename_lincat(lc) {
|
||||
if(lc.cat==oldcat) lc.cat=newcat;
|
||||
}
|
||||
function rename_concrete(c) {
|
||||
for(var i in c.lincats) rename_lincat(c.lincats[i]);
|
||||
}
|
||||
function rename_concretes(cs) {
|
||||
for(var i in cs) rename_concrete(cs[i]);
|
||||
}
|
||||
rename_abstract(g.abstract)
|
||||
rename_concretes(g.concretes);
|
||||
return g;
|
||||
}
|
||||
|
||||
// rename_function :: Grammar -> FunId -> FunId -> Grammar // destructive update
|
||||
function rename_function(g,oldfun,newfun) {
|
||||
function rename_concrete(c) {
|
||||
var i=lin_index(c,oldfun)
|
||||
if(i!=null) c.lins[i].fun=newfun;
|
||||
}
|
||||
for(var i in g.concretes) rename_concrete(g.concretes[i]);
|
||||
return g;
|
||||
}
|
||||
|
||||
// change_lin_lhs :: Grammar -> Fun -> Grammar // destructive update
|
||||
function change_lin_lhs(g,fun) {
|
||||
function change_concrete(c) {
|
||||
var i=lin_index(c,fun.name)
|
||||
if(i!=null) c.lins[i].args=arg_names(fun.type);
|
||||
}
|
||||
for(var i in g.concretes) change_concrete(g.concretes[i]);
|
||||
return g;
|
||||
}
|
||||
|
||||
/* --- Parsing -------------------------------------------------------------- */
|
||||
|
||||
// GF idenfifier syntax:
|
||||
var lex_id=/^[A-Za-z][A-Za-z0-9_']*$/
|
||||
// See https://developer.mozilla.org/en/JavaScript/Guide/Regular_Expressions
|
||||
|
||||
function check_id(s) { return lex_id.test(s); }
|
||||
|
||||
function check_name(s,kind) {
|
||||
return check_id(s)
|
||||
? null
|
||||
: s+"? "+kind+" names must start with a letter and can contain letters, digits, _ and '"
|
||||
}
|
||||
|
||||
// parse_fun :: String -> {error:String} + {ok:Fun}
|
||||
function parse_fun(s) {
|
||||
var ws=s.split(/\s+/);
|
||||
var fun={name:"",type:[]};
|
||||
/* Use a state machine to parse function definitions */
|
||||
/* f : T1 -> ... -> Tn */
|
||||
var state="name";
|
||||
var ok=true;
|
||||
for(var i=0;ok && i<ws.length;i++) {
|
||||
if(ws[i]!="") {
|
||||
switch(state) {
|
||||
case "name": fun.name=ws[i]; state=":"; break;
|
||||
case ":": ok=ws[i]==":"; state="type"; break;
|
||||
case "type": fun.type.push(ws[i]); state="->"; break;
|
||||
case "->": ok=ws[i]=="->"; state="type"; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
var err=check_name(fun.name,"Function");
|
||||
if(err) return {error: err};
|
||||
return ok && state=="->"
|
||||
? {ok:fun}
|
||||
: { error : "Fun : Cat<sub>1</sub> -> ... -> Cat<sub>n</sub>" }
|
||||
}
|
||||
|
||||
|
||||
// parse_param :: String -> {error:String} + { ok:{name:Id,rhs:String} }
|
||||
function parse_param(s) {
|
||||
var ws=s.split("=");
|
||||
if(ws.length==2) {
|
||||
var name=ws[0].trim();
|
||||
var err=check_name(name,"Parameter type");
|
||||
return err ? { error:err } : { ok: { name:name,rhs:ws[1].trim() } }
|
||||
}
|
||||
else
|
||||
return { error: "P = C1 | ... | Cn" }
|
||||
}
|
||||
|
||||
// parse_oper :: String -> {error:String} + {ok:{name:Lhs, rhs:Term}}
|
||||
function parse_oper(s) {
|
||||
var i=s.indexOf(" ");
|
||||
var operr = { error: "op = expr" }
|
||||
if(i>0 && i<s.length-1) {
|
||||
var name=s.substr(0,i).trim();
|
||||
var rhs=s.substr(i).trim();
|
||||
var err=check_name(name,"Operator");
|
||||
return err
|
||||
? {error:err}
|
||||
: rhs!="" ? {ok: {name:name, rhs:rhs}}
|
||||
: operr
|
||||
}
|
||||
else return operr
|
||||
|
||||
}
|
||||
|
||||
/* --- Print as plain text (normal GF source syntax) ------------------------ */
|
||||
|
||||
// show_type :: Type -> String
|
||||
function show_type(t) {
|
||||
var s="";
|
||||
for(var i in t) {
|
||||
if(i>0) s+=" -> ";
|
||||
s+=t[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// show_lintype :: LinType -> String
|
||||
function show_lintype(t) {
|
||||
var s="";
|
||||
for(var i in t) {
|
||||
if(i>0) s+=" -> ";
|
||||
s+= check_id(t[i]) ? t[i] : "("+t[i]+")";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// show_fun :: Fun -> String
|
||||
function show_fun(fun) {
|
||||
return fun.name+" : "+show_type(fun.type);
|
||||
}
|
||||
|
||||
// show_grammar :: Grammar -> String
|
||||
function show_grammar(g) {
|
||||
return show_abstract(g)+"\n"+show_concretes(g)
|
||||
}
|
||||
|
||||
function show_abstract(g) {
|
||||
// var startcat= g.abstract.cats.length==1 ? g.abstract.cats[0] : g.abstract.startcat;
|
||||
var startcat= g.abstract.startcat || g.abstract.cats[0];
|
||||
return "abstract "+g.basename+" = "
|
||||
+show_extends(g.extends)
|
||||
+"{\n\n"
|
||||
+"flags coding = utf8 ;\n\n"
|
||||
+show_startcat(startcat)
|
||||
+show_cats(g.abstract.cats)
|
||||
+show_funs(g.abstract.funs)
|
||||
+"}\n";
|
||||
}
|
||||
|
||||
function show_extends(exts) {
|
||||
return exts && exts.length>0 ? exts.join(", ")+" ** " : "";
|
||||
}
|
||||
|
||||
function show_startcat(startcat) {
|
||||
return startcat && startcat!="-"
|
||||
? "flags startcat = "+startcat+";\n\n"
|
||||
: "";
|
||||
}
|
||||
|
||||
function show_cats(cats) {
|
||||
return cats.length>0 ? "cat\n "+cats.join("; ")+";\n\n" : "";
|
||||
}
|
||||
|
||||
function show_funs(funs) { return show_list("fun",show_fun,funs); }
|
||||
|
||||
function show_concretes(g) {
|
||||
return map(show_concrete(g),g.concretes).join("\n\n");
|
||||
}
|
||||
|
||||
function conc_extends(conc) { return function(m) { return m+conc.langcode; }}
|
||||
|
||||
function show_concrete(g) {
|
||||
return function(conc) {
|
||||
return "" // "--# -path=.:present\n"
|
||||
+ "concrete "+g.basename+conc.langcode+" of "+g.basename+" = "
|
||||
+show_extends((g.extends || []).map(conc_extends(conc)))
|
||||
+show_opens(conc.opens)
|
||||
+"{\n\nflags coding = utf8 ;\n\n"
|
||||
+show_lincats(conc.lincats)
|
||||
+show_lins(conc.lins)
|
||||
+show_params(conc.params)
|
||||
+show_opers(conc.opers)
|
||||
+"}\n"
|
||||
}
|
||||
}
|
||||
|
||||
function show_list(kw,show1,list) {
|
||||
return list.length>0
|
||||
? kw+"\n "+map(show1,list).join(";\n ")+";\n\n"
|
||||
: ""
|
||||
}
|
||||
|
||||
function show_opens(opens) {
|
||||
return opens && opens.length>0 ? "\n\nopen "+opens.join(", ")+" in " : ""
|
||||
}
|
||||
|
||||
function show_params(params) { return show_list("param",show_param,params); }
|
||||
function show_lincats(lincats) { return show_list("lincat",show_lincat,lincats); }
|
||||
function show_opers(opers) { return show_list("oper",show_oper,opers); }
|
||||
function show_lins(lins) { return show_list("lin",show_lin,lins); }
|
||||
|
||||
|
||||
function show_param(p) { return p.name + " = " + p.rhs; }
|
||||
function show_oper(p) { return p.name + " " + p.rhs; }
|
||||
function show_lincat(p) { return p.cat + " = " + p.type; }
|
||||
|
||||
function show_lin(lin) {
|
||||
return lin.fun + " " + lin.args.join(" ")+ " = " + lin.lin;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
CACHE MANIFEST
|
||||
# 5
|
||||
NETWORK:
|
||||
*
|
||||
@@ -0,0 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
<html> <!-- manifest="gfse.manifest" -->
|
||||
<head>
|
||||
<title>GF online editor for simple multilingual grammars</title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" type="text/css" href="editor.css" title="Cloud">
|
||||
<link rel="alternate stylesheet" type="text/css" href="molto.css" title="MOLTO">
|
||||
<link rel="stylesheet" type="text/css" href="../minibar/minibar.css">
|
||||
<link rel="stylesheet" type="text/css" href="../syntax-editor/editor.css">
|
||||
<link rel="stylesheet" type="text/css" href="../wordnet/gf-wordnet.css">
|
||||
|
||||
|
||||
<link rel=author href="http://www.cse.chalmers.se/~hallgren/" title="Thomas Hallgren">
|
||||
|
||||
<meta name = "viewport" content = "width = device-width">
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||||
|
||||
</head>
|
||||
|
||||
<body class=hover>
|
||||
|
||||
<h2>GF online editor for simple multilingual grammars</h2>
|
||||
<div id=editor></div>
|
||||
<small class="hidden">
|
||||
<span class=more>+</span>=Add an item,
|
||||
<span class=delete>×</span>=Delete item,
|
||||
<span class=edit>%</span>=Edit item.
|
||||
</small>
|
||||
<small class="ifhover">Hover over items for hints and editing options.</small>
|
||||
<div id=compiler_output class=compiler_output></div>
|
||||
|
||||
<noscript>
|
||||
This page does not work without JavaScript.
|
||||
</noscript>
|
||||
|
||||
<hr>
|
||||
<div class=modtime><small>
|
||||
HTML
|
||||
<!-- hhmts start -->Last modified: Thu May 21 09:47:39 CEST 2015 <!-- hhmts end -->
|
||||
</small></div>
|
||||
<a href="about.html">About</a>
|
||||
<pre id=debug></pre>
|
||||
<script type="text/javascript" src="config.js"></script> <!-- optional -->
|
||||
<script type="text/javascript" src="../js/support.js"></script>
|
||||
<script type="text/JavaScript" src="../js/pgf_online.js"></script>
|
||||
<script type="text/javascript" src="../js/localstorage.js"></script>
|
||||
<script type="text/JavaScript" src="../js/langcode.js"></script>
|
||||
|
||||
<script type="text/javascript" src="localstorage.js"></script>
|
||||
<script type="text/javascript" src="gf_abs.js"></script>
|
||||
<script type="text/javascript" src="example_based.js"></script>
|
||||
<script type="text/javascript" src="sort.js"></script>
|
||||
<script type="text/javascript" src="cloud2.js"></script>
|
||||
<script type="text/javascript" src="editor.js"></script>
|
||||
|
||||
<script type="text/JavaScript" src="../minibar/minibar.js"></script>
|
||||
<script type="text/JavaScript" src="../minibar/minibar_input.js"></script>
|
||||
<script type="text/JavaScript" src="../minibar/minibar_translations.js"></script>
|
||||
<script type="text/JavaScript" src="../minibar/minibar_support.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../js/grammar_manager.js"></script>
|
||||
<script type="text/javascript" src="../syntax-editor/ast.js"></script>
|
||||
<script type="text/javascript" src="../syntax-editor/editor_menu.js"></script>
|
||||
<script type="text/javascript" src="../syntax-editor/editor.js"></script>
|
||||
<script type="text/javascript" src="../wordnet/js/gf-wordnet.js"></script>
|
||||
<script src="https://unpkg.com/vis-network@9.0.4/standalone/umd/vis-network.min.js"></script>
|
||||
<script type="text/javascript" src="../wordnet/js/wordcloud2.js"></script>
|
||||
|
||||
<div id="search_popup" class="search_popup">
|
||||
<table id="domains" class="selector">
|
||||
<thead></thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
<table id="result" class="result">
|
||||
<thead></thead>
|
||||
<tbody></tbody>
|
||||
<tfoot></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
//Needs ../js/localstorage.js
|
||||
|
||||
var local=appLocalStorage("gf.editor.simple.grammar")
|
||||
@@ -0,0 +1,105 @@
|
||||
body { color: #413b36; background: #fffcfa; }
|
||||
h1 { font-size: 175%; }
|
||||
h1,h2,h3,h4,small { font-family: sans-serif; }
|
||||
h1,h2,h3,h4,a { color: #5c1a1a; }
|
||||
|
||||
h1:first-child, h2:first-child { margin-top: 0; margin-bottom: 1ex; }
|
||||
|
||||
#editor {
|
||||
/* This allows the div to grow wider than the window if necessary to
|
||||
accommodate the contents. Otherwise, wide things inside can poke
|
||||
through the border. */
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.grammar { border: 2px solid #b09779; background: #642121; }
|
||||
div.files { margin: 0 8px 8px 8px; }
|
||||
|
||||
div#file { border: 2px solid #b0977d; border-top-width: 0; }
|
||||
pre.plain { border: 2px solid #b0977d; }
|
||||
div#file, pre.plain { background: #fffcfa; padding: 0.6ex; }
|
||||
|
||||
.slideshow .hidden { display: none; }
|
||||
|
||||
img.cloud, img.right, div.right, div.modtime { float: right; }
|
||||
.modtime { color: #999; white-space: nowrap; }
|
||||
|
||||
table.grammar_list { border-collapse: collapse; margin-left: 1.0em; }
|
||||
table.grammar_list td { padding: 0.4ex 0.25em; }
|
||||
|
||||
/*div.namebar { background: #642121; }*/
|
||||
div.namebar table { width: 100%; }
|
||||
.namebar h3 { margin: 0; color: white; }
|
||||
|
||||
td.right { text-align: right; }
|
||||
|
||||
.kw { font-weight: bold; font-family: sans-serif; color: #642121; }
|
||||
.sep { font-weight: bold; color: #642121; }
|
||||
|
||||
div.indent { padding-left: 1em; min-width: 1em; min-height: 1em; }
|
||||
|
||||
/*
|
||||
div.fun, div.param, div.lincat, div.oper, div.lin
|
||||
{ padding-left: 2em; text-indent: -2em; }
|
||||
*/
|
||||
.more, .delete { font-weight: bold; font-family: sans-serif; }
|
||||
.more, .delete, .edit { cursor: pointer; }
|
||||
|
||||
.hover .more, .hover .delete, .hover .edit { visibility: hidden }
|
||||
|
||||
.hover .hidden, .nohover .ifhover { display: none; }
|
||||
|
||||
.editable:hover, .deletable:hover { background: #ff9; }
|
||||
.namebar .editable:hover { background: #04b; }
|
||||
|
||||
.extensible:hover .more,.editable:hover > .edit ,.deletable:hover > .delete,
|
||||
tr.deletable:hover .delete
|
||||
{ visibility: visible; }
|
||||
|
||||
.more { color: green; }
|
||||
.edit { color: orange; }
|
||||
.delete { color: red; }
|
||||
.error_message,.inError { color: red; }
|
||||
.template, .template .sep, .unimportant { color: #999; }
|
||||
form { display: inline-block; }
|
||||
|
||||
table.tabs {
|
||||
width: 100%;
|
||||
border-width: 0; border-spacing: 0; empty-cells: show;
|
||||
}
|
||||
|
||||
table.tabs td { text-align: center; border: 2px solid #b09779; padding: 2px; white-space: nowrap; }
|
||||
table.tabs td.active { background: white; border-bottom-width: 0; }
|
||||
table.tabs td.inactive {
|
||||
background: #e1e1e1;
|
||||
border-top-color: #b09779; border-left-color: #b09779; border-right-color: #b09779;
|
||||
}
|
||||
|
||||
table.tabs td.gap
|
||||
{ border-top-width: 0; border-left-width: 0; border-right-width: 0; }
|
||||
|
||||
table.tabs input[type=button] {
|
||||
border: 0;
|
||||
background: inherit;
|
||||
color: #642121;
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
/*text-decoration: underline;*/
|
||||
}
|
||||
|
||||
input.string_edit { font-family: inherit; font-size: inherit; }
|
||||
|
||||
ul.languages { -moz-column-width: 20em; }
|
||||
|
||||
li { margin-top: 0.5ex; margin-bottom: 0.5ex; }
|
||||
|
||||
#sharing h1, #sharing .footer { display: none; }
|
||||
|
||||
div.compiler_output .back_to_editor { display: none; }
|
||||
|
||||
div#minibar {
|
||||
border: 1px solid black;
|
||||
padding: 5px;
|
||||
background: #ccc url("../minibar/brushed-metal.png");
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html> <head>
|
||||
<title>Download from Grammar Cloud</title>
|
||||
<link rel="stylesheet" type="text/css" href="editor.css" title="Cloud">
|
||||
<link rel="alternate stylesheet" type="text/css" href="molto.css" title="MOLTO">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1><img src="P/1307545048_weather_09.png" alt="">Download from Grammar Cloud</h1>
|
||||
|
||||
<pre id=debug></pre>
|
||||
|
||||
<hr>
|
||||
<address></address>
|
||||
<!-- hhmts start -->Last modified: Wed Apr 3 21:53:09 CEST 2013 <!-- hhmts end -->
|
||||
<script type="text/javascript" src="../js/support.js"></script>
|
||||
<script type="text/javascript" src="../js/localstorage.js"></script>
|
||||
<script type="text/javascript" src="localstorage.js"></script>
|
||||
<script type="text/javascript" src="gf_abs.js"></script>
|
||||
<script type="text/javascript" src="editor.js"></script>
|
||||
<script type="text/javascript" src="cloud2.js"></script>
|
||||
<script type="text/javascript" src="sort.js"></script>
|
||||
<script type="text/javascript">
|
||||
download_from_cloud();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,86 @@
|
||||
|
||||
var internet_explorer=navigator.appName=="Microsoft Internet Explorer";
|
||||
|
||||
/* How to change opacity in IE:
|
||||
http://joseph.randomnetworks.com/archives/2006/08/16/css-opacity-in-internet-explorer-ie/
|
||||
*/
|
||||
|
||||
var set_opacity =
|
||||
internet_explorer
|
||||
? function(el,o) { el.style.filter="alpha(opacity="+Math.round(o*100)+")";}
|
||||
: function(el,o) { el.style.opacity=o; };
|
||||
|
||||
function start_slideshow(img,options) {
|
||||
var p=img.parentNode;
|
||||
if(p.tagName=="A") p=p.parentNode;
|
||||
var is=p.getElementsByTagName("img");
|
||||
if(is.length>1) {
|
||||
var cur=0;
|
||||
var w=img.width;
|
||||
var h=img.height;
|
||||
//p.style.position="relative";
|
||||
p.style.minWidth=w+"px";
|
||||
p.style.minHeight=h+"px";
|
||||
var images=[];
|
||||
for(var i=0;i<is.length;i++) {
|
||||
images[i]=is[i];
|
||||
var c=images[i];
|
||||
if(internet_explorer) c.style.zoom=1;
|
||||
c.style.position="absolute";
|
||||
}
|
||||
var timeout=1000*(options.delay || 5);
|
||||
var ft=options.fade==null ? 1 : options.fade;
|
||||
var tick=function() {
|
||||
var c=images[cur];
|
||||
cur= (cur+1) % images.length;
|
||||
var n=images[cur];
|
||||
set_opacity(n,0);
|
||||
//n.style.position="static";
|
||||
n.style.zIndex=1;
|
||||
n.className="";
|
||||
if(n.width>w) { w=n.width; p.style.minWidth=w+"px"; }
|
||||
if(n.height>h) { h=n.height; p.style.minHeight=h+"px"; }
|
||||
c.style.position="absolute";
|
||||
c.style.zIndex=0;
|
||||
fade(n,0,1,ft,function() {
|
||||
if(c.width>n.width || c.height>n.height) fade(c,1,0,ft,null);
|
||||
else set_opacity(c,0); });
|
||||
//debug.innerHTML=w+"x"+h;
|
||||
//for(var i=0;i<images.length;i++)
|
||||
//debug.appendChild(text(" "+images[i].style.position));
|
||||
}
|
||||
//var debug=document.createElement("div");
|
||||
//p.parentNode.insertBefore(debug,p);
|
||||
//debug.innerHTML=w+"x"+h;
|
||||
setInterval(tick,timeout);
|
||||
}
|
||||
//else alert("No slideshow!");
|
||||
}
|
||||
|
||||
function fade(el,start,stop,t,after) {
|
||||
// el: which element to fade
|
||||
// start: starting opacity [0..1]
|
||||
// stop: ending opacity [0..1]
|
||||
// t: duration of fade (in seconds), default 1s
|
||||
// after: function to call when done fading, optional
|
||||
var dt=40; // Animation granularity, 1/40ms = 25fps
|
||||
el.step=(stop-start)*dt/(1000*(t==null ? 1 : t));
|
||||
el.stop=stop;
|
||||
//alert("fade "+start+" "+stop+" "+el.step);
|
||||
var done=function() {
|
||||
clearInterval(el.timer);
|
||||
el.timer=null;
|
||||
if(after) after();
|
||||
}
|
||||
var f=function() {
|
||||
var next=el.current+el.step;
|
||||
if(next>=1) { next=1; done(); }
|
||||
if(next<=0) { next=0; done(); }
|
||||
set_opacity(el,next);
|
||||
el.current=next
|
||||
}
|
||||
if(!el.timer) {
|
||||
el.current=start;
|
||||
el.timer=setInterval(f,dt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
|
||||
function initialize_sorting(tagList,classList) {
|
||||
/*
|
||||
var debugoutput=empty("pre");
|
||||
document.body.appendChild(debugoutput)
|
||||
function jsdebug(txt) {
|
||||
//clear(debugoutput)
|
||||
debugoutput.appendChild(text(txt+"\n"))
|
||||
}
|
||||
*/
|
||||
function listToSet(list) {
|
||||
var set={}
|
||||
for(var i in list) set[list[i]]=true
|
||||
return set;
|
||||
}
|
||||
var sortable_tag=listToSet(tagList)
|
||||
var sortable_class=listToSet(classList)
|
||||
|
||||
function sortable(elem) {
|
||||
return elem && elem.tagName!="INPUT" && elem.tagName!="BUTTON"
|
||||
&& (sortable_tag[elem.tagName]
|
||||
? sortable_class[elem.className]
|
||||
? elem
|
||||
: null
|
||||
: sortable(elem.parentNode))
|
||||
}
|
||||
|
||||
function move_element(elem,x,y) {
|
||||
elem.style.left=x+"px";
|
||||
elem.style.top=y+"px";
|
||||
elem.delta={x:x,y:y};
|
||||
}
|
||||
|
||||
function adjust_refs(elem,dy) {
|
||||
//jsdebug("dy="+dy);
|
||||
move_element(elem,elem.delta.x,elem.delta.y-dy);
|
||||
elem.downAt.y+=dy;
|
||||
elem.range.lo-=dy;
|
||||
elem.range.hi-=dy;
|
||||
}
|
||||
|
||||
function move_up(elem) {
|
||||
var prev=elem.previousElementSibling;
|
||||
if(prev) {
|
||||
var top=elem.offsetTop;
|
||||
var mid=prev.offsetTop+prev.offsetHeight/2;
|
||||
if(top<mid) {
|
||||
elem.parentNode.insertBefore(elem,prev);
|
||||
adjust_refs(elem,elem.offsetTop-top)
|
||||
}
|
||||
//else jsdebug("not yet, top="+top+", mid="+mid);
|
||||
}
|
||||
//else jsdebug("at top");
|
||||
}
|
||||
|
||||
function move_down(elem) {
|
||||
var next=elem.nextElementSibling;
|
||||
if(next) {
|
||||
var top=elem.offsetTop;
|
||||
var bot=top+elem.offsetHeight;
|
||||
var mid=next.offsetTop+next.offsetHeight/2;
|
||||
if(bot>mid) {
|
||||
next.parentNode.insertBefore(next,elem);
|
||||
adjust_refs(elem,elem.offsetTop-top)
|
||||
}
|
||||
//else jsdebug("not yet, top="+top+", bot="+bot+", mid="+mid);
|
||||
}
|
||||
//else jsdebug("at bottom");
|
||||
}
|
||||
|
||||
function swap(elem,dy) {
|
||||
if(dy>0) move_down(elem);
|
||||
else if(dy<0) move_up(elem);
|
||||
}
|
||||
|
||||
function restrictTo(range,y) {
|
||||
return Math.min(range.hi,Math.max(range.lo,y));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// These functions isolate the difference between mouse interfaces and touch
|
||||
// interfaces
|
||||
|
||||
function eventPosition(event) {
|
||||
var p=event
|
||||
if(event.touches) p=event.touches[0]
|
||||
return {x:p.screenX,y:p.screenY}
|
||||
}
|
||||
|
||||
function setStartHandler(ondown) {
|
||||
if("ontouchstart" in window) document.ontouchstart=ondown
|
||||
else document.onmousedown=ondown
|
||||
}
|
||||
|
||||
function setDragHandlers(onmove,onend) {
|
||||
if("ontouchstart" in window) {
|
||||
document.ontouchmove=onmove;
|
||||
document.ontouchend=onend;
|
||||
}
|
||||
else {
|
||||
document.onmousemove=onmove;
|
||||
document.onmouseup=onend;
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
function startDrag(event,elem) {
|
||||
//jsdebug("Start dragging");
|
||||
elem.style.position="relative";
|
||||
elem.delta || (elem.delta={x:0,y:0});
|
||||
var p=eventPosition(event)
|
||||
elem.downAt={x:p.x-elem.delta.x,y:p.y-elem.delta.y};
|
||||
var list=elem.parentNode;
|
||||
// elem and list must have the same offsetParent for this to work!!
|
||||
var top=list.offsetTop-elem.offsetTop+elem.delta.y;
|
||||
elem.range={lo:top,hi:top+list.offsetHeight-elem.offsetHeight};
|
||||
elem.style.zIndex=1;
|
||||
//console.log("Start dragging",elem.id,list.offsetTop,elem.offsetTop,elem.range.lo,elem.range.hi)
|
||||
function dragMove(event) {
|
||||
var p=eventPosition(event)
|
||||
var dx=0/*p.x-elem.downAt.x*/;
|
||||
var dy=restrictTo(elem.range,p.y-elem.downAt.y);
|
||||
//jsdebug("dragging to "+dx+" "+dy+" "+show_props(elem.range,"range"));
|
||||
//console.log("dragging to ",dy);
|
||||
move_element(elem,dx,dy);
|
||||
//jsdebug("dragging to "+elem.offsetLeft+" "+elem.offsetTop);
|
||||
swap(elem,dy)
|
||||
return false;
|
||||
}
|
||||
function dragEnd() {
|
||||
//jsdebug("dropped");
|
||||
elem.style.zIndex=0;
|
||||
move_element(elem,0,0);
|
||||
setDragHandlers(null,null)
|
||||
preventScroll=false;
|
||||
return false;
|
||||
}
|
||||
preventScroll=true;
|
||||
setDragHandlers(dragMove,dragEnd)
|
||||
return false;
|
||||
}
|
||||
|
||||
function mousedown(event) {
|
||||
var elem=sortable(event.target);
|
||||
if(elem) return startDrag(event,elem);
|
||||
//else jsdebug("Clicked outside"/*+taglist(event.target)/*+show_props(event,"event")*/);
|
||||
}
|
||||
|
||||
//var jsdebug=debug;
|
||||
|
||||
//https://stackoverflow.com/questions/49500339/cant-prevent-touchmove-from-scrolling-window-on-ios
|
||||
var preventScroll=false;
|
||||
function pd(e) {if(preventScroll) e.preventDefault()}
|
||||
|
||||
function init() {
|
||||
setStartHandler(mousedown)
|
||||
document.addEventListener("touchmove",pd,{passive:false})
|
||||
//var d=element("javascriptdebug");
|
||||
//if(d) jsdebug=function(msg) { d.innerHTML=msg; }
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
//Inspired by http://tool-man.org/examples/sorting.html
|
||||