mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-04-09 04:59:31 -06:00
Adding the prototype GF editor for simple multilingual grammars
This commit is contained in:
BIN
src/editor/simple/P/w1s.jpg
Normal file
BIN
src/editor/simple/P/w1s.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
src/editor/simple/P/w2s.jpg
Normal file
BIN
src/editor/simple/P/w2s.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
src/editor/simple/P/w3s.jpg
Normal file
BIN
src/editor/simple/P/w3s.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
src/editor/simple/P/w4s.jpg
Normal file
BIN
src/editor/simple/P/w4s.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
28
src/editor/simple/TODO
Normal file
28
src/editor/simple/TODO
Normal file
@@ -0,0 +1,28 @@
|
||||
+ 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
|
||||
- 2. possibility to import modules - both resource libraries and 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
|
||||
|
||||
+ 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.
|
||||
157
src/editor/simple/about.html
Normal file
157
src/editor/simple/about.html
Normal file
@@ -0,0 +1,157 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>About: GF online editor for simple multilingual grammars</title>
|
||||
<link rel=stylesheet href="editor.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">
|
||||
<meta charset="UTF-8">
|
||||
|
||||
<script type="text/javascript" src="slideshow.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>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>About</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 "Enabile editing on touch devices" that reveals all editing
|
||||
symbols.
|
||||
|
||||
<p>
|
||||
In spite of its name, the editor runs entierly 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>.
|
||||
|
||||
<h3>Limitations</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. The resource grammar library is not available.
|
||||
|
||||
<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 <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>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.
|
||||
</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, concrete syntax definitions are limited to
|
||||
<ul>
|
||||
<li><em>parameter types</em>,
|
||||
<var>P</var> = <var>C<sub>1</sub></var> | ... |<var>C<sub>n</sub></var>,
|
||||
<li><em>linearization types</em> for the categories in the abstract syntax,
|
||||
<li><em>operation definitions</em>, <var>op</var> = <var>expr</var>,
|
||||
<li>and <em>linearizations</em> for the functions in the abstract syntax.
|
||||
</ul>
|
||||
|
||||
Available editing operations:
|
||||
<ul>
|
||||
<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.
|
||||
</ul>
|
||||
|
||||
Error checks:
|
||||
<ul>
|
||||
<li>The RHSs in the concrete syntax are not checked for errors. Arbitrary
|
||||
strings can be entered.
|
||||
</ul>
|
||||
|
||||
<h3>Future work</h3>
|
||||
|
||||
This prototype gives an idea of how a web based GF grammar editor could work.
|
||||
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 to allow grammars to be tested in the GF
|
||||
shell or the minibar.
|
||||
<p>
|
||||
Grammars are currently stored locally in the browser, but a future version
|
||||
could allow grammars to be stored "in the cloud", allowing the same grammars
|
||||
to be accessed from multiple devices.
|
||||
|
||||
<hr>
|
||||
<div class=modtime><small>
|
||||
<!-- hhmts start --> Last modified: Mon Jan 24 17:20:37 CET 2011 <!-- 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>
|
||||
72
src/editor/simple/editor.css
Normal file
72
src/editor/simple/editor.css
Normal file
@@ -0,0 +1,72 @@
|
||||
body { background: #eee; }
|
||||
h1 { font-size: 175%; }
|
||||
h1,h2,h3,h4,small { font-family: sans-serif; }
|
||||
h1:first-child, h2:first-child { margin-top: 0; margin-bottom: 1ex; }
|
||||
|
||||
#editor { max-width: 50em; }
|
||||
div.grammar { border: 1px solid black; background: white; background: #9df; }
|
||||
div.files { margin: 0 8px 8px 8px; }
|
||||
|
||||
div#file { border: 2px solid #009; border-top-width: 0; }
|
||||
pre.plain { border: 2px solid #009; }
|
||||
div#file, pre.plain { background: white; padding: 0.6ex; }
|
||||
|
||||
.slideshow .hidden { display: none; }
|
||||
|
||||
img.right, div.right, div.modtime { float: right; }
|
||||
.modtime { color: #999; white-space: nowrap; }
|
||||
|
||||
div.namebar { background: #9df; }
|
||||
div.namebar table { width: 100%; }
|
||||
.namebar h3 { margin: 0; color: #009; }
|
||||
|
||||
td.right { text-align: right; }
|
||||
|
||||
.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; }
|
||||
|
||||
|
||||
.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; }
|
||||
|
||||
.extensible:hover .more,.editable:hover > .edit ,.deletable:hover > .delete
|
||||
{ visibility: visible; }
|
||||
|
||||
.more { color: green; }
|
||||
.edit { color: orange; }
|
||||
.delete { color: red; }
|
||||
.error_message,.inError { color: red; }
|
||||
.template, .template .sep { 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; }
|
||||
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.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: #009;
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
/*text-decoration: underline;*/
|
||||
}
|
||||
789
src/editor/simple/editor.js
Normal file
789
src/editor/simple/editor.js
Normal file
@@ -0,0 +1,789 @@
|
||||
|
||||
|
||||
var editor=element("editor");
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
function div_id(id,cs) { return node("div",{id:id},cs); }
|
||||
function div_class(cls,cs) { return node("div",{"class":cls},cs); }
|
||||
function a(url,linked) { return node("a",{href:url},linked); }
|
||||
function ul(lis) { return node("ul",{},lis); }
|
||||
function li(xs) { return node("li",{},xs); }
|
||||
function table(rows) { return node("table",{},rows); }
|
||||
function td_right(cs) { return node("td",{"class":"right"},cs); }
|
||||
function jsurl(js) { return "javascript:"+js; }
|
||||
|
||||
function insertAfter(el,ref) {
|
||||
ref.parentNode.insertBefore(el,ref.nextSibling);
|
||||
}
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
function initial_view() {
|
||||
var current=local.get("current");
|
||||
if(current>0) open_grammar(current-1);
|
||||
else draw_grammar_list();
|
||||
//debug(local.get("dir","no server directory yet"));
|
||||
}
|
||||
|
||||
function draw_grammar_list() {
|
||||
local.put("current",0);
|
||||
editor.innerHTML="";
|
||||
editor.appendChild(node("h3",{},[text("Your grammars")]));
|
||||
var gs=ul([]);
|
||||
function del(i) { return function () { delete_grammar(i); } }
|
||||
for(var i=0;i<local.count;i++) {
|
||||
var grammar=local.get(i,null);
|
||||
if(grammar && grammar.basename) {
|
||||
var link=a(jsurl("open_grammar("+i+")"),[text(grammar.basename)]);
|
||||
gs.appendChild(
|
||||
li([deletable(del(i),link,"Delete this grammar")]))
|
||||
}
|
||||
}
|
||||
if(local.get("count",null)==null)
|
||||
editor.appendChild(text("You have not created any grammars yet."));
|
||||
else if(local.count==0)
|
||||
editor.appendChild(text("Your grammar list is empty."));
|
||||
editor.appendChild(gs);
|
||||
|
||||
editor.appendChild(
|
||||
ul([li([a(jsurl("new_grammar()"),[text("New grammar")])])]));
|
||||
//editor.appendChild(text(local.count));
|
||||
}
|
||||
|
||||
function new_grammar() {
|
||||
var g={basename:"Unnamed",
|
||||
abstract:{cats:[],funs:[]},
|
||||
concretes:[]}
|
||||
edit_grammar(g);
|
||||
}
|
||||
|
||||
function delete_grammar(i) {
|
||||
var g=local.get(i);
|
||||
var ok=confirm("Do you really want to delete the grammar "+g.basename+"?")
|
||||
if(ok) {
|
||||
local.remove(i);
|
||||
while(local.count>0 && !local.get(local.count-1))
|
||||
local.count--;
|
||||
initial_view();
|
||||
}
|
||||
}
|
||||
|
||||
function open_grammar(i) {
|
||||
var g=local.get(i);
|
||||
g.index=i;
|
||||
local.put("current",i+1);
|
||||
edit_grammar(g);
|
||||
}
|
||||
|
||||
function close_grammar(g) { save_grammar(g); draw_grammar_list(); }
|
||||
function reload_grammar(g) { save_grammar(g); edit_grammar(g); }
|
||||
|
||||
function save_grammar(g) {
|
||||
if(g.index==null) g.index=local.count++;
|
||||
local.put(g.index,g);
|
||||
}
|
||||
|
||||
function edit_grammar(g) {
|
||||
editor.innerHTML="";
|
||||
editor.appendChild(draw_grammar(g));
|
||||
}
|
||||
|
||||
|
||||
function draw_grammar(g) {
|
||||
var files=div_class("files",[draw_filebar(g),draw_file(g)]);
|
||||
return div_class("grammar",[draw_namebar(g,files),files])
|
||||
|
||||
}
|
||||
|
||||
function draw_namebar(g,files) {
|
||||
return div_class("namebar",
|
||||
[table([tr([td(draw_name(g)),
|
||||
td_right([draw_plainbutton(g,files),
|
||||
upload_button(g),
|
||||
draw_closebutton(g)])])])])
|
||||
}
|
||||
|
||||
function draw_name(g) {
|
||||
return editable("h3",text(g.basename),g,edit_name,"Rename grammar");
|
||||
}
|
||||
|
||||
function draw_closebutton(g) {
|
||||
var b=button("X",function(){close_grammar(g);});
|
||||
b.title="Save and Close this grammar";
|
||||
return b;
|
||||
}
|
||||
|
||||
function draw_plainbutton(g,files) {
|
||||
var b2;
|
||||
function show_editor() { edit_grammar(g); }
|
||||
function show_plain() {
|
||||
files.innerHTML="<pre class=plain>"+show_grammar(g)+"</pre>"
|
||||
b.style.display="none";
|
||||
if(b2) b2.style.display="";
|
||||
else {
|
||||
b2=button("Show editor",show_editor);
|
||||
insertAfter(b2,b);
|
||||
}
|
||||
}
|
||||
var b=button("Show plain",show_plain);
|
||||
b.title="Show plain text representaiton of the grammar";
|
||||
return b;
|
||||
}
|
||||
|
||||
function upload_button(g) {
|
||||
var b=button("Upload",function(){upload(g);});
|
||||
b.title="Upload the grammar to the server to check it in GF and test it in the minibar";
|
||||
return b;
|
||||
}
|
||||
|
||||
function lang(code,name) { return { code:code, name:name} }
|
||||
function lang1(name) {
|
||||
var ws=name.split("/");
|
||||
return ws.length==1 ? lang(name.substr(0,3),name) : lang(ws[0],ws[1]);
|
||||
}
|
||||
var languages =
|
||||
map(lang1,"Amharic Arabic Bulgarian Catalan Danish Dutch English Finnish French German Hindi Ina/Interlingua Italian Latin Norwegian Polish Ron/Romanian Russian Spanish Swedish Thai Turkish Urdu".split(" "));
|
||||
//languages.push(lang("Other","Other"));
|
||||
|
||||
var langname={};
|
||||
//for(var i=0;i<languages.length;i++)
|
||||
for(var i in languages)
|
||||
langname[languages[i].code]=languages[i].name
|
||||
|
||||
function add_concrete(g,el) {
|
||||
var file=element("file");
|
||||
file.innerHTML="";
|
||||
var dc={};
|
||||
// for(var i=0;i<g.concretes.length;i++)
|
||||
for(var i in g.concretes)
|
||||
dc[g.concretes[i].langcode]=true;
|
||||
var list=[]
|
||||
// for(var i=0;i<languages.length;i++) {
|
||||
for(var i in languages) {
|
||||
var l=languages[i], c=l.code;
|
||||
if(!dc[c])
|
||||
list.push(li([a(jsurl("add_concrete2("+g.index+",'"+c+"')"),
|
||||
[text(l.name)])]));
|
||||
}
|
||||
file.appendChild(text("Pick a language for the new concrete syntax:"));
|
||||
file.appendChild(node("ul",{},list));
|
||||
}
|
||||
|
||||
function new_concrete(code) {
|
||||
return { langcode:code,params:[],lincats:[],opers:[],lins:[] };
|
||||
}
|
||||
|
||||
function add_concrete2(ix,code) {
|
||||
var g=local.get(ix);
|
||||
var cs=g.concretes;
|
||||
var i;
|
||||
for(var i=0;i<cs.length;i++) if(cs[i].langcode==code) break;
|
||||
if(i==cs.length) cs.push(new_concrete(code))
|
||||
save_grammar(g);
|
||||
open_concrete(g,i);
|
||||
}
|
||||
|
||||
function open_abstract(g) { g.current=0; reload_grammar(g); }
|
||||
function open_concrete(g,i) { g.current=i+1; reload_grammar(g); }
|
||||
|
||||
function td_gap(c) {return wrap_class("td","gap",c); }
|
||||
function gap() { return td_gap(text(" ")); }
|
||||
|
||||
function tab(active,link) {
|
||||
return wrap_class("td",active ? "active" : "inactive",link);
|
||||
}
|
||||
|
||||
function delete_concrete(g,ci) {
|
||||
var c=g.concretes[ci];
|
||||
var ok=c.params.length==0 && c.lincats.length==0 && c.opers.length==0
|
||||
&& c.lins.length==0
|
||||
|| confirm("Do you really want to delete the concrete syntax for "+
|
||||
langname[c.langcode]+"?");
|
||||
if(ok) {
|
||||
g.concretes=delete_ix(g.concretes,ci)
|
||||
if(g.current && g.current-1>=ci) g.current--;
|
||||
reload_grammar(g);
|
||||
}
|
||||
}
|
||||
|
||||
function draw_filebar(g) {
|
||||
var cur=(g.current||0)-1;
|
||||
var filebar = empty_class("tr","extensible")
|
||||
filebar.appendChild(gap());
|
||||
filebar.appendChild(
|
||||
tab(cur== -1,button("Abstract",function(){open_abstract(g);})));
|
||||
var cs=g.concretes;
|
||||
function del(ci) { return function() { delete_concrete(g,ci); }}
|
||||
function open_conc(i) { return function() {open_concrete(g,1*i); }}
|
||||
// for(var i=0;i<cs.length;i++)
|
||||
for(var i in cs) {
|
||||
filebar.appendChild(gap());
|
||||
filebar.appendChild(
|
||||
tab(i==cur,deletable(del(i),button(langname[cs[i].langcode],open_conc(i)),"Delete this concrete syntax")));
|
||||
}
|
||||
filebar.appendChild(td_gap(more(g,add_concrete,"Add a concrete syntax")));
|
||||
return wrap_class("table","tabs",filebar);
|
||||
}
|
||||
|
||||
function draw_file(g) {
|
||||
return g.current>0 // && g.current<=g.concretes.length
|
||||
? draw_concrete(g,g.current-1)
|
||||
: draw_abstract(g);
|
||||
}
|
||||
|
||||
function draw_startcat(g) {
|
||||
var abs=g.abstract;
|
||||
var startcat = abs.startcat || abs.cats[0];
|
||||
function opt(cat) { return option(cat,cat); }
|
||||
var m= node("select",{},map(opt,abs.cats));
|
||||
m.value=startcat;
|
||||
m.onchange=function() { abs.startcat=m.value; save_grammar(g); }
|
||||
return indent([kw("flags startcat"),sep(" = "),m]);
|
||||
}
|
||||
|
||||
function draw_abstract(g) {
|
||||
var kw_cat = kw("cat");
|
||||
kw_cat.title = "The categories (nonterminals) of the grammar are enumerated here.";
|
||||
var kw_fun = kw("fun");
|
||||
kw_fun.title = "The functions (productions) of the grammar are enumerated here.";
|
||||
var flags=g.abstract.startcat || g.abstract.cats.length>1
|
||||
? draw_startcat(g)
|
||||
: text("");
|
||||
return div_id("file",
|
||||
[kw("abstract "),ident(g.basename),sep(" = "),
|
||||
flags,
|
||||
indent([extensible([kw_cat,
|
||||
indent(draw_cats(g))]),
|
||||
extensible([kw_fun,
|
||||
indent(draw_funs(g))])])]);
|
||||
}
|
||||
|
||||
function add_cat(g,el) {
|
||||
function add(s) {
|
||||
var cats=s.split(/\s*(?:\s|[;])\s*/); // allow separating spaces or ";"
|
||||
if(cats.length>0 && cats[cats.length-1]=="") cats.pop();
|
||||
for(var i in cats) {
|
||||
var err=check_name(cats[i],"Category");
|
||||
if(err) return err;
|
||||
}
|
||||
for(var i in cats) g.abstract.cats.push(cats[i]);
|
||||
reload_grammar(g);
|
||||
return null;
|
||||
}
|
||||
string_editor(el,"",add);
|
||||
}
|
||||
|
||||
function delete_ix(old,ix) {
|
||||
var a=[];
|
||||
// for(var i=0;i<old.length;i++) if(i!=ix) a.push(old[i]);
|
||||
for(var i in old) if(i!=ix) a.push(old[i]);
|
||||
return a;
|
||||
}
|
||||
|
||||
function delete_cat(g,ix) {
|
||||
with(g.abstract) cats=delete_ix(cats,ix);
|
||||
reload_grammar(g);
|
||||
}
|
||||
|
||||
function rename_cat(g,el,cat) {
|
||||
function ren(newcat) {
|
||||
if(newcat!="" && newcat!=cat) {
|
||||
var err=check_name(newcat,"Category");
|
||||
if(err) return err;
|
||||
var dc=defined_cats(g);
|
||||
if(dc[newcat]) return newcat+" is already in use";
|
||||
g=rename_category(g,cat,newcat);
|
||||
reload_grammar(g);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
string_editor(el,cat,ren);
|
||||
}
|
||||
|
||||
function draw_cats(g) {
|
||||
var cs=g.abstract.cats;
|
||||
var es=[];
|
||||
var defined={};
|
||||
function eident(cat) {
|
||||
function ren(g,el) { rename_cat(g,el,cat); }
|
||||
return editable("span",ident(cat),g,ren,"Rename category");
|
||||
}
|
||||
function check(cat,el) {
|
||||
return ifError(defined[cat],"Same category named twice",el);
|
||||
}
|
||||
function del(i) { return function() { delete_cat(g,i); }}
|
||||
// for(var i=0; i<cs.length;i++) {
|
||||
for(var i in cs) {
|
||||
es.push(deletable(del(i),check(cs[i],eident(cs[i])),"Delete this category"));
|
||||
defined[cs[i]]=true;
|
||||
es.push(sep("; "));
|
||||
}
|
||||
es.push(more(g,add_cat,"Add more categories"));
|
||||
return es;
|
||||
}
|
||||
|
||||
function add_fun(g,el) {
|
||||
function add(s) {
|
||||
var p=parse_fun(s);
|
||||
if(p.ok) {
|
||||
g.abstract.funs.push(p.ok);
|
||||
reload_grammar(g);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
return p.error
|
||||
}
|
||||
string_editor(el,"",add);
|
||||
}
|
||||
|
||||
function edit_fun(i) {
|
||||
return function (g,el) {
|
||||
function replace(s) {
|
||||
var p=parse_fun(s);
|
||||
if(p.ok) {
|
||||
var old=g.abstract.funs[i];
|
||||
g.abstract.funs[i]=p.ok;
|
||||
if(p.ok.name!=old.name) g=rename_function(g,old.name,p.ok.name);
|
||||
if(show_type(p.ok.type)!=show_type(old.type))
|
||||
g=change_lin_lhs(g,p.ok);
|
||||
reload_grammar(g);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
return p.error;
|
||||
}
|
||||
string_editor(el,show_fun(g.abstract.funs[i]),replace);
|
||||
}
|
||||
}
|
||||
|
||||
function delete_fun(g,ix) {
|
||||
with(g.abstract) funs=delete_ix(funs,ix);
|
||||
reload_grammar(g);
|
||||
}
|
||||
|
||||
function draw_funs(g) {
|
||||
var funs=g.abstract.funs;
|
||||
var es=[];
|
||||
var dc=defined_cats(g);
|
||||
var df={};
|
||||
function del(i) { return function() { delete_fun(g,i); }}
|
||||
function draw_efun(i,df) {
|
||||
return editable("span",draw_fun(funs[i],dc,df),g,edit_fun(i),"Edit this function");
|
||||
}
|
||||
// for(var i=0;i<funs.length;i++) {
|
||||
for(var i in funs) {
|
||||
es.push(div_class("fun",[deletable(del(i),draw_efun(i,df),"Delete this function")]));
|
||||
df[funs[i].name]=true;
|
||||
}
|
||||
es.push(more(g,add_fun,"Add a new function"));
|
||||
return es;
|
||||
}
|
||||
|
||||
function draw_fun(fun,dc,df) {
|
||||
function check(el) {
|
||||
return ifError(dc[fun.name],
|
||||
"Function names must be distinct from category names",
|
||||
ifError(df[fun.name],"Same function defined twice",el));
|
||||
}
|
||||
return node("span",{},
|
||||
[check(ident(fun.name)),sep(" : "),draw_type(fun.type,dc)]);
|
||||
}
|
||||
|
||||
function draw_type(t,dc) {
|
||||
var el=empty("span");
|
||||
function check(t,el) {
|
||||
return ifError(!dc[t],"Undefined category",el);
|
||||
}
|
||||
// for(var i=0;i<t.length;i++) {
|
||||
for(var i in t) {
|
||||
if(i>0) el.appendChild(sep(" → "));
|
||||
el.appendChild(check(t[i],ident(t[i])));
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
function edit_name(g,el) {
|
||||
function change_name(name) {
|
||||
if(name!=g.basename && name!="") {
|
||||
var err=check_name(name,"Grammar");
|
||||
if(err) return err;
|
||||
g.basename=name
|
||||
reload_grammar(g);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
string_editor(el,g.basename,change_name)
|
||||
}
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
function draw_concrete(g,i) {
|
||||
var conc=g.concretes[i];
|
||||
return div_id("file",
|
||||
[kw("concrete "),ident(g.basename+conc.langcode),
|
||||
kw(" of "),ident(g.basename),sep(" = "),
|
||||
indent([extensible([kw("param"),draw_params(g,i)])]),
|
||||
indent([kw("lincat"),draw_lincats(g,i)]),
|
||||
indent([extensible([kw("oper"),draw_opers(g,i)])]),
|
||||
indent([kw("lin"),draw_lins(g,i)])
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
function draw_param(p,dp) {
|
||||
function check(el) {
|
||||
return ifError(dp[p.name],"Same parameter type defined twice",el);
|
||||
}
|
||||
return node("span",{},[check(ident(p.name)),sep(" = "),text(p.rhs)]);
|
||||
}
|
||||
|
||||
function add_param(g,ci,el) {
|
||||
function add(s) {
|
||||
var p=parse_param(s);
|
||||
if(p.ok) {
|
||||
g.concretes[ci].params.push(p.ok);
|
||||
reload_grammar(g);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
return p.error
|
||||
}
|
||||
string_editor(el,"",add);
|
||||
}
|
||||
|
||||
function edit_param(ci,i) {
|
||||
return function (g,el) {
|
||||
function replace(s) {
|
||||
var p=parse_param(s);
|
||||
if(p.ok) {
|
||||
g.concretes[ci].params[i]=p.ok;
|
||||
reload_grammar(g);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
return p.error;
|
||||
}
|
||||
string_editor(el,show_param(g.concretes[ci].params[i]),replace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function delete_param(g,ci,ix) {
|
||||
with(g.concretes[ci]) params=delete_ix(params,ix);
|
||||
reload_grammar(g);
|
||||
}
|
||||
|
||||
function draw_params(g,ci) {
|
||||
var conc=g.concretes[ci];
|
||||
conc.params || (conc.params=[]);
|
||||
var params=conc.params;
|
||||
var es=[];
|
||||
var dp={};
|
||||
function del(i) { return function() { delete_param(g,ci,i); }}
|
||||
function draw_eparam(i,dp) {
|
||||
return editable("span",draw_param(params[i],dp),g,edit_param(ci,i),"Edit this parameter type");
|
||||
}
|
||||
for(var i in params) {
|
||||
es.push(div_class("param",[deletable(del(i),draw_eparam(i,dp),"Delete this parameter type")]));
|
||||
dp[params[i].name]=true;
|
||||
}
|
||||
es.push(more(g,function(g,el) { return add_param(g,ci,el)},
|
||||
"Add a new parameter type"));
|
||||
return indent(es);
|
||||
}
|
||||
|
||||
function delete_lincat(g,ci,cat) {
|
||||
var i;
|
||||
var c=g.concretes[ci];
|
||||
for(i=0;i<c.lincats.length && c.lincats[i].cat!=cat;i++);
|
||||
if(i<c.lincats.length) c.lincats=delete_ix(c.lincats,i);
|
||||
reload_grammar(g);
|
||||
}
|
||||
|
||||
function draw_lincats(g,i) {
|
||||
var conc=g.concretes[i];
|
||||
function edit(c) {
|
||||
return function(g,el) {
|
||||
function ok(s) {
|
||||
if(c.template) conc.lincats.push({cat:c.cat,type:s});
|
||||
else c.type=s;
|
||||
reload_grammar(g);
|
||||
return null;
|
||||
}
|
||||
string_editor(el,c.type,ok)
|
||||
}
|
||||
}
|
||||
function del(c) { return function() { delete_lincat(g,i,c); } }
|
||||
function dlc(c,cls) {
|
||||
var t=editable("span",text(c.type),g,edit(c),"Edit lincat for "+c.cat);
|
||||
return node("span",{"class":cls},
|
||||
[ident(c.cat),sep(" = "),t]);
|
||||
}
|
||||
var dc=defined_cats(g);
|
||||
function draw_lincat(c) {
|
||||
var cat=c.cat;
|
||||
var err=!dc[cat];
|
||||
var l1=dlc(c,"lincat");
|
||||
var l2= err ? deletable(del(cat),l1,"Delete this lincat") : l1;
|
||||
var l=ifError(err,"lincat for undefined category",l2);
|
||||
delete dc[cat];
|
||||
return wrap("div",l);
|
||||
}
|
||||
function dtmpl(c) {
|
||||
return wrap("div",dlc({cat:c,type:"",template:true},"template")); }
|
||||
var lcs=map(draw_lincat,conc.lincats);
|
||||
for(var c in dc)
|
||||
lcs.push(dtmpl(c));
|
||||
return indent(lcs);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
function draw_oper(p,dp) {
|
||||
function check(el) {
|
||||
return ifError(dp[p.name],"Same operator definition defined twice",el);
|
||||
}
|
||||
return node("span",{},[check(ident(p.name)),text(" "),text(p.rhs)]);
|
||||
}
|
||||
|
||||
function add_oper(g,ci,el) {
|
||||
function add(s) {
|
||||
var p=parse_oper(s);
|
||||
if(p.ok) {
|
||||
g.concretes[ci].opers.push(p.ok);
|
||||
reload_grammar(g);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
return p.error
|
||||
}
|
||||
string_editor(el,"",add);
|
||||
}
|
||||
|
||||
function edit_oper(ci,i) {
|
||||
return function (g,el) {
|
||||
function replace(s) {
|
||||
var p=parse_oper(s);
|
||||
if(p.ok) {
|
||||
g.concretes[ci].opers[i]=p.ok;
|
||||
reload_grammar(g);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
return p.error;
|
||||
}
|
||||
string_editor(el,show_oper(g.concretes[ci].opers[i]),replace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function delete_oper(g,ci,ix) {
|
||||
with(g.concretes[ci]) opers=delete_ix(opers,ix);
|
||||
reload_grammar(g);
|
||||
}
|
||||
|
||||
function draw_opers(g,ci) {
|
||||
var conc=g.concretes[ci];
|
||||
conc.opers || (conc.opers=[]);
|
||||
var opers=conc.opers;
|
||||
var es=[];
|
||||
var dp={};
|
||||
function del(i) { return function() { delete_oper(g,ci,i); }}
|
||||
function draw_eoper(i,dp) {
|
||||
return editable("span",draw_oper(opers[i],dp),g,edit_oper(ci,i),"Edit this operator definition");
|
||||
}
|
||||
for(var i in opers) {
|
||||
es.push(div_class("oper",[deletable(del(i),draw_eoper(i,dp),"Delete this operator definition")]));
|
||||
dp[opers[i].name]=true;
|
||||
}
|
||||
es.push(more(g,function(g,el) { return add_oper(g,ci,el)},
|
||||
"Add a new operator definition"));
|
||||
return indent(es);
|
||||
}
|
||||
|
||||
function delete_lin(g,ci,fun) {
|
||||
var i;
|
||||
var c=g.concretes[ci];
|
||||
for(i=0;i<c.lins.length && c.lins[i].fun!=fun;i++);
|
||||
if(i<c.lins.length) c.lins=delete_ix(c.lins,i);
|
||||
reload_grammar(g);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
function arg_names(type) {
|
||||
function lower(s) { return s.toLowerCase(); }
|
||||
var names=map(lower,type);
|
||||
names.pop(); // remove result type
|
||||
var n,count={},use={};
|
||||
for(var i in names) n=names[i],count[n]=0,use[n]=0;
|
||||
for(var i in names) count[names[i]]++;
|
||||
function unique(n) {
|
||||
return count[n]>1 ? n+(++use[n]) : n;
|
||||
}
|
||||
return map(unique,names);
|
||||
}
|
||||
|
||||
function draw_lins(g,i) {
|
||||
var conc=g.concretes[i];
|
||||
function edit(f) {
|
||||
return function(g,el) {
|
||||
function ok(s) {
|
||||
if(f.template)
|
||||
conc.lins.push({fun:f.fun,args:f.args,lin:s});
|
||||
else f.lin=s;
|
||||
reload_grammar(g);
|
||||
return null;
|
||||
}
|
||||
string_editor(el,f.lin,ok)
|
||||
}
|
||||
}
|
||||
function del(fun) { return function () { delete_lin(g,i,fun); } }
|
||||
function dl(f,cls) {
|
||||
var l=[ident(f.fun)]
|
||||
for(var i in f.args) {
|
||||
l.push(text(" "));
|
||||
l.push(ident(f.args[i]));
|
||||
}
|
||||
l.push(sep(" = "));
|
||||
var t=editable("span",text(f.lin),g,edit(f),"Edit lin for "+f.fun);
|
||||
l.push(t);
|
||||
return node("span",{"class":cls},l);
|
||||
}
|
||||
var df=defined_funs(g);
|
||||
function draw_lin(f) {
|
||||
var fun=f.fun;
|
||||
var err= !df[fun];
|
||||
var l= err ? deletable(del(fun),dl(f,"lin"),"Delete this function") : dl(f,"lin")
|
||||
var l=ifError(err,"Function "+fun+" is not part of the abstract syntax",l);
|
||||
delete df[fun];
|
||||
return wrap("div",l);
|
||||
}
|
||||
function largs(f) {
|
||||
var funs=g.abstract.funs;
|
||||
for(var i=0;i<funs.length && funs[i].name!=f;i++);
|
||||
return arg_names(funs[i].type);
|
||||
}
|
||||
function dtmpl(f) {
|
||||
return wrap("div",
|
||||
dl({fun:f,args:largs(f),lin:"",template:true},"template"));
|
||||
}
|
||||
var ls=map(draw_lin,conc.lins);
|
||||
for(var f in df)
|
||||
ls.push(dtmpl(f));
|
||||
return indent(ls);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
function upload(g) {
|
||||
var dir=local.get("dir","");
|
||||
if(dir) upload2(g,dir);
|
||||
else ajax_http_get("upload.cgi?dir",
|
||||
function(dir) {
|
||||
local.put("dir",dir);
|
||||
upload2(g,dir);
|
||||
});
|
||||
}
|
||||
|
||||
function upload2(g,dir) {
|
||||
var form=node("form",{method:"post",action:"upload.cgi"+dir},
|
||||
[hidden(g.basename,show_abstract(g))])
|
||||
for(var i in g.concretes)
|
||||
form.appendChild(hidden(g.basename+g.concretes[i].langcode,
|
||||
show_concrete(g.basename)(g.concretes[i])));
|
||||
editor.appendChild(form);
|
||||
form.submit();
|
||||
form.parentNode.removeChild(form);
|
||||
}
|
||||
|
||||
function hidden(name,value) {
|
||||
return node("input",{type:"hidden",name:name,value:value},[])
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
function string_editor(el,init,ok) {
|
||||
var p=el.parentNode;
|
||||
function restore() {
|
||||
e.parentNode.removeChild(e);
|
||||
el.style.display="";
|
||||
}
|
||||
function done() {
|
||||
var edited=e.it.value;
|
||||
restore();
|
||||
var msg=ok(edited);
|
||||
if(msg) start(msg);
|
||||
return false;
|
||||
}
|
||||
function start(msg) {
|
||||
el.style.display="none";
|
||||
m.innerHTML=msg;
|
||||
p.insertBefore(e,el);
|
||||
e.it.focus();
|
||||
}
|
||||
var m=empty_class("span","error_message");
|
||||
var i=node("input",{name:"it",value:init},[]);
|
||||
if(init.length>10) i.size=init.length+5;
|
||||
var e=node("form",{},
|
||||
[i,
|
||||
node("input",{type:"submit",value:"OK"},[]),
|
||||
button("Cancel",restore),
|
||||
text(" "),
|
||||
m])
|
||||
e.onsubmit=done
|
||||
start("");
|
||||
}
|
||||
|
||||
function ifError(b,msg,el) { return b ? inError(msg,el) : el; }
|
||||
|
||||
function inError(msg,el) {
|
||||
return node("span",{"class":"inError",title:msg},[el]);
|
||||
}
|
||||
|
||||
function kw(txt) { return wrap_class("span","kw",text(txt)); }
|
||||
function sep(txt) { return wrap_class("span","sep",text(txt)); }
|
||||
function ident(txt) { return wrap_class("span","ident",text(txt)); }
|
||||
function indent(cs) { return div_class("indent",cs); }
|
||||
|
||||
function extensible(cs) { return div_class("extensible",cs); }
|
||||
|
||||
function more(g,action,hint) {
|
||||
var b=node("span",{"class":"more","title":hint || "Add more"},
|
||||
[text(" + ")]);
|
||||
b.onclick=function() { action(g,b); }
|
||||
return b;
|
||||
}
|
||||
|
||||
function editable(tag,cs,g,f,hint) {
|
||||
var b=edit_button(function(){f(g,e)},hint);
|
||||
var e=node(tag,{"class":"editable"},[cs,b]);
|
||||
return e;
|
||||
}
|
||||
|
||||
function edit_button(action,hint) {
|
||||
var b=node("span",{"class":"edit","title":hint || "Edit"},[text("%")]);
|
||||
b.onclick=action;
|
||||
return b;
|
||||
}
|
||||
|
||||
function deletable(del,el,hint) {
|
||||
var b=node("span",{"class":"delete",title:hint || "Delete"},[text("×")])
|
||||
b.onclick=del;
|
||||
return node("span",{"class":"deletable"},[b,el])
|
||||
}
|
||||
|
||||
function touch_edit() {
|
||||
var b=node("input",{type:"checkbox"},[]);
|
||||
function touch() {
|
||||
document.body.className=b.checked ? "nohover" : "hover";
|
||||
}
|
||||
b.onchange=touch;
|
||||
insertAfter(b,editor);
|
||||
insertAfter(wrap("small",text("Enable editing on touch devices. ")),b);
|
||||
|
||||
}
|
||||
|
||||
/* --- Initialization ------------------------------------------------------- */
|
||||
|
||||
//document.body.appendChild(empty_id("div","debug"));
|
||||
|
||||
initial_view();
|
||||
touch_edit();
|
||||
1
src/editor/simple/gf_abs.js
Normal file
1
src/editor/simple/gf_abs.js
Normal file
@@ -0,0 +1 @@
|
||||
/* Abstract syntax for a small subset of GF grammars in JavaScript */
|
||||
4
src/editor/simple/gfse.manifest
Normal file
4
src/editor/simple/gfse.manifest
Normal file
@@ -0,0 +1,4 @@
|
||||
CACHE MANIFEST
|
||||
# 5
|
||||
NETWORK:
|
||||
*
|
||||
42
src/editor/simple/index.html
Normal file
42
src/editor/simple/index.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html manifest="gfse.manifest">
|
||||
<head>
|
||||
<title>GF online editor for simple multilingual grammars</title>
|
||||
<link rel=stylesheet href="editor.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">
|
||||
<meta 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>
|
||||
|
||||
<noscript>
|
||||
This page does not work without JavaScript.
|
||||
</noscript>
|
||||
|
||||
<hr>
|
||||
<div class=modtime><small>
|
||||
HTML
|
||||
<!-- hhmts start --> Last modified: Wed Feb 16 15:10:52 CET 2011 <!-- hhmts end -->
|
||||
</small></div>
|
||||
<a href="about.html">About</a>
|
||||
<script type="text/javascript" src="../../runtime/javascript/minibar/support.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>
|
||||
</body>
|
||||
</html>
|
||||
21
src/editor/simple/localstorage.js
Normal file
21
src/editor/simple/localstorage.js
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
// We use localStorage to store the grammars,
|
||||
// see http://diveintohtml5.org/storage.html
|
||||
|
||||
var local={
|
||||
prefix:"gf.editor.simple.grammar",
|
||||
get: function (name,def) {
|
||||
var id=this.prefix+name
|
||||
return localStorage[id] ? JSON.parse(localStorage[id]) : def;
|
||||
},
|
||||
put: function (name,value) {
|
||||
var id=this.prefix+name;
|
||||
localStorage[id]=JSON.stringify(value);
|
||||
},
|
||||
remove: function(name) {
|
||||
var id=this.prefix+name;
|
||||
localStorage.removeItem(id);
|
||||
},
|
||||
get count() { return this.get("count",0); },
|
||||
set count(v) { this.put("count",v); }
|
||||
}
|
||||
12
src/editor/simple/save.hs
Normal file
12
src/editor/simple/save.hs
Normal file
@@ -0,0 +1,12 @@
|
||||
import Monad(zipWithM_)
|
||||
import System(getArgs)
|
||||
|
||||
main = save =<< getArgs
|
||||
|
||||
save [dir] =
|
||||
do fs@[ns,_] <- readIO =<< getContents
|
||||
save_all fs
|
||||
putStrLn $ unwords [n++".gf"|n<-ns]
|
||||
where
|
||||
save_all [ns,cs] = zipWithM_ write1 ns cs
|
||||
write1 n = writeFile (dir++"/"++n++".gf")
|
||||
86
src/editor/simple/slideshow.js
Normal file
86
src/editor/simple/slideshow.js
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
77
src/editor/simple/upload.cgi
Normal file
77
src/editor/simple/upload.cgi
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/bin/bash
|
||||
|
||||
bin=/Users/hallgren/www/bin
|
||||
|
||||
charset="UTF-8"
|
||||
AUTOHEADER=no
|
||||
|
||||
. $bin/cgistart.sh
|
||||
PATH=$PATH:/Users/hallgren/.cabal/bin
|
||||
export LC_CTYPE="UTF-8"
|
||||
style_url="editor.css"
|
||||
|
||||
make_dir() {
|
||||
dir="$(mktemp -d ../tmp/gfse.XXXXXXXX)"
|
||||
# chmod a+rxw "$dir"
|
||||
chmod a+rx "$dir"
|
||||
ln "$dir/../../grammars/grammars.cgi" "$dir"
|
||||
}
|
||||
|
||||
|
||||
check_grammar() {
|
||||
pagestart "Uploaded"
|
||||
# echo "$PATH_INFO"
|
||||
files=$(Reg from-url | LC_CTYPE=sv_SE.ISO8859-1 ./save "$dir")
|
||||
cd $dir
|
||||
begin pre
|
||||
if gf -s -make $files 2>&1 ; then
|
||||
end
|
||||
h3 OK
|
||||
begin ul
|
||||
[ -z "$minibar" ] || { li; link "$minibar?/tmp/${dir##*/}/" "Minibar"; }
|
||||
[ -z "$gfshell" ] || { li; link "$gfshell?dir=${dir##*/}" "GF Shell"; }
|
||||
end
|
||||
begin pre
|
||||
ls -l *.pgf
|
||||
else
|
||||
end
|
||||
begin h3 class=error_message; echo Error; end
|
||||
for f in *.gf ; do
|
||||
h4 "$f"
|
||||
begin pre class=plain
|
||||
cat -n "$f"
|
||||
end
|
||||
done
|
||||
fi
|
||||
hr
|
||||
date
|
||||
# begin pre ; env
|
||||
endall
|
||||
}
|
||||
|
||||
case "$REQUEST_METHOD" in
|
||||
POST)
|
||||
case "$PATH_INFO" in
|
||||
/tmp/gfse.*)
|
||||
style_url="../../$style_url"
|
||||
dir="../tmp/${PATH_INFO##*/}"
|
||||
check_grammar
|
||||
;;
|
||||
*)
|
||||
make_dir
|
||||
check_grammar
|
||||
rm -rf "$dir"
|
||||
esac
|
||||
;;
|
||||
GET)
|
||||
case "$QUERY_STRING" in
|
||||
dir) make_dir
|
||||
ContentType="text/plain"
|
||||
cgiheaders
|
||||
echo "/tmp/${dir##*/}"
|
||||
;;
|
||||
*) pagestart "Error"
|
||||
echo "What do you want?"
|
||||
endall
|
||||
esac
|
||||
esac
|
||||
Reference in New Issue
Block a user