Um, yeah - I know what you mean. I had a good chat with Jeremy awhile
ago about this exact problem (Jeremy wrote the JSON OT type). We came
up with the same problems you did. My opinion is that the JSON OT type
is insufficient for rich text editing. I think we need another OT
type, separate from JSON, specifically to express rich text. - and
which correctly captures the semantics of rich text documents.
I've written the whole architecture in such a way that adding types is
'easy' - basically, you just add more types into src/types/. Types
define a few functions (transform, apply, etc). Once you've defined a
type you can register it in src/types/index.coffee for nodejs and add
it to the cakefile to include it in the browser javascripts.
I would be super happy if you're keen to take a stab at this - I <3
pull requests.
The equivalent of the google wave rich text type looks like this:
['some normal text', [{bold:true}], 'some bold text', [{bold:null,
italics:true}], 'some italicised text']
Operations can be similar to normal text operations:
[{position:100, insert:'hi there'}]
I don't know what the behaviour should be if you insert text at the
end of an annotated region. Like, if you insert text after a bolded
region should it get bolded automatically? Maybe operations have to
specify all the annotations on text they're inserting. Unfortunately,
that means that as you type in a bolded region, every operation needs
to specify that the text is bold. Google wave inherits annotations,
but the logic for that is really complicated.
What do you think?
-Joseph
Unfortunately, I think a lot of the juicy stuff happened in person.
This is a pretty good intro to how the OT stuff works in general
(might be too basic):
http://www.youtube.com/watch?v=3ykZYKCK7AM
But - definitely watch this. This is Christian talking about how he
thinks wave could have done OT better:
http://www.youtube.com/watch?v=zo8uGlqQaCo - start at about 15 minutes.
>> I've written the whole architecture in such a way that adding types is
>> 'easy' - basically, you just add more types into src/types/. Types
>> define a few functions (transform, apply, etc). Once you've defined a
>> type you can register it in src/types/index.coffee for nodejs and add
>> it to the cakefile to include it in the browser javascripts.
>
> ok, digging there!
Cool.
Yep, though this optimisation shouldn't go in the editor. In general,
if you're describing text in blocks and you have to explicitly split /
join blocks, the OT stuff gets really hairy. You're much better off
having that stuff be automatic.
Wave did this by making annotation boundaries not take up space, so to
speak. So like, say you have this document:
<bold>abcde</bold><bold>fghij</bold>
... Thats an identical document to this:
<bold>abcdefghij</bold>
If you want to insert in the middle of that text, your operation says:
{Insert:'hi' position: 5}
The text doesn't take up space in the sense that when you specify a
position, the number just says how many characters to skip. Annotation
boundaries don't need to be skipped.
Of course, the downside of this is that you have to be clever if you
want to insert characters between tags or something. So, something
like:
{insert:[</bold>hi<bold>], position: 5} to insert unbolded text in the
middle of the document.
> 2. A bit more advanced variant: to take in account position of the
> cursor before user started to type text. So if user moved cursor on
> the left of the border of styles (user continues to type text), then
> we put new text in the left block of styles. If user returns cursor
> back from the right side (possibly to type one more word or correct
> the beginning of the style block), then we put new text in the right
> block. That would be quite easy for user to control that behavior. In
> case when user places cursor at the border with mouse click or screen
> touch we can follow point 1.
>
> To submit text insertion with this approach we need some more
> complicated API than just [{position:100, insert:'hi there'}]. Or it
> could be that API will remain as JSON operations, but for Rich-Text
> document should be represented with more complex model then just a
> string of text. For example an array of objects of some type like I
> described TextFragments before, or like your array of object of
> different types: annotation and text. And for those models we need
> some OT operations not only to create, but also to mutate (split and
> merge) objects used in the Rich-Text document model.
I think we need something different from the text API (so we can
describe style annotations and stuff), and different from the JSON
API. (Using the JSON API we'll need to split and join annotation
regions, and that'll get confusing fast).
Does that make sense?
Joseph
It also looks like you guys don't have any tests. I highly recommend
adding some at some stage - I found an enormous number of bugs in all
of sharejs's other OT types using unit tests & a random op generator.
-J
Here is a new update of rich-text editor: https://github.com/rizzoma/ShareJS
Now there is a simple server that allows run one instance of editor
for multiple users.
Anton