Skip to content

Commit e7bbec8

Browse files
authored
Merge pull request #30 from AtomGraph/result-document-and-canvas-caret
SaxonJS 3 result-document methods, and the caret survives clicks between blocks
2 parents 2cd3a2b + 379ca10 commit e7bbec8

3 files changed

Lines changed: 76 additions & 33 deletions

File tree

‎src/blocks.xsl‎

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -99,18 +99,27 @@ version="3.0">
9999
<xsl:template name="rdfae:replace-rendering">
100100
<xsl:param name="island" as="element()"/>
101101
<xsl:param name="content" as="node()*"/>
102-
<xsl:for-each select="$island/*[@data-role = 'rendering']">
103-
<xsl:sequence select="ixsl:call(., 'remove', [])[current-date() lt xs:date('2000-01-01')]"/>
104-
</xsl:for-each>
105-
<xsl:variable name="rendering" as="element()" select="rdfae:element('div')"/>
106-
<xsl:for-each select="$rendering">
107-
<ixsl:set-attribute name="data-role" select="'rendering'"/>
108-
</xsl:for-each>
109-
<!-- html method: XML's self-closing tags read as OPEN tags to the HTML
110-
fragment parser and swallow following siblings -->
111-
<ixsl:set-property name="innerHTML"
112-
select="serialize($content, map{ 'method': 'html' })" object="$rendering"/>
113-
<xsl:sequence select="ixsl:call($island, 'appendChild', [ $rendering ])[current-date() lt xs:date('2000-01-01')]"/>
102+
<xsl:variable name="rendering" as="element()">
103+
<div data-role="rendering">
104+
<xsl:copy-of select="$content"/>
105+
</div>
106+
</xsl:variable>
107+
<xsl:choose>
108+
<xsl:when test="$island/*[@data-role = 'rendering']">
109+
<xsl:for-each select="($island/*[@data-role = 'rendering'])[1]">
110+
<xsl:result-document href="?." method="ixsl:replace-element">
111+
<xsl:copy-of select="$rendering"/>
112+
</xsl:result-document>
113+
</xsl:for-each>
114+
</xsl:when>
115+
<xsl:otherwise>
116+
<xsl:for-each select="$island">
117+
<xsl:result-document href="?." method="ixsl:append-content">
118+
<xsl:copy-of select="$rendering"/>
119+
</xsl:result-document>
120+
</xsl:for-each>
121+
</xsl:otherwise>
122+
</xsl:choose>
114123
</xsl:template>
115124

116125
<!-- ............................ extension hooks ............................ -->

‎src/edit.xsl‎

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,16 @@ version="3.0">
379379
<xsl:template name="rdfae:init-region">
380380
<xsl:param name="region" as="element()"/>
381381

382+
<!-- the region is the canvas' focusable floor. Only leaf text hosts are
383+
contenteditable, so the surface between them - sibling margins, the
384+
handle gutter, a structural container's own box, chrome on a structural
385+
block - has no focusable ancestor at all, and a press there drops focus
386+
out of the editor entirely (which hosts read as leaving it, and a press
387+
on a drag handle cannot preventDefault without killing dragstart). With
388+
tabindex the region absorbs that focus instead: same idiom as the block
389+
images and object-block islands in rdfae:init-block, out of the tab
390+
order, and stripped by the canonical form -->
391+
<ixsl:set-attribute name="tabindex" select="'-1'" object="$region"/>
382392
<!-- boundary-normalize invalid host markup (bare text in blockquote,
383393
blocks inside p, stray inline at region level, ...) before
384394
editability init; the probe keeps the valid case zero-churn -->
@@ -390,8 +400,11 @@ version="3.0">
390400
<xsl:if test="$invalid">
391401
<xsl:variable name="fixed" as="node()*"
392402
select="cm:wrap-inline-runs(cm:normalize($region/node()), 'p')"/>
393-
<ixsl:set-property name="innerHTML"
394-
select="serialize($fixed, map{ 'method': 'html' })" object="$region"/>
403+
<xsl:for-each select="$region">
404+
<xsl:result-document href="?." method="ixsl:replace-content">
405+
<xsl:copy-of select="$fixed"/>
406+
</xsl:result-document>
407+
</xsl:for-each>
395408
</xsl:if>
396409
<!-- an empty region cannot hold a caret: seed a paragraph (the
397410
empty-blockquote idiom in rdfae:init-block) -->
@@ -1651,19 +1664,12 @@ version="3.0">
16511664
<xsl:with-param name="host" select="$host"/>
16521665
<xsl:with-param name="range" select="$range"/>
16531666
</xsl:call-template>
1654-
<!-- method html: XML's self-closing <p/> reads as an OPEN tag to the
1655-
HTML fragment parser and swallows following siblings -->
1656-
<xsl:variable name="stage" as="element()" select="rdfae:element('div')"/>
1657-
<ixsl:set-property name="innerHTML" select="serialize($blocks, map{ 'method': 'html' })" object="$stage"/>
1658-
<xsl:variable name="count" as="xs:integer" select="xs:integer(ixsl:get($stage, 'childNodes.length'))"/>
1659-
<xsl:iterate select="1 to $count">
1660-
<xsl:param name="anchor" select="$host"/>
1661-
<xsl:variable name="node" select="ixsl:get($stage, 'firstChild')"/>
1662-
<xsl:sequence select="ixsl:call($anchor, 'after', [ $node ])[current-date() lt xs:date('2000-01-01')]"/>
1663-
<xsl:next-iteration>
1664-
<xsl:with-param name="anchor" select="$node"/>
1665-
</xsl:next-iteration>
1666-
</xsl:iterate>
1667+
<xsl:variable name="count" as="xs:integer" select="count($blocks)"/>
1668+
<xsl:for-each select="$host">
1669+
<xsl:result-document href="?." method="ixsl:insert-after">
1670+
<xsl:copy-of select="$blocks"/>
1671+
</xsl:result-document>
1672+
</xsl:for-each>
16671673
<xsl:for-each select="$host/following-sibling::*[position() le $count]">
16681674
<xsl:call-template name="rdfae:init-block">
16691675
<xsl:with-param name="block" select="."/>

‎src/select.xsl‎

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,14 @@ version="3.0">
247247

248248
<!-- mousedown in a region: Shift extends the standing selection to the
249249
clicked point (the anchor never moves - preventDefault stops the
250-
native caret placement that would collapse it), a plain primary press
251-
arms a sweep anchor for the mousemove takeover (no preventDefault:
252-
native caret placement and in-host drags proceed untouched). SaxonJS
253-
dispatches an event at the innermost matching template only, so a
254-
press on the drag handle never reaches this (the handle owns its
255-
gesture) and chrome is guarded out explicitly -->
250+
native caret placement that would collapse it), a press on non-editable
251+
canvas places the caret at the point itself (no native placement exists
252+
there), and a plain primary press on a host arms a sweep anchor for the
253+
mousemove takeover (no preventDefault: native caret placement and
254+
in-host drags proceed untouched). SaxonJS dispatches an event at the
255+
innermost matching template only, so a press on the drag handle never
256+
reaches this (the handle owns its gesture) and chrome is guarded out
257+
explicitly -->
256258
<xsl:template match="*[contains-token(@class, 'rdfa-editor-content')]" mode="ixsl:onmousedown">
257259
<xsl:variable name="event" select="ixsl:event()"/>
258260
<xsl:variable name="region" as="element()" select="."/>
@@ -282,6 +284,32 @@ version="3.0">
282284
<ixsl:set-property name="sweepAnchorHost" select="()" object="rdfae:editor-state()"/>
283285
<ixsl:set-property name="sweepRegion" select="rdfae:root-of($anchor-node)" object="rdfae:editor-state()"/>
284286
</xsl:when>
287+
<!-- a press that landed outside every editable host: the surface
288+
between blocks, the handle gutter, a structural container's own
289+
box. The browser has no caret to place there and no focusable
290+
element below the region to focus, so the placement is ours -
291+
the nearest host takes focus and the caret lands at the point,
292+
rather than focus dropping off the canvas. A point that resolves
293+
into a non-editable parent (rdfae:caret-at-point escapes islands
294+
and chrome to a position beside them) has no host to focus and
295+
nothing to place: it falls through to the region's own tabindex -->
296+
<xsl:when test="empty($target/ancestor-or-self::*[@contenteditable = 'true'])
297+
and exists(rdfae:host-of($point?node))">
298+
<xsl:sequence select="ixsl:call($event, 'preventDefault', [])[current-date() lt xs:date('2000-01-01')]"/>
299+
<xsl:call-template name="rdfae:focus-caret">
300+
<xsl:with-param name="node" select="$point?node"/>
301+
<xsl:with-param name="offset" select="$point?offset"/>
302+
</xsl:call-template>
303+
<!-- the preventDefault killed the native drag session, so a sweep
304+
dragged out of here is synthetic throughout (no anchor host) -->
305+
<ixsl:set-property name="sweepAnchorNode" select="$point?node" object="rdfae:editor-state()"/>
306+
<ixsl:set-property name="sweepAnchorOffset" select="$point?offset" object="rdfae:editor-state()"/>
307+
<ixsl:set-property name="sweepAnchorHost" select="()" object="rdfae:editor-state()"/>
308+
<ixsl:set-property name="sweepRegion" select="$region" object="rdfae:editor-state()"/>
309+
<!-- focusin only fires when the host changes; a point in the same
310+
host still moves the caret, so sync from the choke point -->
311+
<xsl:call-template name="rdfae:update-breadcrumb"/>
312+
</xsl:when>
285313
<xsl:otherwise>
286314
<xsl:for-each select="$point">
287315
<ixsl:set-property name="sweepAnchorNode" select="?node" object="rdfae:editor-state()"/>

0 commit comments

Comments
 (0)