-------- Original Message --------
Subject: A variation on Rado's graph editor
Date: Thu, 02 Jul 2009 13:29:37 -0700
From: Pat LeSmithe <qed777>
To: jason, rkirov, beezer
Hi Jason, Rado, and Rob!
Feeling a bit inspired, I put together a preliminary experiment with
contentEditable divs and Rado's graph editor:
http://www.math.uiuc.edu/~rkirov2/processing/grapheditor_live.html
I've attached an archive of a few files. The main file is
contedit.html, which pulls graphed.html, which pulls
processing.editor.min.js. Google provides jQuery/UI.
I tried using processingjs.org's processing.min.js, but it seemed the
strain on my computer was even greater. However, I don't know much
about *any* flavor of Processing. It's also quite possible that I broke
something during the mischief. One oddity: The canvas's location on
the page seems to affect the accuracy of clicks, at least in Firefox 3.5.
Other issues: Re-opening multiple editors can trigger a reload, or
perhaps just the randomizing routine. The styles may clash. I'm sure
there are many more...
Of course, this is only an example. There's also much to do, e.g.,
making the whole framework generic, working with selections, ensuring
editor instances don't clash, integrating with the notebook, maybe
converting the editor to pure JavaScript, etc.
I found this contentEditable demo somewhat informative:
http://www.quirksmode.org/dom/execCommand/
Take care!
Sincerely,
Mitesh Patel
This is really great! Thanks for posting the code!
Looking at this code last night helped me to have a better idea of what
I'm thinking as well.
I envision the sage code cells having javascript plugins. A plugin
would be a javascript object or something that, given a piece of sage
code, could return an html "widget" that represents that sage code in
some nice, possibly interactive way, and could be queried for a string
of Sage code that represents the current state of the widget.
The sage code cell (a designmode iframe or contenteditable div) would
then provide some way of activating the widget from a text selection (or
from an empty string, if no text is selected). When a widget is
activated, the following sort of thing is inserted into the code cell,
either replacing the selected text or just at the current position if no
text is selected:
<div class="widgetcontainer"><div class="code">sage code that the widget
represents</div><div class="widget">html code returned by the
plugin</span></div>
The code cell would provide a way to toggle the text and the widget
spans visible or hidden (or deleting it completely). Both the text and
the widget update each other on a change (i.e., if the user changes the
text, the widget updates; if the user changes the widget, the text
updates). Obviously there can be some shortcuts here if one or the
other is not visible.
When the code cell is evaluated, the text is extracted from each
widgetcontainer. In addition, metadata is sent to the sage server
marking where the widgets are (e.g., widget "GraphEditor" surrounds the
text from character position 120 to 133).
Here are some other plugins that could be implemented:
1. An image representation. When first invoked, it has a blank space.
Click on the blank space and a small javascript window opens up that
allows you to upload an image to the sage server. Then the widget
contains a picture of the image. The underlying Sage code is the sage
code that references the image file.
2. A list of points constructor -- if you want a series of 2d points,
then get something like Rado's editor to just return the list of
coordinates.
3. Like Rob Beezer has mentioned, an equation editor, matrix editor, etc.
4. A widget that helps you choose the sloane sequence (i.e., provides a
search form, displays the sequence, etc.). The underlying Sage code is
"sloane_sequence(<whatever sequence number you picked>)".
Really, with a flexible, easy plugin framework, the sky is the limit!
What do you think? I've started working on something like this from the
code that was attached above (thanks!), but really, there are people
that are way better at this (and may have more time) than me! Feel free
to jump in!
Thanks,
Jason
Thanks to everyone for their comments, ideas, and contributions, whether
they're either already here or forthcoming!
A quick note: I've posted a newer version at
http://trac.sagemath.org/sage_trac/ticket/6460
It should now work in Firefox 3/3.5 and sundry other browsers. But most
of the other the changes are cosmetic. I'm sure Jason is working on his
uber-cells...
By the way, does anyone have experience with the desktop package Tulip:
http://www.labri.fr/perso/auber/projects/tulip/
? Its focus appears to be 2D/3D interactive manipulation and
visualization of large graphs:
Rob Beezer wrote:
> Radoslav Kirov wrote:
>> [about an editor window that communicates directly with the server]
>
> Yes, this is more what I have in mind. But why not have the editor open
> as a method call on the graph? This would make the import button
> unnecessary, and there could be several windows open at once for several
> different graphs. In a way you would be binding the window to the
> graph, as a sort of property, I guess.
>
> I think Jason is thinking larger and is looking for a framework that
> will also allow for things like equation editors, or maybe pallettes for
> symbols, that sort of thing.
>
> Radoslav Kirov wrote:
>> [about the disadvantage of opening a window just for quick changes,
>> plus the potential overhead of real-time client-server communication]
>
> I hadn't considered the communication cost to a remote server. But then
> I don't think it has to be that expensive. I would not send the
> "dragging" information for a vertex, or highlighting a vertex for an
> edge change. I would send: vertex addition, vertex removal, new vertex
> location, edge addition, edge removal. As minor local changes, its not
> much information, and if the Graph class doesn't have the right methods
> to deal with them, we can add them. and they probably only happen at
> the rate of every few seconds.
Some ways, not necessarily graceful...
Pushed by the parent, if the timing is right:
var push = function (data) {
$('#iframe1').contents().find('div#hidden').val(data);
}
push(data);
Or call a frame's function:
$('#iframe1')[0].contentWindow.do_something(data);
A somewhat safer alternative:
var ifr = $('<iframe> ... </iframe>');
ifr.bind('load', function () { push(data) });
$('#something').append(ifr);
But 'load' can fire too early in Opera. A generic probabilistic workaround:
setTimeout('push(data)', 1000);
or
var wrap = function () {
push(data);
}
setTimeout(wrap, 1000);
Another way, pulled by the frame:
$(document).ready({
data = $('#textarea1', top.document).val();
});