@@ -15,6 +15,20 @@ let styles = new Map();
1515let groups = new Map ( ) ;
1616var lastGroupId = 0 ;
1717
18+ /**
19+ * Returns the document body's writing mode.
20+ */
21+ function getDocumentWritingMode ( ) {
22+ return getComputedStyle ( document . body ) . writingMode ;
23+ }
24+
25+ /**
26+ * Returns the closest element ancestor of the given node.
27+ */
28+ function getContainingElement ( node ) {
29+ return node . nodeType === Node . ELEMENT_NODE ? node : node . parentElement ;
30+ }
31+
1832/**
1933 * Registers a list of additional supported Decoration Templates.
2034 *
@@ -172,46 +186,108 @@ export function DecorationGroup(groupId, groupName) {
172186 }
173187
174188 let itemContainer = document . createElement ( "div" ) ;
175- itemContainer . setAttribute ( "id" , item . id ) ;
176- itemContainer . setAttribute ( "data-style" , item . decoration . style ) ;
177- itemContainer . style . setProperty ( "pointer-events" , "none" ) ;
178-
179- let viewportWidth = window . innerWidth ;
180- let columnCount = parseInt (
181- getComputedStyle ( document . documentElement ) . getPropertyValue (
182- "column-count"
183- )
184- ) ;
185- let pageWidth = viewportWidth / ( columnCount || 1 ) ;
186- let scrollingElement = document . scrollingElement ;
187- let xOffset = scrollingElement . scrollLeft ;
188- let yOffset = scrollingElement . scrollTop ;
189-
190- function positionElement ( element , rect , boundingRect ) {
189+ itemContainer . id = item . id ;
190+ itemContainer . dataset . style = item . decoration . style ;
191+ itemContainer . style . pointerEvents = "none" ;
192+
193+ const documentWritingMode = getDocumentWritingMode ( ) ;
194+ const isVertical =
195+ documentWritingMode === "vertical-rl" ||
196+ documentWritingMode === "vertical-lr" ;
197+
198+ const scrollingElement = document . scrollingElement ;
199+ const { scrollLeft : xOffset , scrollTop : yOffset } = scrollingElement ;
200+ const viewportWidth = isVertical ? window . innerHeight : window . innerWidth ;
201+ const viewportHeight = isVertical ? window . innerWidth : window . innerHeight ;
202+
203+ const columnCount =
204+ parseInt (
205+ getComputedStyle ( document . documentElement ) . getPropertyValue (
206+ "column-count"
207+ )
208+ ) || 1 ;
209+ const pageSize =
210+ ( isVertical ? viewportHeight : viewportWidth ) / columnCount ;
211+
212+ function positionElement ( element , rect , boundingRect , writingMode ) {
191213 element . style . position = "absolute" ;
192-
193- if ( style . width === "wrap" ) {
194- element . style . width = `${ rect . width } px` ;
195- element . style . height = `${ rect . height } px` ;
196- element . style . left = `${ rect . left + xOffset } px` ;
197- element . style . top = `${ rect . top + yOffset } px` ;
198- } else if ( style . width === "viewport" ) {
199- element . style . width = `${ viewportWidth } px` ;
200- element . style . height = `${ rect . height } px` ;
201- let left = Math . floor ( rect . left / viewportWidth ) * viewportWidth ;
202- element . style . left = `${ left + xOffset } px` ;
203- element . style . top = `${ rect . top + yOffset } px` ;
204- } else if ( style . width === "bounds" ) {
205- element . style . width = `${ boundingRect . width } px` ;
206- element . style . height = `${ rect . height } px` ;
207- element . style . left = `${ boundingRect . left + xOffset } px` ;
208- element . style . top = `${ rect . top + yOffset } px` ;
209- } else if ( style . width === "page" ) {
210- element . style . width = `${ pageWidth } px` ;
211- element . style . height = `${ rect . height } px` ;
212- let left = Math . floor ( rect . left / pageWidth ) * pageWidth ;
213- element . style . left = `${ left + xOffset } px` ;
214- element . style . top = `${ rect . top + yOffset } px` ;
214+ const isVerticalRL = writingMode === "vertical-rl" ;
215+ const isVerticalLR = writingMode === "vertical-lr" ;
216+
217+ if ( isVerticalRL || isVerticalLR ) {
218+ if ( style . width === "wrap" ) {
219+ element . style . width = `${ rect . width } px` ;
220+ element . style . height = `${ rect . height } px` ;
221+ if ( isVerticalRL ) {
222+ element . style . right = `${
223+ - rect . right - xOffset + scrollingElement . clientWidth
224+ } px`;
225+ } else {
226+ // vertical-lr
227+ element . style . left = `${ rect . left + xOffset } px` ;
228+ }
229+ element . style . top = `${ rect . top + yOffset } px` ;
230+ } else if ( style . width === "viewport" ) {
231+ element . style . width = `${ rect . height } px` ;
232+ element . style . height = `${ viewportWidth } px` ;
233+ const top = Math . floor ( rect . top / viewportWidth ) * viewportWidth ;
234+ if ( isVerticalRL ) {
235+ element . style . right = `${ - rect . right - xOffset } px` ;
236+ } else {
237+ // vertical-lr
238+ element . style . left = `${ rect . left + xOffset } px` ;
239+ }
240+ element . style . top = `${ top + yOffset } px` ;
241+ } else if ( style . width === "bounds" ) {
242+ element . style . width = `${ boundingRect . height } px` ;
243+ element . style . height = `${ viewportWidth } px` ;
244+ if ( isVerticalRL ) {
245+ element . style . right = `${
246+ - boundingRect . right - xOffset + scrollingElement . clientWidth
247+ } px`;
248+ } else {
249+ // vertical-lr
250+ element . style . left = `${ boundingRect . left + xOffset } px` ;
251+ }
252+ element . style . top = `${ boundingRect . top + yOffset } px` ;
253+ } else if ( style . width === "page" ) {
254+ element . style . width = `${ rect . height } px` ;
255+ element . style . height = `${ pageSize } px` ;
256+ const top = Math . floor ( rect . top / pageSize ) * pageSize ;
257+ if ( isVerticalRL ) {
258+ element . style . right = `${
259+ - rect . right - xOffset + scrollingElement . clientWidth
260+ } px`;
261+ } else {
262+ // vertical-lr
263+ element . style . left = `${ rect . left + xOffset } px` ;
264+ }
265+ element . style . top = `${ top + yOffset } px` ;
266+ }
267+ } else {
268+ if ( style . width === "wrap" ) {
269+ element . style . width = `${ rect . width } px` ;
270+ element . style . height = `${ rect . height } px` ;
271+ element . style . left = `${ rect . left + xOffset } px` ;
272+ element . style . top = `${ rect . top + yOffset } px` ;
273+ } else if ( style . width === "viewport" ) {
274+ element . style . width = `${ viewportWidth } px` ;
275+ element . style . height = `${ rect . height } px` ;
276+ const left = Math . floor ( rect . left / viewportWidth ) * viewportWidth ;
277+ element . style . left = `${ left + xOffset } px` ;
278+ element . style . top = `${ rect . top + yOffset } px` ;
279+ } else if ( style . width === "bounds" ) {
280+ element . style . width = `${ boundingRect . width } px` ;
281+ element . style . height = `${ rect . height } px` ;
282+ element . style . left = `${ boundingRect . left + xOffset } px` ;
283+ element . style . top = `${ rect . top + yOffset } px` ;
284+ } else if ( style . width === "page" ) {
285+ element . style . width = `${ pageSize } px` ;
286+ element . style . height = `${ rect . height } px` ;
287+ const left = Math . floor ( rect . left / pageSize ) * pageSize ;
288+ element . style . left = `${ left + xOffset } px` ;
289+ element . style . top = `${ rect . top + yOffset } px` ;
290+ }
215291 }
216292 }
217293
@@ -230,32 +306,38 @@ export function DecorationGroup(groupId, groupName) {
230306 }
231307
232308 if ( style . layout === "boxes" ) {
233- let doNotMergeHorizontallyAlignedRects = true ;
234- let clientRects = getClientRectsNoOverlap (
309+ const doNotMergeHorizontallyAlignedRects =
310+ ! documentWritingMode . startsWith ( "vertical" ) ;
311+ const startElement = getContainingElement ( item . range . startContainer ) ;
312+ // Decorated text may have a different writingMode from document body
313+ const decoratorWritingMode = getComputedStyle ( startElement ) . writingMode ;
314+
315+ const clientRects = getClientRectsNoOverlap (
235316 item . range ,
236317 doNotMergeHorizontallyAlignedRects
237- ) ;
238-
239- clientRects = clientRects . sort ( ( r1 , r2 ) => {
240- if ( r1 . top < r2 . top ) {
241- return - 1 ;
242- } else if ( r1 . top > r2 . top ) {
243- return 1 ;
318+ ) . sort ( ( r1 , r2 ) => {
319+ if ( r1 . top !== r2 . top ) return r1 . top - r2 . top ;
320+ if ( decoratorWritingMode === "vertical-rl" ) {
321+ return r2 . left - r1 . left ;
322+ } else if ( decoratorWritingMode === "vertical-lr" ) {
323+ return r1 . left - r2 . left ;
244324 } else {
245- return 0 ;
325+ return r1 . left - r2 . left ;
246326 }
247327 } ) ;
248328
249329 for ( let clientRect of clientRects ) {
250330 const line = elementTemplate . cloneNode ( true ) ;
251- line . style . setProperty ( "pointer-events" , "none" ) ;
252- positionElement ( line , clientRect , boundingRect ) ;
331+ line . style . pointerEvents = "none" ;
332+ line . dataset . writingMode = decoratorWritingMode ;
333+ positionElement ( line , clientRect , boundingRect , documentWritingMode ) ;
253334 itemContainer . append ( line ) ;
254335 }
255336 } else if ( style . layout === "bounds" ) {
256337 const bounds = elementTemplate . cloneNode ( true ) ;
257- bounds . style . setProperty ( "pointer-events" , "none" ) ;
258- positionElement ( bounds , boundingRect , boundingRect ) ;
338+ bounds . style . pointerEvents = "none" ;
339+ bounds . dataset . writingMode = documentWritingMode ;
340+ positionElement ( bounds , boundingRect , boundingRect , documentWritingMode ) ;
259341
260342 itemContainer . append ( bounds ) ;
261343 }
@@ -276,9 +358,9 @@ export function DecorationGroup(groupId, groupName) {
276358 function requireContainer ( ) {
277359 if ( ! container ) {
278360 container = document . createElement ( "div" ) ;
279- container . setAttribute ( "id" , groupId ) ;
280- container . setAttribute ( "data- group" , groupName ) ;
281- container . style . setProperty ( "pointer-events" , "none" ) ;
361+ container . id = groupId ;
362+ container . dataset . group = groupName ;
363+ container . style . pointerEvents = "none" ;
282364 document . body . append ( container ) ;
283365 }
284366 return container ;
0 commit comments