diva.js 6.0.1 → 7.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/.clang-format +7 -0
  2. package/.github/workflows/npm-publish.yml +45 -0
  3. package/LICENSE +55 -0
  4. package/Makefile +75 -0
  5. package/README.md +15 -108
  6. package/elm.json +32 -0
  7. package/package.json +12 -59
  8. package/review/elm.json +52 -0
  9. package/review/src/ReviewConfig.elm +87 -0
  10. package/scripts/elm-esm.sh +40 -0
  11. package/scripts/minify-css.mjs +31 -0
  12. package/src/Filters.elm +1044 -0
  13. package/src/Main.elm +1217 -0
  14. package/src/Model.elm +213 -0
  15. package/src/Msg.elm +59 -0
  16. package/src/Utilities.elm +46 -0
  17. package/src/View/CollectionExplorer.elm +172 -0
  18. package/src/View/Helpers.elm +86 -0
  19. package/src/View/HtmlRenderer.elm +136 -0
  20. package/src/View/Icons.elm +159 -0
  21. package/src/View/ManifestInfoModal.elm +363 -0
  22. package/src/View/PageViewModal.elm +1046 -0
  23. package/src/View/Sidebar.elm +786 -0
  24. package/src/View/Toolbar.elm +189 -0
  25. package/src/View.elm +244 -0
  26. package/src/diva.ts +802 -0
  27. package/src/filters.ts +1843 -0
  28. package/src/styles/app.css +328 -0
  29. package/src/styles/collection.css +75 -0
  30. package/src/styles/modal.css +388 -0
  31. package/src/styles/sidebar.css +215 -0
  32. package/src/styles/theme.css +39 -0
  33. package/src/styles/toolbar.css +154 -0
  34. package/src/viewer-element.ts +1307 -0
  35. package/testing/index.html +52 -0
  36. package/testing/testing.html +231 -0
  37. package/tsconfig.json +12 -0
  38. package/AUTHORS +0 -22
  39. package/_site/diva.iml +0 -11
  40. package/build/diva.css +0 -554
  41. package/build/diva.css.map +0 -1
  42. package/build/diva.js +0 -9
  43. package/build/diva.js.map +0 -1
  44. package/build/plugins/download.js +0 -2
  45. package/build/plugins/download.js.map +0 -1
  46. package/build/plugins/manipulation.js +0 -2
  47. package/build/plugins/manipulation.js.map +0 -1
  48. package/build/plugins/metadata.js +0 -2
  49. package/build/plugins/metadata.js.map +0 -1
  50. package/diva.iml +0 -11
  51. package/index.html +0 -28
  52. package/karma.conf.js +0 -87
  53. package/source/css/_mixins.scss +0 -43
  54. package/source/css/_variables.scss +0 -50
  55. package/source/css/_viewer.scss +0 -462
  56. package/source/css/diva.scss +0 -15
  57. package/source/css/plugins/_manipulation.scss +0 -228
  58. package/source/css/plugins/_metadata.scss +0 -31
  59. package/source/img/adjust.svg +0 -11
  60. package/source/img/book-view.svg +0 -6
  61. package/source/img/close.svg +0 -6
  62. package/source/img/download.svg +0 -6
  63. package/source/img/from-fullscreen.svg +0 -8
  64. package/source/img/grid-fewer.svg +0 -6
  65. package/source/img/grid-more.svg +0 -6
  66. package/source/img/grid-view.svg +0 -6
  67. package/source/img/link.svg +0 -6
  68. package/source/img/metadata.svg +0 -9
  69. package/source/img/page-view.svg +0 -6
  70. package/source/img/to-fullscreen.svg +0 -11
  71. package/source/img/zoom-in.svg +0 -6
  72. package/source/img/zoom-out.svg +0 -7
  73. package/source/js/composite-image.js +0 -174
  74. package/source/js/diva-global.js +0 -7
  75. package/source/js/diva.js +0 -1543
  76. package/source/js/document-handler.js +0 -180
  77. package/source/js/document-layout.js +0 -286
  78. package/source/js/exceptions.js +0 -26
  79. package/source/js/gesture-events.js +0 -190
  80. package/source/js/grid-handler.js +0 -122
  81. package/source/js/iiif-source-adapter.js +0 -63
  82. package/source/js/image-cache.js +0 -113
  83. package/source/js/image-manifest.js +0 -157
  84. package/source/js/image-request-handler.js +0 -76
  85. package/source/js/interpolate-animation.js +0 -122
  86. package/source/js/page-layouts/book-layout.js +0 -161
  87. package/source/js/page-layouts/grid-layout.js +0 -97
  88. package/source/js/page-layouts/index.js +0 -38
  89. package/source/js/page-layouts/page-dimensions.js +0 -9
  90. package/source/js/page-layouts/singles-layout.js +0 -27
  91. package/source/js/page-overlay-manager.js +0 -102
  92. package/source/js/page-tools-overlay.js +0 -95
  93. package/source/js/parse-iiif-manifest.js +0 -302
  94. package/source/js/plugins/_filters.js +0 -679
  95. package/source/js/plugins/download.js +0 -83
  96. package/source/js/plugins/manipulation.js +0 -837
  97. package/source/js/plugins/metadata.js +0 -190
  98. package/source/js/renderer.js +0 -584
  99. package/source/js/settings-view.js +0 -30
  100. package/source/js/tile-coverage-map.js +0 -25
  101. package/source/js/toolbar.js +0 -572
  102. package/source/js/utils/dragscroll.js +0 -106
  103. package/source/js/utils/elt.js +0 -94
  104. package/source/js/utils/events.js +0 -190
  105. package/source/js/utils/get-scrollbar-width.js +0 -29
  106. package/source/js/utils/hash-params.js +0 -86
  107. package/source/js/utils/parse-label-value.js +0 -34
  108. package/source/js/utils/vanilla.kinetic.js +0 -527
  109. package/source/js/validation-runner.js +0 -177
  110. package/source/js/viewer-core.js +0 -1505
  111. package/source/js/viewport.js +0 -143
  112. package/test/_setup.js +0 -13
  113. package/test/composite-image_test.js +0 -94
  114. package/test/diva_test.js +0 -43
  115. package/test/hash-params_test.js +0 -221
  116. package/test/image-cache_test.js +0 -106
  117. package/test/main.js +0 -6
  118. package/test/manifests/beromunsterManifest.json +0 -15514
  119. package/test/manifests/iiifv2.json +0 -11032
  120. package/test/manifests/iiifv2pages.json +0 -30437
  121. package/test/manifests/iiifv3.json +0 -10965
  122. package/test/navigation_test.js +0 -355
  123. package/test/parse-iiif-manifest_test.js +0 -68
  124. package/test/public_test.js +0 -881
  125. package/test/settings_test.js +0 -487
  126. package/test/utils/book-layout_test.js +0 -148
  127. package/test/utils/elt_test.js +0 -102
  128. package/test/utils/events_test.js +0 -245
  129. package/test/utils/hash-params_test.js +0 -79
  130. package/test/utils/parse-label-value_test.js +0 -45
  131. package/test/z_plugins_test.js +0 -180
  132. package/webpack.config.js +0 -58
  133. package/webpack.config.test.js +0 -45
@@ -0,0 +1,786 @@
1
+ module View.Sidebar exposing (viewSidebarPanel, viewSidebarResizer)
2
+
3
+ import Dict exposing (Dict)
4
+ import Html exposing (Html, a, button, div, img, li, text, ul)
5
+ import Html.Attributes as HA exposing (alt, attribute, classList, id, src, type_)
6
+ import Html.Events as Events
7
+ import Html.Lazy as Lazy
8
+ import IIIF.Language exposing (LabelValue, Language, extractLabelFromLanguageMap)
9
+ import IIIF.Presentation exposing (IIIFManifest, MediaFormats, Range, RangeItem(..), ResourceTypes, ViewingDirection(..), canvasLabel, toCanvases, toHomepage, toMetadata, toRanges, toViewingDirection)
10
+ import Json.Decode as Decode
11
+ import Model exposing (ContentsView(..), Model, Page, ResourceResponse(..), Response(..), SidebarState(..), ViewMode(..), currentManifest, pageViewStartIndex, primaryImage)
12
+ import Msg exposing (Msg(..))
13
+ import View.Helpers exposing (emptyHtml, viewMaybe)
14
+ import View.HtmlRenderer exposing (renderHtml)
15
+
16
+
17
+ viewSidebarPanel : Model -> Html Msg
18
+ viewSidebarPanel model =
19
+ case model.resourceResponse of
20
+ ResourceLoadedCollection _ ->
21
+ viewSidebarPanelWithMaybeManifest model (currentManifest model)
22
+
23
+ _ ->
24
+ currentManifest model
25
+ |> viewMaybe (viewSidebarPanelWithManifest model)
26
+
27
+
28
+ viewSidebarResizer : Model -> Html Msg
29
+ viewSidebarResizer model =
30
+ if shouldRenderSidebarShell model then
31
+ div
32
+ [ classList
33
+ [ ( "sidebar-resizer", True )
34
+ , ( "is-hidden", not (isSidebarVisible model.sidebarState) )
35
+ ]
36
+ , Events.on "mousedown"
37
+ (Decode.field "clientX" Decode.int
38
+ |> Decode.map UserStartedSidebarResize
39
+ )
40
+ ]
41
+ [ text "⋮" ]
42
+
43
+ else
44
+ emptyHtml
45
+
46
+
47
+ chunk : Int -> List a -> List (List a)
48
+ chunk size items =
49
+ if size <= 0 then
50
+ []
51
+
52
+ else
53
+ chunkHelp size items []
54
+
55
+
56
+ chunkHelp : Int -> List a -> List (List a) -> List (List a)
57
+ chunkHelp size remaining acc =
58
+ case remaining of
59
+ [] ->
60
+ List.reverse acc
61
+
62
+ _ ->
63
+ let
64
+ nextChunk =
65
+ List.take size remaining
66
+
67
+ rest =
68
+ List.drop size remaining
69
+ in
70
+ chunkHelp size rest (nextChunk :: acc)
71
+
72
+
73
+ currentCanvasId : Model -> IIIFManifest -> Maybe String
74
+ currentCanvasId model manifest =
75
+ model.selectedIndex
76
+ |> Maybe.andThen (\index -> List.drop index (toCanvases manifest) |> List.head)
77
+ |> Maybe.map .id
78
+
79
+
80
+ hasManifestMetadata : IIIFManifest -> Bool
81
+ hasManifestMetadata manifest =
82
+ let
83
+ hasMetadataEntries =
84
+ toMetadata manifest
85
+ |> List.isEmpty
86
+ |> not
87
+
88
+ hasHomepageEntries =
89
+ case toHomepage manifest of
90
+ Just links ->
91
+ not (List.isEmpty links)
92
+
93
+ Nothing ->
94
+ False
95
+ in
96
+ hasMetadataEntries || hasHomepageEntries
97
+
98
+
99
+ homepageEntries : Language -> IIIFManifest -> List (Html Msg)
100
+ homepageEntries language manifest =
101
+ case toHomepage manifest of
102
+ Just links ->
103
+ if List.isEmpty links then
104
+ []
105
+
106
+ else
107
+ [ div
108
+ [ HA.class "metadata-item" ]
109
+ [ div
110
+ [ HA.class "metadata-label" ]
111
+ [ text "Homepage" ]
112
+ , div
113
+ [ HA.class "metadata-value" ]
114
+ (List.map (homepageLinkBlock language) links)
115
+ ]
116
+ ]
117
+
118
+ Nothing ->
119
+ []
120
+
121
+
122
+ homepageLinkBlock :
123
+ Language
124
+ ->
125
+ { id : String
126
+ , label : IIIF.Language.LanguageMap
127
+ , format : MediaFormats
128
+ , type_ : ResourceTypes
129
+ }
130
+ -> Html Msg
131
+ homepageLinkBlock language page =
132
+ div []
133
+ [ a
134
+ [ HA.href page.id
135
+ , HA.target "_blank"
136
+ , HA.rel "noopener noreferrer"
137
+ ]
138
+ [ extractLabelFromLanguageMap language page.label
139
+ |> text
140
+ ]
141
+ ]
142
+
143
+
144
+ isSidebarVisible : SidebarState -> Bool
145
+ isSidebarVisible state =
146
+ state /= SidebarHidden
147
+
148
+
149
+ isThumbnailActive : ViewMode -> Bool -> Maybe Int -> Int -> Bool
150
+ isThumbnailActive viewMode shiftByOne selectedIndex index =
151
+ case selectedIndex of
152
+ Just selected ->
153
+ case viewMode of
154
+ OneUp ->
155
+ selected == index
156
+
157
+ TwoUp ->
158
+ if shiftByOne && selected == 0 then
159
+ index == 0
160
+
161
+ else
162
+ let
163
+ startIndex =
164
+ pageViewStartIndex TwoUp shiftByOne selected
165
+ in
166
+ index == startIndex || index == startIndex + 1
167
+
168
+ Nothing ->
169
+ False
170
+
171
+
172
+ lookupRangeIndex : Dict String (Maybe Int) -> String -> Maybe Int
173
+ lookupRangeIndex rangeIndexMap rangeId =
174
+ Dict.get rangeId rangeIndexMap
175
+ |> Maybe.withDefault Nothing
176
+
177
+
178
+ metadataEntries : Language -> IIIFManifest -> List (Html Msg)
179
+ metadataEntries language manifest =
180
+ toMetadata manifest
181
+ |> List.map (metadataEntry language)
182
+
183
+
184
+ metadataEntry : Language -> LabelValue -> Html Msg
185
+ metadataEntry language entry =
186
+ div
187
+ [ HA.class "metadata-item" ]
188
+ [ div
189
+ [ HA.class "metadata-label" ]
190
+ [ extractLabelFromLanguageMap language entry.label |> text ]
191
+ , div
192
+ [ HA.class "metadata-value" ]
193
+ (extractLabelFromLanguageMap language entry.value |> renderHtml)
194
+ ]
195
+
196
+
197
+ rangeCanvasIds : List RangeItem -> List String
198
+ rangeCanvasIds items =
199
+ let
200
+ step pending acc =
201
+ case pending of
202
+ [] ->
203
+ List.reverse acc
204
+
205
+ [] :: restStacks ->
206
+ step restStacks acc
207
+
208
+ (item :: rest) :: restStacks ->
209
+ case item of
210
+ RangeCanvas idValue ->
211
+ step (rest :: restStacks) (idValue :: acc)
212
+
213
+ RangeRange range ->
214
+ step (range.items :: rest :: restStacks) acc
215
+ in
216
+ step [ items ] []
217
+
218
+
219
+ rangeCanvasLabels : Dict String String -> Range -> List String
220
+ rangeCanvasLabels canvasLabelMap range =
221
+ rangeCanvasIds range.items
222
+ |> List.filterMap (\idValue -> Dict.get idValue canvasLabelMap)
223
+
224
+
225
+ rangeContainsCanvas : String -> Range -> Bool
226
+ rangeContainsCanvas canvasId range =
227
+ List.any (rangeItemContainsCanvas canvasId) range.items
228
+
229
+
230
+ rangeItemContainsCanvas : String -> RangeItem -> Bool
231
+ rangeItemContainsCanvas canvasId item =
232
+ case item of
233
+ RangeCanvas idValue ->
234
+ idValue == canvasId
235
+
236
+ RangeRange range ->
237
+ rangeContainsCanvas canvasId range
238
+
239
+
240
+ rangesForCanvas : String -> List Range -> List Range
241
+ rangesForCanvas canvasId ranges =
242
+ List.concatMap (rangesForCanvasInRange canvasId) ranges
243
+
244
+
245
+ rangesForCanvasInRange : String -> Range -> List Range
246
+ rangesForCanvasInRange canvasId range =
247
+ let
248
+ nested =
249
+ List.concatMap
250
+ (\item ->
251
+ case item of
252
+ RangeCanvas _ ->
253
+ []
254
+
255
+ RangeRange child ->
256
+ rangesForCanvasInRange canvasId child
257
+ )
258
+ range.items
259
+ in
260
+ if rangeContainsCanvas canvasId range then
261
+ range :: nested
262
+
263
+ else
264
+ nested
265
+
266
+
267
+ reverseInRows : Int -> List a -> List a
268
+ reverseInRows rowSize items =
269
+ if rowSize <= 1 then
270
+ items
271
+
272
+ else
273
+ chunk rowSize items
274
+ |> List.concatMap List.reverse
275
+
276
+
277
+ shouldRenderSidebarShell : Model -> Bool
278
+ shouldRenderSidebarShell model =
279
+ case model.resourceResponse of
280
+ ResourceLoadedCollection _ ->
281
+ True
282
+
283
+ _ ->
284
+ currentManifest model /= Nothing
285
+
286
+
287
+ viewContentsContent : Model -> Html Msg
288
+ viewContentsContent model =
289
+ let
290
+ maybeManifest =
291
+ currentManifest model
292
+
293
+ body =
294
+ case ( model.contentsView, maybeManifest ) of
295
+ ( ContentsIndex, Just manifest ) ->
296
+ viewContentsIndexBody model manifest
297
+
298
+ ( ContentsIndex, Nothing ) ->
299
+ viewContentsEmptyBody
300
+
301
+ ( ContentsPages, Just manifest ) ->
302
+ viewOnThisPageBody model manifest
303
+
304
+ ( ContentsPages, Nothing ) ->
305
+ viewOnThisPageEmptyBody
306
+ in
307
+ div
308
+ [ HA.class "contents-panel" ]
309
+ [ div [ HA.class "contents-title" ] [ text "Contents" ]
310
+ , viewContentsToggle model.viewMode model.contentsView
311
+ , body
312
+ ]
313
+
314
+
315
+ viewContentsEmpty : String -> Html Msg
316
+ viewContentsEmpty message =
317
+ div
318
+ [ HA.class "contents-empty" ]
319
+ [ text message ]
320
+
321
+
322
+ viewContentsEmptyBody : Html Msg
323
+ viewContentsEmptyBody =
324
+ viewContentsEmpty "No contents available."
325
+
326
+
327
+ viewContentsIndexBody : Model -> IIIFManifest -> Html Msg
328
+ viewContentsIndexBody model manifest =
329
+ case toRanges manifest of
330
+ Just list ->
331
+ if List.isEmpty list then
332
+ viewContentsEmptyBody
333
+
334
+ else
335
+ viewRangeList model model.rangeIndexMap list
336
+
337
+ Nothing ->
338
+ viewContentsEmptyBody
339
+
340
+
341
+ viewContentsToggle : ViewMode -> ContentsView -> Html Msg
342
+ viewContentsToggle viewMode contentsView =
343
+ div
344
+ [ HA.class "contents-view-tabs" ]
345
+ [ button
346
+ [ classList
347
+ [ ( "contents-view-button", True )
348
+ , ( "is-active", contentsView == ContentsIndex )
349
+ ]
350
+ , type_ "button"
351
+ , Events.onClick UserSelectedContentsIndex
352
+ ]
353
+ [ text "Index" ]
354
+ , button
355
+ [ classList
356
+ [ ( "contents-view-button", True )
357
+ , ( "is-active", contentsView == ContentsPages )
358
+ ]
359
+ , type_ "button"
360
+ , Events.onClick UserSelectedContentsPages
361
+ ]
362
+ [ text
363
+ (case viewMode of
364
+ OneUp ->
365
+ "On this page"
366
+
367
+ TwoUp ->
368
+ "On these pages"
369
+ )
370
+ ]
371
+ ]
372
+
373
+
374
+ viewMetadataContent : Model -> Html Msg
375
+ viewMetadataContent model =
376
+ div
377
+ [ HA.class "metadata-panel" ]
378
+ (case currentManifest model of
379
+ Just manifest ->
380
+ [ div
381
+ [ HA.class "metadata-body" ]
382
+ (metadataEntries model.detectedLanguage manifest
383
+ ++ homepageEntries model.detectedLanguage manifest
384
+ )
385
+ ]
386
+
387
+ Nothing ->
388
+ [ div
389
+ [ HA.class "metadata-body" ]
390
+ [ text "No metadata available." ]
391
+ ]
392
+ )
393
+
394
+
395
+ viewOnThisPageBody : Model -> IIIFManifest -> Html Msg
396
+ viewOnThisPageBody model manifest =
397
+ case currentCanvasId model manifest of
398
+ Just canvasId ->
399
+ case toRanges manifest of
400
+ Just list ->
401
+ let
402
+ matches =
403
+ rangesForCanvas canvasId list
404
+ in
405
+ if List.isEmpty matches then
406
+ viewOnThisPageEmptyBody
407
+
408
+ else
409
+ let
410
+ canvasLabelMap =
411
+ toCanvases manifest
412
+ |> List.map (\canvas -> ( canvas.id, canvasLabel canvas ))
413
+ |> Dict.fromList
414
+ in
415
+ ul
416
+ [ HA.class "contents-list list-reset" ]
417
+ (List.map (viewOtpRangeItem model canvasLabelMap) matches)
418
+
419
+ Nothing ->
420
+ viewOnThisPageEmptyBody
421
+
422
+ Nothing ->
423
+ viewOnThisPageEmptyBody
424
+
425
+
426
+ viewOnThisPageEmptyBody : Html Msg
427
+ viewOnThisPageEmptyBody =
428
+ viewContentsEmpty "No ranges for this page."
429
+
430
+
431
+ viewOtpRangeItem : Model -> Dict String String -> Range -> Html Msg
432
+ viewOtpRangeItem model canvasLabelMap range =
433
+ let
434
+ canvasLabels =
435
+ rangeCanvasLabels canvasLabelMap range
436
+
437
+ maybeIndex =
438
+ lookupRangeIndex model.rangeIndexMap range.id
439
+
440
+ labelText =
441
+ extractLabelFromLanguageMap model.detectedLanguage range.label
442
+
443
+ firstLabel =
444
+ List.head canvasLabels
445
+
446
+ lastLabel =
447
+ List.reverse canvasLabels |> List.head
448
+
449
+ rangePrefix =
450
+ case ( firstLabel, lastLabel ) of
451
+ ( Just first, Just last ) ->
452
+ if first == last then
453
+ "[" ++ first ++ "] "
454
+
455
+ else
456
+ "[" ++ first ++ "-" ++ last ++ "] "
457
+
458
+ ( Just first, Nothing ) ->
459
+ "[" ++ first ++ "] "
460
+
461
+ ( Nothing, Just last ) ->
462
+ "[" ++ last ++ "] "
463
+
464
+ _ ->
465
+ ""
466
+
467
+ resolvedLabel =
468
+ if String.isEmpty labelText then
469
+ rangePrefix ++ "[Untitled range]"
470
+
471
+ else
472
+ rangePrefix ++ labelText
473
+
474
+ labelNode =
475
+ viewRangeButton range.id maybeIndex resolvedLabel
476
+
477
+ metadataBlock =
478
+ viewRangeMetadata model.detectedLanguage range.metadata
479
+ in
480
+ li
481
+ [ HA.class "contents-item" ]
482
+ (labelNode :: metadataBlock)
483
+
484
+
485
+ viewRangeButton : String -> Maybe Int -> String -> Html Msg
486
+ viewRangeButton rangeId maybeIndex labelText =
487
+ button
488
+ [ HA.class "contents-button ui-button"
489
+ , type_ "button"
490
+ , Events.onClick (UserClickedRange rangeId maybeIndex)
491
+ ]
492
+ [ text labelText ]
493
+
494
+
495
+ viewRangeItems : Model -> Dict String (Maybe Int) -> List RangeItem -> List (Html Msg)
496
+ viewRangeItems model rangeIndexMap items =
497
+ let
498
+ rendered =
499
+ List.filterMap
500
+ (\item ->
501
+ case item of
502
+ RangeCanvas _ ->
503
+ Nothing
504
+
505
+ RangeRange range ->
506
+ Just (Lazy.lazy3 viewRangeNode model rangeIndexMap range)
507
+ )
508
+ items
509
+ in
510
+ if List.isEmpty rendered then
511
+ []
512
+
513
+ else
514
+ [ ul [ HA.class "contents-list-nested list-reset" ] rendered ]
515
+
516
+
517
+ viewRangeList : Model -> Dict String (Maybe Int) -> List Range -> Html Msg
518
+ viewRangeList model rangeIndexMap ranges =
519
+ ul
520
+ [ HA.class "contents-list list-reset" ]
521
+ (List.map (Lazy.lazy3 viewRangeNode model rangeIndexMap) ranges)
522
+
523
+
524
+ viewRangeMetadata : Language -> List LabelValue -> List (Html Msg)
525
+ viewRangeMetadata language metadata =
526
+ if List.isEmpty metadata then
527
+ []
528
+
529
+ else
530
+ [ div
531
+ [ HA.class "contents-meta" ]
532
+ (List.map (metadataEntry language) metadata)
533
+ ]
534
+
535
+
536
+ viewRangeNode : Model -> Dict String (Maybe Int) -> Range -> Html Msg
537
+ viewRangeNode model rangeIndexMap range =
538
+ let
539
+ maybeIndex =
540
+ lookupRangeIndex rangeIndexMap range.id
541
+
542
+ labelText =
543
+ extractLabelFromLanguageMap model.detectedLanguage range.label
544
+
545
+ resolvedLabel =
546
+ if String.isEmpty labelText then
547
+ "[Untitled range]"
548
+
549
+ else
550
+ labelText
551
+
552
+ labelNode =
553
+ viewRangeButton range.id maybeIndex resolvedLabel
554
+
555
+ metadataBlock =
556
+ if model.selectedRangeId == Just range.id then
557
+ viewRangeMetadata model.detectedLanguage range.metadata
558
+
559
+ else
560
+ []
561
+
562
+ children =
563
+ viewRangeItems model rangeIndexMap range.items
564
+ in
565
+ li
566
+ [ HA.class "contents-item" ]
567
+ (labelNode :: metadataBlock ++ children)
568
+
569
+
570
+ viewSidebarPane : SidebarState -> SidebarState -> Html Msg -> Html Msg
571
+ viewSidebarPane current target content =
572
+ div
573
+ [ classList
574
+ [ ( "sidebar-pane", True )
575
+ , ( "is-hidden", current /= target )
576
+ ]
577
+ ]
578
+ [ content ]
579
+
580
+
581
+ viewSidebarPanelWithManifest : Model -> IIIFManifest -> Html Msg
582
+ viewSidebarPanelWithManifest model manifest =
583
+ viewSidebarPanelWithMaybeManifest model (Just manifest)
584
+
585
+
586
+ viewSidebarPanelWithMaybeManifest : Model -> Maybe IIIFManifest -> Html Msg
587
+ viewSidebarPanelWithMaybeManifest model maybeManifest =
588
+ let
589
+ hasContents =
590
+ maybeManifest
591
+ |> Maybe.andThen toRanges
592
+ |> Maybe.map (List.isEmpty >> not)
593
+ |> Maybe.withDefault False
594
+
595
+ hasMetadata =
596
+ maybeManifest
597
+ |> Maybe.map hasManifestMetadata
598
+ |> Maybe.withDefault False
599
+
600
+ viewingDirection =
601
+ maybeManifest
602
+ |> Maybe.map toViewingDirection
603
+ |> Maybe.withDefault LeftToRight
604
+
605
+ thumbnailPages =
606
+ if model.resourceResponse == ResourceLoading || model.response == Loading then
607
+ []
608
+
609
+ else
610
+ model.pages
611
+
612
+ panelClasses =
613
+ [ ( "sidebar-panel", True )
614
+ , ( "is-fullscreen", model.fullscreen )
615
+ , ( "is-hidden", not (isSidebarVisible model.sidebarState) )
616
+ , ( "is-overlay", model.mobileSidebarOpen )
617
+ , ( "is-mobile-hidden", not model.mobileSidebarOpen )
618
+ ]
619
+
620
+ contentsTab =
621
+ if hasContents then
622
+ [ viewSidebarTab model.sidebarState SidebarContents "Contents" UserToggledContents ]
623
+
624
+ else
625
+ []
626
+
627
+ metadataTab =
628
+ if hasMetadata then
629
+ [ viewSidebarTab model.sidebarState SidebarMetadata "Metadata" UserToggledMetadata ]
630
+
631
+ else
632
+ []
633
+
634
+ contentsPane =
635
+ if hasContents then
636
+ [ viewSidebarPane model.sidebarState SidebarContents (viewContentsContent model) ]
637
+
638
+ else
639
+ []
640
+
641
+ metadataPane =
642
+ if hasMetadata then
643
+ [ viewSidebarPane model.sidebarState SidebarMetadata (viewMetadataContent model) ]
644
+
645
+ else
646
+ []
647
+ in
648
+ div
649
+ [ classList panelClasses
650
+ , HA.style "width"
651
+ (if isSidebarVisible model.sidebarState then
652
+ String.fromInt model.sidebarWidth ++ "px"
653
+
654
+ else
655
+ "0px"
656
+ )
657
+ ]
658
+ [ div
659
+ [ HA.class "sidebar-tabs" ]
660
+ (viewSidebarTab model.sidebarState SidebarThumbnails "Thumbnails" UserToggledThumbnails
661
+ :: metadataTab
662
+ ++ contentsTab
663
+ )
664
+ , div
665
+ [ HA.class "sidebar-content" ]
666
+ (viewSidebarPane model.sidebarState
667
+ SidebarThumbnails
668
+ (viewThumbnails
669
+ { fullscreen = model.fullscreen
670
+ , selectedIndex = model.selectedIndex
671
+ , shiftByOne = model.shiftByOne
672
+ , thumbsInstantScroll = model.thumbsInstantScroll
673
+ , viewMode = model.viewMode
674
+ , viewingDirection = viewingDirection
675
+ }
676
+ thumbnailPages
677
+ )
678
+ :: metadataPane
679
+ ++ contentsPane
680
+ )
681
+ ]
682
+
683
+
684
+ viewSidebarTab : SidebarState -> SidebarState -> String -> Msg -> Html Msg
685
+ viewSidebarTab current target label msg =
686
+ button
687
+ [ classList
688
+ [ ( "sidebar-tab-button", True )
689
+ , ( "is-active", current == target )
690
+ ]
691
+ , type_ "button"
692
+ , Events.onClick msg
693
+ ]
694
+ [ text label ]
695
+
696
+
697
+ viewThumbnail : ViewMode -> Bool -> Maybe Int -> Int -> Page -> Html Msg
698
+ viewThumbnail viewMode shiftByOne selectedIndex index page =
699
+ let
700
+ isActive =
701
+ isThumbnailActive viewMode shiftByOne selectedIndex index
702
+
703
+ attrs =
704
+ [ classList
705
+ [ ( "thumbs-item", True )
706
+ , ( "ui-card", True )
707
+ , ( "ui-card--dark", True )
708
+ , ( "is-active", isActive )
709
+ ]
710
+ , type_ "button"
711
+ , id ("thumb-" ++ String.fromInt index)
712
+ , attribute "data-thumb-index" (String.fromInt index)
713
+ , Events.onClick (UserClickedThumbnail index)
714
+ ]
715
+
716
+ thumbUrl =
717
+ primaryImage page
718
+ |> Maybe.map .thumbUrl
719
+ |> Maybe.withDefault ""
720
+
721
+ hasChoices =
722
+ List.length page.images > 1
723
+ in
724
+ button attrs
725
+ [ img
726
+ [ HA.class "thumbs-image"
727
+ , src thumbUrl
728
+ , alt ("Page " ++ String.fromInt (index + 1))
729
+ ]
730
+ []
731
+ , div
732
+ [ classList
733
+ [ ( "thumbs-label", True )
734
+ , ( "is-active", isActive )
735
+ ]
736
+ ]
737
+ [ text
738
+ (if hasChoices then
739
+ page.label ++ " *"
740
+
741
+ else
742
+ page.label
743
+ )
744
+ ]
745
+ ]
746
+
747
+
748
+ viewThumbnails :
749
+ { fullscreen : Bool
750
+ , selectedIndex : Maybe Int
751
+ , shiftByOne : Bool
752
+ , thumbsInstantScroll : Bool
753
+ , viewMode : ViewMode
754
+ , viewingDirection : ViewingDirection
755
+ }
756
+ -> List Page
757
+ -> Html Msg
758
+ viewThumbnails { fullscreen, selectedIndex, shiftByOne, thumbsInstantScroll, viewMode, viewingDirection } pages =
759
+ let
760
+ indexedPages =
761
+ List.indexedMap Tuple.pair pages
762
+
763
+ orderedPages =
764
+ if viewingDirection == RightToLeft then
765
+ reverseInRows 3 indexedPages
766
+
767
+ else
768
+ indexedPages
769
+ in
770
+ div
771
+ [ classList
772
+ [ ( "thumbs", True )
773
+ , ( "is-fullscreen", fullscreen )
774
+ ]
775
+ , id "thumbs"
776
+ , HA.style "scroll-behavior"
777
+ (if thumbsInstantScroll then
778
+ "auto"
779
+
780
+ else
781
+ "smooth"
782
+ )
783
+ ]
784
+ (orderedPages
785
+ |> List.map (\( index, page ) -> Lazy.lazy5 viewThumbnail viewMode shiftByOne selectedIndex index page)
786
+ )