@@ -30,13 +30,15 @@ const getViewport = (doc, viewport) => {
3030}
3131
3232export class FixedLayout extends HTMLElement {
33- static observedAttributes = [ 'zoom' ]
33+ static observedAttributes = [ 'zoom' , 'spread' , 'odd-pages' ]
3434 #root = this . attachShadow ( { mode : 'closed' } )
3535 #observer = new ResizeObserver ( ( ) => this . #render( ) )
3636 #spreads
3737 #index = - 1
3838 defaultViewport
3939 spread
40+ #rtl
41+ #oddPages
4042 #portrait = false
4143 #left
4244 #right
@@ -57,6 +59,8 @@ export class FixedLayout extends HTMLElement {
5759 overflow: auto;
5860 }` )
5961
62+ this . #oddPages = this . getAttribute ( 'odd-pages' )
63+
6064 this . #observer. observe ( this )
6165 }
6266 attributeChangedCallback ( name , _ , value ) {
@@ -66,6 +70,15 @@ export class FixedLayout extends HTMLElement {
6670 ? parseFloat ( value ) : value
6771 this . #render( )
6872 break
73+ case 'spread' :
74+ this . spread = value
75+ this . respread ( )
76+ break
77+ case 'odd-pages' :
78+ this . #oddPages = value
79+ this . #determineOddPages( )
80+ this . respread ( )
81+ break
6982 }
7083 }
7184 async #createFrame( { index, src : srcOption } ) {
@@ -198,50 +211,85 @@ export class FixedLayout extends HTMLElement {
198211 return true
199212 }
200213 }
214+ #determineOddPages( ) {
215+ if ( this . #oddPages !== 'recto' && this . #oddPages !== 'verso' ) {
216+ this . #oddPages = 'recto'
217+ const firstPageSpread = this . book . sections [ 0 ] ?. pageSpread
218+ if ( this . #rtl && firstPageSpread === 'right' || ! this . #rtl && firstPageSpread === 'left' ) {
219+ this . #oddPages = 'verso'
220+ }
221+ }
222+ }
223+ #respread( ) {
224+ if ( this . spread === 'none' )
225+ this . #spreads = this . book . sections . map ( section => ( { center : section } ) )
226+ else {
227+ const rtl = this . #rtl
228+ const ltr = ! this . #rtl
229+ const oddPagesVerso = this . #oddPages === 'verso'
230+
231+ //Work out first page position based on settings and epub standard assumptions
232+ let firstPageSpread = this . book . sections [ 0 ] ?. pageSpread
233+ || ( ltr && oddPagesVerso || rtl && ! oddPagesVerso ? 'left' : 'right' )
234+
235+ //Determine if the spread must be flipped (never flip based on just embedded pagination direction)
236+ const flipSpreads = this . #oddPages === 'recto' && ( ltr && firstPageSpread === 'left' || rtl && firstPageSpread === 'right' )
237+ || this . #oddPages === 'verso' && ( ltr && firstPageSpread === 'right' || rtl && firstPageSpread === 'left' )
238+
239+ this . #spreads = this . book . sections . reduce ( ( arr , section , i ) => {
240+ const last = arr [ arr . length - 1 ]
241+ const isFirstPage = ! i
242+
243+ let { pageSpread } = section
244+ if ( ! pageSpread && isFirstPage ) pageSpread = firstPageSpread
245+ if ( flipSpreads ) {
246+ if ( pageSpread === 'left' ) pageSpread = 'right'
247+ else if ( pageSpread === 'right' ) pageSpread = 'left'
248+ }
249+
250+ const newSpread = ( ) => {
251+ const spread = { }
252+ arr . push ( spread )
253+ return spread
254+ }
255+
256+ if ( pageSpread === 'center' ) {
257+ const spread = newSpread ( )
258+ spread . center = section
259+ }
260+ else if ( pageSpread === 'left' ) {
261+ const spread = ! last || last . center || last . left || ltr ? newSpread ( ) : last
262+ spread . left = section
263+ }
264+ else if ( pageSpread === 'right' ) {
265+ const spread = ! last || last . center || last . right || rtl ? newSpread ( ) : last
266+ spread . right = section
267+ }
268+ else if ( ltr ) {
269+ if ( ! last || last . center || last . right ) newSpread ( ) . left = section
270+ else last . right = section
271+ }
272+ else {
273+ if ( ! last || last . center || last . left ) newSpread ( ) . right = section
274+ else last . left = section
275+ }
276+ return arr
277+ } , [ ] )
278+ }
279+ }
280+ respread ( ) {
281+ this . #respread( )
282+ this . goToSpread ( this . #index, this . #side, 'respread' )
283+ }
201284 open ( book ) {
202285 this . book = book
203286 const { rendition } = book
204- this . spread = rendition ?. spread
287+ this . spread = this . getAttribute ( 'spread' )
288+ if ( ! this . spread || this . spread === 'auto' ) this . spread = rendition ?. spread
205289 this . defaultViewport = rendition ?. viewport
206-
207- const rtl = book . dir === 'rtl'
208- const ltr = ! rtl
209- this . rtl = rtl
210-
211- if ( rendition ?. spread === 'none' )
212- this . #spreads = book . sections . map ( section => ( { center : section } ) )
213- else this . #spreads = book . sections . reduce ( ( arr , section , i ) => {
214- const last = arr [ arr . length - 1 ]
215- const { pageSpread } = section
216- const newSpread = ( ) => {
217- const spread = { }
218- arr . push ( spread )
219- return spread
220- }
221- if ( pageSpread === 'center' ) {
222- const spread = last . left || last . right ? newSpread ( ) : last
223- spread . center = section
224- }
225- else if ( pageSpread === 'left' ) {
226- const spread = last . center || last . left || ltr && i ? newSpread ( ) : last
227- spread . left = section
228- }
229- else if ( pageSpread === 'right' ) {
230- const spread = last . center || last . right || rtl && i ? newSpread ( ) : last
231- spread . right = section
232- }
233- else if ( ltr ) {
234- if ( last . center || last . right ) newSpread ( ) . left = section
235- else if ( last . left || ! i ) last . right = section
236- else last . left = section
237- }
238- else {
239- if ( last . center || last . left ) newSpread ( ) . right = section
240- else if ( last . right || ! i ) last . left = section
241- else last . right = section
242- }
243- return arr
244- } , [ { } ] )
290+ this . #rtl = book . dir === 'rtl'
291+ this . #determineOddPages( )
292+ this . #respread( )
245293 }
246294 get index ( ) {
247295 const spread = this . #spreads[ this . #index]
@@ -264,7 +312,7 @@ export class FixedLayout extends HTMLElement {
264312 }
265313 async goToSpread ( index , side , reason ) {
266314 if ( index < 0 || index > this . #spreads. length - 1 ) return
267- if ( index === this . #index) {
315+ if ( index === this . #index && reason !== 'respread' ) {
268316 this . #render( side )
269317 return
270318 }
@@ -298,12 +346,12 @@ export class FixedLayout extends HTMLElement {
298346 await this . goToSpread ( index , side )
299347 }
300348 async next ( ) {
301- const s = this . rtl ? this . #goLeft( ) : this . #goRight( )
302- if ( ! s ) return this . goToSpread ( this . #index + 1 , this . rtl ? 'right' : 'left' , 'page' )
349+ const s = this . # rtl ? this . #goLeft( ) : this . #goRight( )
350+ if ( ! s ) return this . goToSpread ( this . #index + 1 , this . # rtl ? 'right' : 'left' , 'page' )
303351 }
304352 async prev ( ) {
305- const s = this . rtl ? this . #goRight( ) : this . #goLeft( )
306- if ( ! s ) return this . goToSpread ( this . #index - 1 , this . rtl ? 'left' : 'right' , 'page' )
353+ const s = this . # rtl ? this . #goRight( ) : this . #goLeft( )
354+ if ( ! s ) return this . goToSpread ( this . #index - 1 , this . # rtl ? 'left' : 'right' , 'page' )
307355 }
308356 getContents ( ) {
309357 return Array . from ( this . #root. querySelectorAll ( 'iframe' ) , frame => ( {
0 commit comments