Fwd: Editor problems

0 views
Skip to first unread message

Ian C

unread,
Mar 27, 2016, 12:37:31 AM3/27/16
to Peter Kelly, CorinthiaTeam
Hi Peter,

I just changed my link to the editor code so the ln -s is
editor -> ../../../../../../corinthia-editor/

And subsequently changed the url in the iframe page. This means I can now debug the Typescript ok and step through so see what is happening.

I think we need to have insertParagraph, and deleteParagraph functions on the API?
Selecting a paragraph and calling deleteCharacter does not remove the paragraph it replaces the contents with and HTML BR?

Currently text drops down and new paragraphs are created on a EnterPressed call.

I can't see how else we can insert a new paragraph before the first?

Then do we need insertHeader deleteHeader too (of whatever level)?

And I stepped through the moveToEndOfDocument function... it is finding the SCRIPT tag and not the last text container.
Not sure how to fix it. Thinking on it.

Cheers,

Ian



---------- Forwarded message ----------
From: Ian C <i...@amham.net>
Date: Sat, Mar 26, 2016 at 11:58 AM
Subject: Editor problems
To: Peter Kelly <kell...@gmail.com>


Hi Peter,

I am trying to add other tests. Simple add a paragraph to the start, add one to the end.

To do this I have been playing around with a couple of functions

PrependParagraph

function insertParagraph(str) {
    for (var i = 0; i < str.length; i++) {
        api.cursor.insertCharacter(str[i], true);
    }
    api.cursor.enterPressed();
}

function autoEdit(api) {
    insertParagraph("Puff the magic dragon lives by the sea.");
}

This appears to work.... but not quite

Before the edit is my Corinthia generated paragraph.
<body id="661CD485-10">
<div id="661CD485-11">
<p class="Text_20_body" id="661CD485-17">Hello world</p>
</div>
<script src="../../js/require.js" data-main="../../js/iframe-code"></script>
</body>

After the edit
<div id="661CD485-11">
<p class="Text_20_body" id="661CD485-17">Puff the magic dragon lives by the sea.</p>
<p class="Text_20_body">Hello world</p>
</div>

It has replaced the contents of the original paragraph which moved to the second without the id.

So Corinthia now picks up the original as having been edited.


And as for appendParagraph I have

function insertParagraph(str) {
    for (var i = 0; i < str.length; i++) {
        api.cursor.insertCharacter(str[i], true);
    }
    api.cursor.enterPressed();
}

function autoEdit(api) {
    api.cursor.moveToEndOfDocument();
    insertParagraph("Puff the magic dragon lives by the sea.");
}

The moveToEnd seems to get confused because of the script we have there. I start with the same document and end up with

<body id="661CD485-10">
<div id="661CD485-11">
<p class="Text_20_body" id="661CD485-17">Hello world</p>
</div>
<p>
<script src="../../js/require.js" data-main="../../js/iframe-code">Puff the magic dragon lives by the sea.
</script>
</p>
<p>
<script src="../../js/require.js" data-main="../../js/iframe-code">
</script>
<br>
</p>
</body>

So my beautifully crafted prose ends up in the script tag? And hence disappears.

I've recreated it in the demo and have tried stepping through but can't quite fathom what is happening. Something about stepping back through the nodes from the end to find the first paragraph not working.

And as an aside Chrome steps through the debugger for the Typescript there but not in my test case... still looking at that.

Hope all is well, I'll keep looking at variations and other test cases.

--
Cheers,

Ian C



--
Cheers,

Ian C

Ian C

unread,
Mar 27, 2016, 6:51:14 AM3/27/16
to Peter Kelly, CorinthiaTeam
Hi,

I got my appendParagraph test generating the expected HTML by ...

function autoEdit(api) {
    //don't like this but it works
    api.cursor.moveRight();
    api.cursor.moveRight();
    api.cursor.moveRight();
    api.cursor.moveRight();
    api.cursor.moveRight();
    api.cursor.moveRight();
    api.cursor.moveRight();
    api.cursor.moveRight();
    api.cursor.moveRight();
    api.cursor.moveRight();
    api.cursor.moveRight();
    api.cursor.enterPressed();
    api.cursor.insertCharacter("Puff, the magic dragon, lives by the sea.");

    autosave = false;
}

My merge of the abstract trees shows....
Inline image 1
Green is new....

I think the MoveToEndOfDocument should be the equivalent? Or is it meant to be end of DOM?

And having been working through the code I see that insertCharacter takes a string and I can do the whole thing in one hit.

That should give me the added paragraph to try and pick up in the ODF Lenses.


--
Cheers,

Ian C

Ian C

unread,
Mar 28, 2016, 7:43:45 AM3/28/16
to Peter Kelly, CorinthiaTeam
Hi,

following from the test case above, I changed the ODF lenses to pick up the new paragraph.
So the resulting document now passes. For multiple paragraphs too.

Pushed that with some other tidy up changes to the test runner and logging.

Plan to move on to Headers. And adding them.

Then deletions and moving headers and paragraphs.

Peter there is some mapping done in the Word processing. May need some explanation there.


--
Cheers,

Ian C

Peter Kelly

unread,
Mar 28, 2016, 1:25:31 PM3/28/16
to corinthiateam
Hi Ian - been a busy week for me; I’ll respond in more detail to your other mails shortly, but here’s a few comments on some of the the issues you’ve raised:

On 27 Mar 2016, at 5:51 PM, Ian C <i...@amham.net> wrote:

Hi,

I got my appendParagraph test generating the expected HTML by ...

function autoEdit(api) {
    //don't like this but it works
    api.cursor.moveRight();
    api.cursor.moveRight();
    api.cursor.moveRight();
    api.cursor.moveRight();

The Input module in the API is helpful here. What you can do is:

1. First, call api.input.documentStartAnchor(), which gives you a special position reference that always refers to the start of the document (there are similar methods for the end of document, and also the start and end of selection).
2. Next, call api.input.positionRelativeTo() with the result of (1), the direction “forward”, and the offset being the number of characters you want to move by. This won’t actually move the cursor, but will give you a PositionRef object that refers to the location in the next where you want to insert.
3. Finally, all api.input.setSelectedTextRange(), passing the result of (2) as both the start and end parameters. When the start and end of the selection is the same, it’s interpreted as a cursor, so calling insertCharacter() will work in the same way as it does with your current code. Perhaps, for clarity, we could add a setCursorPosition() method which does the same but just takes the one parameter and calls through to api.input.setSelectedTextRange().

I recently wrote about how position objects and position references work at https://www.pmkelly.net/2016/corinthia-editor/

As you noted, insertCharacter() accepts a string containing an arbitrary number of characters. For ages I’ve been meaning to rename it to insertText() but never got around to it. I think doing so, and implementing setCursorPosition(), would be a nice easy starting point for you in terms of modifying the library.

Incidentally, the Input API came about as a result of needing to comply with the UITextInput interface (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInput_Protocol/) used on iOS. I originally tried to implement cursor movement and selection by providing all the necessary methods to do so, such as moveRight(). But it turned out the only way to do text input on iOS (at least at the time - this was around iOS 5 or 6 I think) was to let it manage all the movement logic, and be able to query for positions. However ultimately I think it’s resulted in a more useful API, avoiding the need for all the moveRight() calls.

I think the MoveToEndOfDocument should be the equivalent? Or is it meant to be end of DOM?

That’s an interesting issue actually. Most of the movement methods work with three classes of positions: “raw” (not explicitly mentioned in the code, but basically any node/offset), “ok for movement” (a place where the cursor is allowed to be) and “ok for insertion” (a place where it’s ok to insert text). This is implemented in position.ts. The algorithm for determining what is and isn’t a valid position for insertion of movement is crazily complicated, but it works. A while ago I came across an excellent article which describes a better way to do it, by one of the guys that wrote the blog editor for Medium: https://medium.com/medium-eng/why-contenteditable-is-terrible-122d8a40e480

Much of the complexity regarding paragraphs in our editor comes from the fact that in HTML you can potentially have text that resides *between* paragraph elements. In rendering terms, any sequence of inline elements (e.g. text, spans) that exists between two block elements (p, div, h1 etc) is effectively treated as a block - I think the proper term is an “anonymous block box”. See https://www.w3.org/TR/CSS2/visuren.html for all the gory details.

The editor code tries to keep everything in block-level elements, with a strong preference for <p>. So if you try to insert text between two <p> elements, or at the very start or end positions of the document (that is, node = document.body, offset = 0 or document.body.childNodes.length), it will actually search forwards or backwards for the nearest block-level element and do the insertion there. If there is any inline content that precedes (respectively, follows) the block element, it will wrap that in a paragraph. There are several instances in which the editor tries to force content to be in block elements - have a look at the code in hierarchy.ts, especially ensureInlineNodesInParagraph(), for where it does this.

In another email you mentioned you had several <p> elements inside a <div>. When I wrote the code, I didn’t really take this into consideration, and I think that depending on the circumstances, this may cause problems of the form of it either getting rid of the div, or treating the paragraphs incorrectly. I’ll have to take a look at the code to figure out exactly what it does in this situation and why (it’s been a long time since I touched that part).

I made an implicit assumption when writing much of the code that the documents the editor would deal with would have only a single level of block elements inside the body. This stems in part from the model of Word/OOXML documents, where this is always the case (well, excluding special stuff like frames), and also for the LaTeX’s concept of environments (though I realised just now these can actually be nested). In ODF, if I remember correctly, has a more flexible model regarding nesting compared to OOXML.

I agree that the editor should respect the nesting of block elements where a document already has them. We should try to fix this; I think the solution will likely only need to involve the code in hierarchy.ts and cursor.ts, possibly also formatting.ts.

Regarding the <script> element at the end of the document… I think what we need to do here is remove the element during initialisation, specifically in Main.init(). Originally when i wrote the code the scripts were always injected through “out of band” mechanisms like eval() that didn’t involve adding <script> elements to the DOM as we now do with require.js. So I think simply removing the script element as part of initialisation will solve the problem. I suggest adding a function called something like removeScripts() and call it from Main.init() to deal with this. In the particular case where we’re using require.js, it should be sufficient to just check the last element in the body.

--
Dr. Peter M. Kelly
kell...@gmail.com

(fingerprint 5435 6718 59F0 DD1F BFA0 5E46 2523 BAA1 44AE 2966)

signature.asc

Peter Kelly

unread,
Mar 29, 2016, 12:10:34 PM3/29/16
to corinthiateam
On 29 Mar 2016, at 6:29 PM, Ian C <i...@amham.net> wrote:

Hi Peter,

the function documentStartAnchor does not appear in the input module of the editor. A grep -R does not find it anywhere?

It’s in externalapi.ts: https://github.com/corinthia/corinthia-editor/blob/master/src/externalapi.ts#L388
signature.asc

Franz de Copenhague

unread,
Mar 30, 2016, 1:18:59 AM3/30/16
to Peter Kelly, corinthiateam
I have created basic package.json and gulpfile.js files to start working on UI components using the corinthia-editor API.

I am going to start using gulp as build utility to speed up the dev workflow. My first commit has a couple of gulp tasks to watch the typescript files and run a webserver with livereload.

Steps to use them:

Pull the files from corinthia-editor repository

npm install gulp -g

npm install

gulp serve

NOTE: package.json is incomplete, so far is used only to download the gulp dev-dependencies


-Franz


Subject: Re: Editor problems
From: kell...@gmail.com
Date: Tue, 29 Mar 2016 23:10:26 +0700
To: corint...@googlegroups.com
--
You received this message because you are subscribed to the Google Groups "CorinthiaTeam" group.
To unsubscribe from this group and stop receiving emails from it, send an email to corinthiatea...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages