diva.js 6.0.2 → 7.2.4
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.
- package/.clang-format +7 -0
- package/.github/workflows/npm-publish.yml +45 -0
- package/LICENSE +55 -0
- package/Makefile +75 -0
- package/README.md +15 -114
- package/elm.json +32 -0
- package/package.json +12 -59
- package/review/elm.json +52 -0
- package/review/src/ReviewConfig.elm +87 -0
- package/scripts/elm-esm.sh +40 -0
- package/scripts/minify-css.mjs +31 -0
- package/src/Filters.elm +1044 -0
- package/src/Main.elm +1217 -0
- package/src/Model.elm +213 -0
- package/src/Msg.elm +59 -0
- package/src/Utilities.elm +46 -0
- package/src/View/CollectionExplorer.elm +172 -0
- package/src/View/Helpers.elm +86 -0
- package/src/View/HtmlRenderer.elm +136 -0
- package/src/View/Icons.elm +159 -0
- package/src/View/ManifestInfoModal.elm +363 -0
- package/src/View/PageViewModal.elm +1046 -0
- package/src/View/Sidebar.elm +786 -0
- package/src/View/Toolbar.elm +189 -0
- package/src/View.elm +244 -0
- package/src/diva.ts +802 -0
- package/src/filters.ts +1843 -0
- package/src/styles/app.css +328 -0
- package/src/styles/collection.css +75 -0
- package/src/styles/modal.css +388 -0
- package/src/styles/sidebar.css +215 -0
- package/src/styles/theme.css +39 -0
- package/src/styles/toolbar.css +154 -0
- package/src/viewer-element.ts +1307 -0
- package/testing/index.html +52 -0
- package/testing/testing.html +231 -0
- package/tsconfig.json +12 -0
- package/AUTHORS +0 -22
- package/build/diva.css +0 -554
- package/build/diva.css.map +0 -1
- package/build/diva.js +0 -9
- package/build/diva.js.map +0 -1
- package/build/plugins/download.js +0 -2
- package/build/plugins/download.js.map +0 -1
- package/build/plugins/manipulation.js +0 -2
- package/build/plugins/manipulation.js.map +0 -1
- package/build/plugins/metadata.js +0 -2
- package/build/plugins/metadata.js.map +0 -1
- package/index.html +0 -28
- package/karma.conf.js +0 -87
- package/source/css/_mixins.scss +0 -43
- package/source/css/_variables.scss +0 -50
- package/source/css/_viewer.scss +0 -462
- package/source/css/diva.scss +0 -15
- package/source/css/plugins/_manipulation.scss +0 -228
- package/source/css/plugins/_metadata.scss +0 -31
- package/source/img/adjust.svg +0 -11
- package/source/img/book-view.svg +0 -6
- package/source/img/close.svg +0 -6
- package/source/img/download.svg +0 -6
- package/source/img/from-fullscreen.svg +0 -8
- package/source/img/grid-fewer.svg +0 -6
- package/source/img/grid-more.svg +0 -6
- package/source/img/grid-view.svg +0 -6
- package/source/img/link.svg +0 -6
- package/source/img/metadata.svg +0 -9
- package/source/img/page-view.svg +0 -6
- package/source/img/to-fullscreen.svg +0 -11
- package/source/img/zoom-in.svg +0 -6
- package/source/img/zoom-out.svg +0 -7
- package/source/js/composite-image.js +0 -174
- package/source/js/diva-global.js +0 -7
- package/source/js/diva.js +0 -1543
- package/source/js/document-handler.js +0 -180
- package/source/js/document-layout.js +0 -286
- package/source/js/exceptions.js +0 -26
- package/source/js/gesture-events.js +0 -190
- package/source/js/grid-handler.js +0 -122
- package/source/js/iiif-source-adapter.js +0 -63
- package/source/js/image-cache.js +0 -113
- package/source/js/image-manifest.js +0 -157
- package/source/js/image-request-handler.js +0 -76
- package/source/js/interpolate-animation.js +0 -122
- package/source/js/page-layouts/book-layout.js +0 -161
- package/source/js/page-layouts/grid-layout.js +0 -97
- package/source/js/page-layouts/index.js +0 -38
- package/source/js/page-layouts/page-dimensions.js +0 -9
- package/source/js/page-layouts/singles-layout.js +0 -27
- package/source/js/page-overlay-manager.js +0 -102
- package/source/js/page-tools-overlay.js +0 -95
- package/source/js/parse-iiif-manifest.js +0 -302
- package/source/js/plugins/_filters.js +0 -679
- package/source/js/plugins/download.js +0 -83
- package/source/js/plugins/manipulation.js +0 -837
- package/source/js/plugins/metadata.js +0 -190
- package/source/js/renderer.js +0 -584
- package/source/js/settings-view.js +0 -30
- package/source/js/tile-coverage-map.js +0 -25
- package/source/js/toolbar.js +0 -573
- package/source/js/utils/dragscroll.js +0 -106
- package/source/js/utils/elt.js +0 -94
- package/source/js/utils/events.js +0 -190
- package/source/js/utils/get-scrollbar-width.js +0 -29
- package/source/js/utils/hash-params.js +0 -86
- package/source/js/utils/parse-label-value.js +0 -34
- package/source/js/utils/vanilla.kinetic.js +0 -527
- package/source/js/validation-runner.js +0 -177
- package/source/js/viewer-core.js +0 -1514
- package/source/js/viewport.js +0 -143
- package/test/_setup.js +0 -13
- package/test/composite-image_test.js +0 -94
- package/test/diva_test.js +0 -43
- package/test/hash-params_test.js +0 -221
- package/test/image-cache_test.js +0 -106
- package/test/main.js +0 -6
- package/test/manifests/beromunsterManifest.json +0 -15514
- package/test/manifests/iiifv2.json +0 -11032
- package/test/manifests/iiifv2pages.json +0 -30437
- package/test/manifests/iiifv3.json +0 -10965
- package/test/navigation_test.js +0 -355
- package/test/parse-iiif-manifest_test.js +0 -68
- package/test/public_test.js +0 -881
- package/test/settings_test.js +0 -487
- package/test/utils/book-layout_test.js +0 -148
- package/test/utils/elt_test.js +0 -102
- package/test/utils/events_test.js +0 -245
- package/test/utils/hash-params_test.js +0 -79
- package/test/utils/parse-label-value_test.js +0 -45
- package/test/z_plugins_test.js +0 -180
- package/webpack.config.js +0 -58
- 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
|
+
)
|