Anony-bot 30 June 2010
A little robot using the Robots V2 API to expose a wave to a URL. If the URL is shared, then anyone can post anonymously on the wave.
A little robot using the Robots V2 API to expose a wave to a URL. If the URL is shared, then anyone can post anonymously on the wave.
Can you tell that the screenshot on the left is actually an embedded wave? Probably not.
First, a little overview on what this is. A lot of people are interested in using Google Wave as a commenting system, however the official embed api at the current state suffers from several shortfalls. It’s tedious to operate a wave-integrated commenting system. As an author, you have to manually create a wave for every post, copy the content over to the wave, get the wave ID, and paste it into your template. Every time you change your post, you have to yet again manually sync the page with wave. Once you’ve gotten the wave into the page, the embedded wave itself has a few issues. You can’t style it using CSS, and you can only change the background color and font. The width can’t adjust to the width of your page after the initial load. There’s no way to get rid of or change the style of the huge blue border around it. It forces it’s own scroll bar which looks nothing like anything else on the page and prevents intuitive navigation of comments using the same scrollbar as the rest of the page. Lastly, there’s no way for anonymous readers to comment on the post.
For each of these issues, the new w2_embed api offers a solution. Instead of using iframes to hack another page onto the current one, this one is built into the page, injecting elements directly onto the page’s DOM. These elements can be styled in nearly any imaginable way through CSS and HTML. The sandboxed and namespaced single-function API allows for multiple waves on one page with less than 10KB of JS overhead (Compare to about 1.4 megabytes). Instead of relying on a special Wave ID, the app uses a centralized and remotely stored database which associates any identifier you provide with functional wave IDs. That means, that instead of using a string like googlewave.com!w+Blsm0RMW_A
, you have the URL of your blog, say 2010/06/wave-embed-api
. Not only that, but it can automatically pull the title of your wave, the authors, the permalink and tags from the post, automatically create a wave from that with the same tags and title with the content automatically set as the root blip as well as a permalink back to the blog post. When viewed from wave, the user can follow, unfollow, search and find waves using the exact same familiar interface. On your blog, you can make the wave backend entirely seamless and subtle. Since it’s built on top of Anonybot, people can reply blips without having google wave accounts right from your blog.
It’s all part of a new embed API built upon Anony-bot (which itself is built upon microwave, which is built on wave reader, which is built upon the prototype python desktop client… Someone should make a wordpress plugin for this to make this family history even longer).
The last post noted the beginning of this project, and here is part two. The API has been cleaned up a lot. The code has been simplified. Features added and design changed.
The major change is a total decoupling of it and the mainline Anonybot codebase. There are some major implications of the change. In the original prototype of the API, it still had the iconic blip context menu inherited from microwave. That’s gone. So is the participants bar, link to real wave client, blip html, blip content, reply box, and everything. There are now no required or automatically added html/dom elements. Everything is specified through a simple HTML templating engine. Since the code isn’t shared, changes from each individual project now on probably won’t be instantly applied to both. This has advantages and disadvantages. On the plus side, is that changes to anonybot won’t break the embed api. However, bug fixes on one may not propagate to the other. The code can be much smaller since features like search, which was previously hidden, can now be totally removed. Also, since it’s been rewritten from scratch as an embed api, it no longer exposes any extra globals and multiple embeds can be done for each page.
The biggest change is the templating engine. Before, it only worked with the blip design, but now it’s totally integral to every aspect of the interface. Lets walk through the basics of the API, starting with a minimal example.
All the content is wrapped in a huge hidden `div`, and inside is one element with the `wavebody` class. That huge wave is the scope of the templating engine. That means that if you put `{{1+1}}` anywhere inside the `div`, it’ll show up as 2. Place it anywhere outside the div, and it shows up as `{{1+1}}`. Simple, right? The `wavebody` element is where the actual blips will go. But since there aren’t any blips beforehand, `wavebody` is filled with the template for a single blip. The content gets multiplied several times and that makes the wave. The template scheme is really simple. Basically, you just wrap arbitrary JS code within `{{` and `}}`. The code gets executed (in the scope of the wave) and the block gets replaced with the return value of the script. When you’re inside `wavebody` there are a few additional blip-specific things that you can add. Now we have to learn the magical snippets of code you can use inside the curly brackets. One of the objects exposed to the templates is `wave`. The nice thing is that it mirrors [the wave api](http://code.google.com/apis/wave/extensions/robots/protocol.html#Wavelets). So the wave object exposes the following attributes (copied from the google page). * `creationTime` denotes the [Unix time](http://en.wikipedia.org/wiki/Unix_time) at which this wavelet was created. * `creator` denotes the address of the participant who created this wavelet. * `lastModifiedTime` denotes the [Unix time](http://en.wikipedia.org/wiki/Unix_time) at which the wavelet was last modified by any participant. * `participants` contains an array of participant IDs for all participants on the wave. * `rootBlipId` contains the Blip ID of the root blip. * `title` contains the title of the wavelet, which by default consists of the first line of text up to the first carriage return. * `version` contains the version of this wavelet. Each atomic operation on a wavelet increases this version number. * `waveId` contains the Wave ID of this wavelet. * `waveletId` contains the wavelet ID of this wavelet. Note that for waves which contain only one wavelet (that don’t have private conversations, in other words), this wavelet ID is usually of the form `conv+root` indicating that the wavelet is identical to the conversation root, the root wave. * `dataDocuments` contains a dictionary (associated array) of the IDs and data of any data documents attached to this wavelet. In addition, there are a few non-standard components. * `permalink` contains a link to the waveref which points to the specific wave on the real wave client * `blips` contains a js object (similar to a hash or dictionary) of blipIDs associated to blip objects * `bliparr` contains an array of blip ids in no particular order, most frequently used for getting the number of replies in a conversation in `{{wave.bliparr.length-1}}` There are also some functions you can call. For example, `wave.creationTime` is in the form of a unix timestamp. That probably isn’t too useful to an ordinary user, so a `format_time` function is there to format it like “6/30 10:45am”. If you want to have some other custom datetime format, get me a script to do it in javascript in less than 500 bytes and I’ll include it in the next version. * `format_time(int)` Format a unix-style date as a human-readable “6/30 10:45am” or if you prefer “m/d h:MMtt” * `render(blip)` Render a blip and return the HTML. Most of the time, it’s unnecessary as there’s a blip.html attribute There are a few actions that you can do. Certain strings like `{{addparticipant}}` can be used in `onclick=` attributes to trigger certain actions on various events.Here you see a link and a button. The link has been configured to prompt the user for a participant ID to add when clicked, and the button was set to prompt the user for a tag to add when clicked. There are several such actions.
addparticipant
Prompt the suer for a wave address to add to the wavereload
Reload the current waveaddtag
Prompt the user for a tag to addsetname
Prompt the user to set a username for posting
There is also the user list. It comes in two flavors, {{participantlist}}
and {{contributorlist}}
. The latter only works when inside a blip scope (the stuff inside wavebody
). They’re special because they’re actually interactive. When the list is long, it shortens it and creates a link to expand.
participantlist
A list made from wave.participants
contributorlist
Only works when in wavebody
, a simple users list made from blip.contributors
Now, is finally the stuff that goes in the blip. Like wave
it extends some wave api features, so I’ll start with pasting what google has documented.
blipId
contains the ID of blip in which the event occurred.
childBlipIds
contains an array of blip IDs for each of the blip’s children.contributors
denotes participants who have contributed to the state of this blip.creator
denotes the participant who created this blip.lastModifiedTime
denotes the Unix time at which this blip was last modified by any participant.content
contains the textual content of this blip.version
contains the version of this blip. Each atomic operation on a blip increases this version number.waveId
contains the Wave ID associated with this blip.waveletId
contains the wavelet ID associated with this blip. Note that for waves which contain only one wavelet (that don’t have private conversations, in other words), this wavelet ID is usually of the form conv+root
indicating that the wavelet is identical to the conversation root, the root wave.
There are a few additional methods which, again are non-standard.
permalink
contains a link to the waveref which points to the specific blip on the real wave client
html
contains a string of the rendered (with formatting and annotations applied) content
There are also actions, similar to the global addparticipant
, reload
and setname
actions. They can still be used within wavebody
but there are some actions which can only work in the blip template.
edit
Add an edit box immediately below the blip, pre-populated with the text content of the blip
remove
Remove the blip, it will first issue a confirmation prompt to the userreply
Add a reply box immediately below the blip. If there is already a blip below, the reply box is indented.
Below is a little example showing how to use the blip
object.
Lastly is the JS part. Without it, nothing would ever show up. You need to include the currently 6.5KB JS library from http://anony-bot.appspot.com/assets/embed2.mini.js
. After that, you need a separate script tag anywhere after the big hidden super element. It must be outside the super element (unimaginable evils will happen if it isn’t). Inside that script tag needs to be a function call to w2_embed
. The first (and only) argument has to be a JS object with at least the element
key pointing to a DOM element which contains an element with the class wavebody
. There are lots of other config options, and here they are.
element
(Required) a dom element which contains a div with the “wavebody” class
identifier
The identifier for the wave that the server will use to generate a wave ID from, default: location.pathnameroot_title
If the identifier has not previously been associated with a wave ID, the wave will be created with the specified title. default: (no title)root_content
If the identifier has not previously been associated with a wave ID, the wave will be created with the specified content as the root blip. default: (no content)participants
The participants the wave should be created with if not previously associated with a wave ID, default: [], suggested: [‘public@a.gwave.com’]hideroot
Boolean whether or not to not render the root blipedit
A reference to a DOM element which can be templated into the edit box, should contain handlers for the actions {{submit}} and {{cancel}} as well as a textarea with the class “wavetext”tags
Set of tags to be added to the wave if the identifier has not previously been associated with a wave and must be created.api_root
The domain containing the server component (proxy and dictionary server). Default: anony-bot.appspot.comgadgets
Enable native gadgets (does not work)render_state
Display the user a gadget statechronological
Render blips chronologically (see Microwave’s “Classic Forum Layout” option)json_url
Location of JSON implementation to be loaded dynamically in case it’s not native to the browser default: http://anony-bot.appspot.com/assets/json2.mini.jswaveid
Skip the identifier lookup and directly reference a wave by it’s id You can see that the function call was saved to a variable named demo
. That’s useful because you can use that to reference later to manipulate things. The return of a w2_embed call is a JS object with the following keys.
config
A reference to the initial config objectreply(text)
Create a root-level replyreload()
Reload the wave.
So lets try putting everything together.
Afterthoughts: Just a little thing to add at the end. You can can create a a div with the class waveedit
as used above. It’s not strictly necessary, but it lets you customize things a bit more. You also need a CSS style for .wavethread with a padding-left (or margin-left) in order for threads to show up properly. Blips are automatically given a .waveblip class.
That screenshot on the top of the post, if you want to try it out. It’s here.
I’m not making any money off of this, so there’s no reason for me to hide the fallibilities of this solution from anyone. This, like any other solution is not perfect. There is no true wave-style real-time editing, it uses polling. It does however update every 10 seconds if it’s configured to do so using the poll_updates: true
configuration option. It can’t ever really do much better, since app engine doesn’t yet support pushing out real time data to attached clients, even though they announced the neat Channel API (still waiting to use it!) at I/O. Either way, that probably won’t help too much as w2_embed uses JSONP for client/server communication across multiple domains, and it’s unlikely that the channel api will allow for that. Also, since there’s no way to fetch a single blip, the user must request the entire wave to check the tiny 32 bit version integer if it’s been incremented, and if it has, then the whole wave has to be fetched again. It’s by no means efficient, and so my server would be very very sad if the polling interval was significantly increased. It’s not very SEO friendly. Though it’s not always that people want the comments to be indexed by search engines, so some people may care less. Accessibility-wise, it shouldn’t be too horrible if the screen reader/browser supports javascript and hugely depends on the HTML being used in conjunction with the script.
As of date, this was my longest blog post ever, and I would be sad if nobody commented (ironically using my non-wave-based commenting system). And I’m not posting my source code unless someone comments, so, an incentive :P
Over a few days, things can change fairly quickly. There have been several speed improvements, a new Forum-Style blip rendering option which arranges blip linearly by the time edited with each containing a formatted quote of the parent to establish context. Attachments are now fully supported, including thumbnails and download links. The operations engine was totally rewritten which uses asynchronous XMLHttpRequest, a new callback based system and support for a batch operations (which means fewer requests and faster responses). A wavelet header containing a list of all participants in the entire wave has been added, as well as an Add Participant button. A specialized, extremely fast gadget viewer was added, which allows for blazingly fast rendering of two popular gadgets (and more will come), it works by bypassing the entire gadget infrastructure and loading trusted code directly inline with the DOM. There is a “New Wave” button which allows people to create new waves directly from the client. The OAuth backend was authenticated with google, for more secure login transactions. Blips have a new context menu which allows for features such as Delete Blip, Edit Blip and Change Title. A full changelog can be found here.
The Ajax Animator (Wave/Mini interface version) now has support for basic Onion skinning, where the last keyframe is semitransparently placed in the background to aid in the positioning of the next keyframe. It’s now enabled by default at 20% opacity but can be disabled.
To disable it, click the advanced button on the right side of the toolbar and find the entry labeled Onion Skinning, there you can set the onion skinning opacity or disable it with 0% opacity.
Loading a 500 blip wave in Google’s GWT Client takes 3:34 to get to a usable state (Where the scroll bar works) on a 3ghz Core2 Duo (whose extra core admittedly won’t do much). It also uses 972 MB of RAM.
Loading the same wave in my Wave Reader, takes 678 milliseconds. A 315x speed-up. Also, my client is totally unoptimized, pure 30KB of javascript. On top of those features, anyone can view waves, without a google account, individual blips can be linked to, it supports rendering almost everything Wave can, that is gadgets, inline replies, nesting, font color/size, italics, bold, everything you could probably expect. Interestingly, when you add an attachment to Wave, and delete the parent blip, it still stores the attachment on the current wave state, and this client can display/link to them without using Playback. There is an option to generate plain simple HTML for turning a Wave into a standalone page or Website. Private waves can be exposed read-only as a website simply by adding the gwavereader@googlewave.com username.
Using it is simple, take a Wave https://wave.google.com/wave/#restored:wave:**googlewave.com!w%252Br5lewFqCA** and then put it after the Wave Reader URL http://antimatter15.com/misc/wave/read.html?**googlewave.com!w%252Br5lewFqCA** And magically you have a super awesome URL to link to.
You can learn some tricks on how to use it to do some more awesome things http://antimatter15.com/misc/wave/read.html?googlewave.com!w%252BrnG0vaFXA such as the before mentioned HTML generation.
Samples (Some random waves): http://antimatter15.com/misc/wave/read.html?googlewave.com!w%252Br5lewFqCA (New for 4.6 Inline reply support) http://antimatter15.com/misc/wave/read.html?Ze3l0mj0A http://antimatter15.com/misc/wave/read.html?oPg9HfEXE&beta http://antimatter15.com/misc/wave/read.html?Mu9eK7j2H http://antimatter15.com/misc/wave/read.html?AhbL5fooD&beta http://antimatter15.com/misc/wave/read.html?googlewave.com!w+UDMZOGpSG
I made an extension which shows a green sign (like gmail) on the side whenever there are blips outside the viewport. It allows you to quickly and effectively use keyboard navigation without blindly checking above and below, or generally scroll without needing to guess the position.
It comes as a Chrome Extension and also as a Greasemonkey Userscript.
Closure Tools were recently released by Google to allow for others to use the same rich libraries that Google uses internally. Its used by Gmail, Google Docs and Google Maps, but interestingly also used by Google Wave. While they don’t actually announce that Closure is used in Wave, the view-source obfuscated mess from Wave does strongly show it.The reason why its use in wave is more interesting than most others is that Wave is in GWT, while the rest are pure JS. This implies that an interface to the Closure Library may be coming to GWT. While the interface doesn’t seem to be made from Closure, there are references to “closure_hashCode” and “goog.net.BrowserChannel”.
An issue now with the floods (is there any post about wave complete without a pun?) of people in wave, the with:public channel (of waves!) is becoming a unusable tsunami (har har har) of waves where clicking on one will end up opening up something else totally randomly as it has shifted down several by the time you click it.
So what is there to stop it? Well, I made this insanely great, floodwall: the Wave Pause button. It’s a simple bookmarklet that if you click, it stops all incoming updates to the search panel and clicking it again restores it.
The functional bookmarklet can be found at http://antimatter15.com/misc/wave/pause.html which is a rather hideous looking page hacked together quickly.
How does that work? Well, it turns out it’s actually not that easy. My first idea was to just find the function which updated things (it turned out to be Vwh) and replace it with function(){} and restore it later. That worked fine… But I remembered some evil crap that google said, about GWT source being browser-targeted. Incidentally that means Wave gets dfferent versions of the code with different function names, and if they ever update wave, all the function names screw up too!
Oh noes! What now? Well, it uses a sort of pseudo-heuristic approach. It searches for a big characteristic which is .style[w2g]. BUUT, w2g is fwg in chrome! So first it iterates through all variables and finds one whose value equals “display”. Then it takes that variable name, and searches for functions which have .style[name of string which equals display] twice. That reduces the pool significantly. Then it checks to make sure it has the string .innerHTML in it, and has 4 arguments. After that, well, you are reduced to a single function and you can use the same method described above.
Some people have probably heard of WaveBoard, the application for iPhone and OS X that claims to be a desktop client for wave. However, it’s really just a webkit browser that has some extra triggers for some better desktop integration. But it’s still not a real Wave client. Obviously cause doing so would be insanely hard and since the Client/Server protocol isn’t released, has to be reverse engineered.
So I reverse engineered part of Wave that allows an entire wave to be downloaded and parsed into blips with tree structure.
I’m careful to title this a Wave desktop reader because it only does reading and in the crudest of forms.
http://static-bot.appspot.com/view/?id=googlewave.com!w+Ze3l0mj0A the state of Google Wave does seem to have changed, so this post, from June 30th 2010 at 6:43am (which is what Wordpress says, but I have a hard time believing as I usually am not up that early. But obviously this commentary is from much after, after the great wave-announcement of the end of the year) Anyway, static-bot (which later became wavereader and then anonybot) has deceased in the most horrific way, and while the original Wave is still accessible, it probably won’t be for long (by the time I have my next eruption of nostalgia in a few months), Wave may have already died. So It’s probably best to paste information that was there into this post
For the Google Wave Client/Server protocol documentation, scroll down. For documentation which relates to stuff other than loading a wave (eg, search, playback, etc) See “Google Wave Protocol Documentation”
I spent a bit of time with firebug trying to reverse engineer some parts of Wave’s complex inner workings. I’ve made a offline wave reader using this amazing technology.
This is way different from Waveboard which is an embedded Webkit for a mac application which has some extra bindings for somewhat better desktop integration. It is not a real desktop client (technically, neither is this, but this is closer). This one is based on a reverse engineered part of the Wave client-server protocol (which hasn’t been released, hence reverse engineering).
But first, to make clear the limitations:
- Does not contain a web renderer so no images, embeds, gadgets, etc
- Does not maintain an active connection for real time updates
- Can not submit updates (no OT)
- Can not list waves
- Can not create waves
So what can it do? It can read a wave, and create a raw text backup. While not terribly exciting, it’s still progress. It is usually quite a bit faster than Wave at loading things.
**Download (Read Usage below):
** http://dl.getdropbox.com/u/1024307/pywavereader/login.py
http://dl.getdropbox.com/u/1024307/pywavereader/read.py
**Usage:
** Yay, the fun part. This isn’t very user friendly, so be warned. Unless you’ve used the command line before, it may be insanely scary.
- Download the files.
- Extract it somewhere
- Go to that directory from the command line
- Run python login.py USERNAME PASSWORD (Feel free to check the source if you’re afraid its gonna steal your credentials)
- Watch as some awesome info piles down and state.txt is generated
- Run python read.py WAVEID
- This part is weirdish. Since bash (for me) throws random errors and all the waves you can see are on googlewave.com!w+ anyway, a shorter wave ID can be used which lacks the part before the + sign. For example, this wave’s short ID is Ze3l0mj0A
- Watch as the wave’s contents are populated onto the command line notably without order (may be fixed eventually, probably not).
Future:
I’m trying to find out how the Wave searches are done and that might be next, soon a mostly usable read-only wave client may be possible. It would be great if people could help in this process.
Licensed under GPLv3 and made in Python (Yeah, commandline ftw). The code is pretty crappy, I’m not especially good at Python but at least it’s cross platform. Probably someone could hook it up to a web server to give access to public Wave content to the general public. Maybe this will spark some future wave desktop client.
And obviously, some time in september, Google will decide to shut down Wave because it was super experimental and rendered this all useless and one day on the cold not snowy monday afternoon of december twenty seventh the year 2010 though the relative epoch really isnt that important, so you could also say that it’s year 40 if you use the unix epoch. And obviously this was not in the initial post.
If you have a Google Wave developer account, you can visit the above link to use the gadget. It supports almost all the features of the full normal Ajax Animator and many more. It includes better Text, Images, Rotation, Resizing (still needs work), Layer Visibility, Stability, Platform Support, Export options, etc. However it does notably lack the entire old right (east) panel, which also means no undo, or redo. Also since it uses a different graphics editor (VectorEditor), it does not have all the transform options which were present in OnlyPaths. It also supports the whole real time editing that Wave is so famous for. Two people can concurrently edit the same frame at the same time or one user can watch the animation while the others are editing and see the animation develop.
If you aren’t fortunate enough to use wave, you can use it without the collaborative features at http://antimatter15.com/ajaxanimator/wave/
The problem with the implementation of lots of things like VectorEditor, SVG-edit and the Ajax Animator is that something i’m going to randomly call “echo”.
Basically, you might delete something on your client, and right after that, since the result isn’t real time, the app adds the shape back, and it gets deleted again. With multiple users, it gets more confusing, and there’s a random chance that it might not even delete.
In wave2 which is now a really complicated and massively slow library filled with tons of inelegant crap spanning almost 600 lines, the way it’s resolved (somewhat) is by not truly deleting things, but rather replacing it with DELETED/TIMESTAMP.
So I decided to mix up an old project, which I was almost about to migrate to GitHub but it’s still on Google Code (http://code.google.com/p/datastore-service/) which was basically a free service that allows easy prototyping of things by providing basic persistence (using JSONP). So I mixed it up with the Ajax Animator Standalone Player, so the Google Wave version of the ajax animator will have a Publish button which will upload things to a server and give you a shareable URL Such as http://antimatter15.com/ajaxanimator/player/player.htm?1e025941543678 while it would be great if a) the URL was shorter and b) the URL was more customizable, it works basically.
So I think that the Ajax Animator Wave thingy is almost done. I think it’s really awesome, there’s some new stuff in there that may help in collaboration. There is still a bit of dogfooding left (VectorEditor needs to be updated as while the new version of Raphael is being used which eliminates a lot of the hacks being used, the change in path APIs means that lines, polylines and paths all fail). So after a bit of more bug testing, I think it’s going to be pretty cool. It will definately be out by the time the 100,000 people join Google Wave (I can’t wait!). But I’m not sure if it will be today, next week, or the week after that.
So to show some cool stuff I can do, I’ll publish the first time this blog has ever really seen an animation by me. But here’s the first animation I’ve made (It uses the Wave, center and flip plugins which you can access in the script executor, but someone could easily and tediously do this by hand too):
The cool stuff being used here are first the ability to draw a rectangle, to multi-select, multi-copy, multi-paste and manual repeatition. After that, it’s multi-dragged down, and next frame then, the Ax.plugins\["center"]()
plugin is called which obviously centers all of them (by the Y axis, preserving the X one). Then it goes to the next keyframe and using Ax.plugins["Sine"](100,0.01)
(first arg defines multiplier for y axis and latter defines something I forgot, i think multiplier for the current X axis). Then the same function but with (100,0.02)
and then Ax.plugins.flip()
to make it look like the wave logo. Do some multi-select and set the color. After that cool stuff, it gets saved as text and opened in OnlyPaths Ajax Animator (which also demos a really cool feature called forwards-compatibility). It gets saved as a GIF and uploaded to my blog after that.
So I’ve been having issues with the Wave Gadget API (again). This one is simple, Wave isn’t really real time. So right after doing a submitDelta, you can’t get() the data and expect to have the new one instantly.
It’s been giving me some problems, but I’m getting around it by using my awesome wave gadget library which will now magically apply the changes immediately so you can access it even before things actually happen.
Also, partially due to this, things would be far more useful if you could get the date of each insertion or deletion.
I was working on a new little Wave State API update. It supports lists in the form of subkeys, and something very much like hierarchy and events on specific sub-nodes. This way you don’t have everything update (someone’s edits on frame 36 doesn’t matter if you’re on frame 42).
Everything is namespaced under a singleton global, wave2. It includes functions like
So I don’t actually have wave yet, but for all 2 of you (likely less) who have used pygowave, an open source third party implementation of the wave protocol. So there was how I developed it, I read through the APIs and tested on pygowave. So what does VectorEditor do that svg-edit and… erm… svg-edit don’t do?
First, VectorEditor for wave is really really real time. Waaay more real time than svg-edit (not really). But VectorEdit (VectorWave might be a nice name.. I’m going to try using that name from now on in this post) transmits the data such as even while the shape is still being drawn, rotated, or moved (rotation needs work). Another nice feature is that the transitions are animated so things are even more seamless.
Another important feature is shape locking. So when someone selects a shape, it gets locked and can’t be edited by anyone else. If anyone tries, an alert box appears saying “Shape shape:5sdfwef98dfe3ssdf is locked by user antimatter15@pygowave.p2k-network.org”. svg-edit (the latter) doesn’t support moving things after they’re created so it doesn’t really matter then, and I’m quite certain the former doesn’t do any type of locking.
And lets not forget the likely most important, yet totally untested feature that seperates VectorEditor from the rest: IE support, which is inherent since it uses Raphael for rendering, but it may not be necessary since google may be making some shim-type system of hacking svg awesomeness onto IE and making the whole VectorEditor project useless.
So if you want to try it out, go to pygowave, sign up, create a wave and add the
Edit: This post is mostly crap. I figured out how to solve my problems while writing this. But I’m posting it anyway because I feel obligated to spam the internet with my outdated thoughts
One of the main features of Google Wave is the ability to do live concurrent realtime editing. Sadly, this functionality isn’t easy or as far as I know even possible on Google Wave. Most of the time it doesn’t matter. The only time it would matter is if you are using live concurrent text editing within the gadget. Of course, that’s what I tried doing and that’s why i’m writing this blog post. So I still haven’t gotten my Google Wave Invite yet (hope soon!) so I’m experimenting with the pygowave project, which is a third party open source implementation of the Wave Protocol. The interface is missing something that is quite important to wave: multi-user text editing. So I decided to try implementing it as a Wave Gadget.
I really did understimate it’s complexity. While implementing isn’t usually too hard, the structure of the Wave Gadget API makes it more difficult than it could/should be. What the wave gadget API does is it has a real time updating key-value table. It’s quite flexible and useable most of the time, but for real time editing, not quite as useful as when something is changed.
For instance, if 2 people are editing the same thing, then whoever submits the data last is the one which wrote the data and the first person’s edits are ignored. Very rarely do 2 people need to edit the exact same thing. But when they do, it’s not easy to merge the things. A more chat-like system could work.
But while implementing that chat-type system on top of Wave is possible, it feels very inefficent, partly due to everything being cached at all states (to support Playback) and worry about something akin to garbage collection to delete things after everyone has patched their running copy.