@projektemacher/storymapjs 0.10.6 → 0.10.7
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/CHANGELOG +11 -0
- package/dist/assets/{demo-CVQ7kFek.js → demo-D6cTmzn6.js} +1 -1
- package/dist/assets/{harness-C-VZV-ou.js → harness-BPFlzutp.js} +1 -1
- package/dist/assets/main-Y-MYUSJ4.js +17 -0
- package/dist/css/fonts/font.oldstandard.css +18 -22
- package/dist/demo.html +2 -2
- package/dist/docs/migration.html +1 -1
- package/dist/embed/index.html +129 -4
- package/dist/harness.html +2 -2
- package/dist/index.html +17 -0
- package/dist/js/storymap.d.ts +126 -7
- package/dist/js/storymap.js +4518 -3613
- package/dist/js/storymap.js.map +1 -1
- package/docs/migration-from-knightlab.md +139 -0
- package/docs/storymap-as-iiif-manifest.md +510 -0
- package/package.json +6 -3
- package/src/animation/easings.ts +18 -0
- package/src/animation/morpheus.d.ts +41 -0
- package/src/core/Browser.ts +19 -0
- package/src/core/Load.ts +180 -0
- package/src/core/Util.ts +354 -0
- package/src/core/mixins.ts +171 -0
- package/src/dom/Dom.ts +31 -0
- package/src/dom/DomEvent.ts +61 -0
- package/src/globals.d.ts +16 -0
- package/src/language/Language.ts +107 -0
- package/src/language/locale/be.json +18 -0
- package/src/language/locale/bg.json +18 -0
- package/src/language/locale/cs.json +18 -0
- package/src/language/locale/de.json +20 -0
- package/src/language/locale/el.json +18 -0
- package/src/language/locale/en.json +28 -0
- package/src/language/locale/es.json +18 -0
- package/src/language/locale/et.json +18 -0
- package/src/language/locale/fr.json +18 -0
- package/src/language/locale/he.json +18 -0
- package/src/language/locale/hu.json +18 -0
- package/src/language/locale/is.json +18 -0
- package/src/language/locale/it.json +18 -0
- package/src/language/locale/jp.json +18 -0
- package/src/language/locale/ko.json +18 -0
- package/src/language/locale/nl.json +18 -0
- package/src/language/locale/nn.json +18 -0
- package/src/language/locale/no.json +18 -0
- package/src/language/locale/pl.json +18 -0
- package/src/language/locale/pt.json +18 -0
- package/src/language/locale/ru.json +18 -0
- package/src/language/locale/sk.json +18 -0
- package/src/language/locale/sr.json +17 -0
- package/src/language/locale/sv.json +18 -0
- package/src/language/locale/tr.json +18 -0
- package/src/language/locale/uk.json +18 -0
- package/src/language/locale/ur.json +18 -0
- package/src/language/locale/zh-cn.json +18 -0
- package/src/language/locale/zh-tw.json +18 -0
- package/src/main.ts +51 -0
- package/src/map/Map.ts +717 -0
- package/src/map/MapMarker.ts +149 -0
- package/src/map/openlayers/Map.OpenLayers.ts +1879 -0
- package/src/map/openlayers/MapMarker.OpenLayers.ts +176 -0
- package/src/map/openlayers/zoomifyTiles.ts +28 -0
- package/src/map/types.ts +17 -0
- package/src/media/EmbedUtil.ts +168 -0
- package/src/media/Media.ts +409 -0
- package/src/media/MediaType.ts +163 -0
- package/src/media/types/Audio.ts +87 -0
- package/src/media/types/Blockquote.ts +40 -0
- package/src/media/types/DailyMotion.ts +58 -0
- package/src/media/types/DocumentCloud.ts +41 -0
- package/src/media/types/Facebook.ts +41 -0
- package/src/media/types/Flickr.ts +115 -0
- package/src/media/types/GoogleDoc.ts +59 -0
- package/src/media/types/IFrame.ts +51 -0
- package/src/media/types/Image.ts +49 -0
- package/src/media/types/Juxtapose.ts +32 -0
- package/src/media/types/SoundCloud.ts +77 -0
- package/src/media/types/Text.ts +172 -0
- package/src/media/types/Twitter.ts +96 -0
- package/src/media/types/Video.ts +84 -0
- package/src/media/types/Vimeo.ts +65 -0
- package/src/media/types/Website.ts +36 -0
- package/src/media/types/Wikipedia.ts +124 -0
- package/src/media/types/YouTube.ts +192 -0
- package/src/scss/Typography.scss +237 -0
- package/src/scss/VCO.StoryMap.Dark.scss +138 -0
- package/src/scss/VCO.StoryMap.scss +164 -0
- package/src/scss/Variables.Dark.scss +56 -0
- package/src/scss/Variables.scss +47 -0
- package/src/scss/core/Mixins.scss +112 -0
- package/src/scss/core/Reset.scss +206 -0
- package/src/scss/fonts/_font.base.scss +113 -0
- package/src/scss/fonts/font.abril-droidsans.scss +75 -0
- package/src/scss/fonts/font.amatic-andika.scss +73 -0
- package/src/scss/fonts/font.bitter-raleway.scss +86 -0
- package/src/scss/fonts/font.clicker-garamond.scss +92 -0
- package/src/scss/fonts/font.dancing-ledger.scss +74 -0
- package/src/scss/fonts/font.default.scss +9 -0
- package/src/scss/fonts/font.fjalla-average.scss +80 -0
- package/src/scss/fonts/font.georgia-helvetica.scss +58 -0
- package/src/scss/fonts/font.knightlab.scss +97 -0
- package/src/scss/fonts/font.lustria-lato.scss +80 -0
- package/src/scss/fonts/font.medula-lato.scss +73 -0
- package/src/scss/fonts/font.oldstandard.scss +88 -0
- package/src/scss/fonts/font.opensans-gentiumbook.scss +80 -0
- package/src/scss/fonts/font.playfair-faunaone.scss +86 -0
- package/src/scss/fonts/font.playfair.scss +85 -0
- package/src/scss/fonts/font.pt.scss +90 -0
- package/src/scss/fonts/font.roboto-megrim.scss +73 -0
- package/src/scss/fonts/font.rufina-sintony.scss +73 -0
- package/src/scss/fonts/font.ubuntu.scss +78 -0
- package/src/scss/fonts/font.unicaone-vollkorn.scss +85 -0
- package/src/scss/icons/Icons.scss +239 -0
- package/src/scss/map/VCO.ImageMarker.scss +23 -0
- package/src/scss/map/VCO.Map.scss +21 -0
- package/src/scss/map/VCO.MapMarker.scss +147 -0
- package/src/scss/map/openlayers/VCO.Map.OpenLayers.scss +126 -0
- package/src/scss/media/VCO.Media.scss +222 -0
- package/src/scss/media/types/VCO.Media.Blockquote.scss +76 -0
- package/src/scss/media/types/VCO.Media.IFrame.scss +11 -0
- package/src/scss/media/types/VCO.Media.Image.scss +13 -0
- package/src/scss/media/types/VCO.Media.Instagram.scss +7 -0
- package/src/scss/media/types/VCO.Media.Profile.scss +5 -0
- package/src/scss/media/types/VCO.Media.Text.scss +90 -0
- package/src/scss/media/types/VCO.Media.Twitter.scss +87 -0
- package/src/scss/media/types/VCO.Media.Wikipedia.scss +64 -0
- package/src/scss/site/site.scss +748 -0
- package/src/scss/slider/VCO.Slide.scss +447 -0
- package/src/scss/slider/VCO.SlideNav.scss +338 -0
- package/src/scss/slider/VCO.StorySlider.scss +64 -0
- package/src/scss/snapmap/VCO.SnapMap.scss +103 -0
- package/src/scss/ui/VCO.Consent.scss +77 -0
- package/src/scss/ui/VCO.MenuBar.Button.scss +99 -0
- package/src/scss/ui/VCO.MenuBar.scss +20 -0
- package/src/scss/ui/VCO.Message.scss +104 -0
- package/src/site/site.ts +87 -0
- package/src/slider/Slide.ts +460 -0
- package/src/slider/SlideNav.ts +212 -0
- package/src/slider/StorySlider.ts +722 -0
- package/src/storymap/Consent.ts +146 -0
- package/src/storymap/StoryMap.ts +1108 -0
- package/src/storymap/iiif.ts +386 -0
- package/src/storymap/validate.ts +184 -0
- package/src/types.ts +297 -0
- package/src/ui/Draggable.ts +374 -0
- package/src/ui/MenuBar.ts +323 -0
- package/src/ui/Message.ts +118 -0
- package/src/ui/Swipable.ts +388 -0
- package/dist/assets/main-DwbXrrYn.js +0 -17
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
@use "../core/Mixins" as *;
|
|
2
|
+
|
|
3
|
+
/* MESSAGE
|
|
4
|
+
================================================== */
|
|
5
|
+
.vco-message,
|
|
6
|
+
.vco-message-full {
|
|
7
|
+
width: 100%;
|
|
8
|
+
|
|
9
|
+
// max-width: 200px;
|
|
10
|
+
height: 100%;
|
|
11
|
+
position: absolute;
|
|
12
|
+
|
|
13
|
+
// position: relative;
|
|
14
|
+
|
|
15
|
+
display: table;
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
top: 0;
|
|
18
|
+
left: 0;
|
|
19
|
+
z-index: 99;
|
|
20
|
+
margin: auto;
|
|
21
|
+
text-align: center;
|
|
22
|
+
|
|
23
|
+
.vco-message-container {
|
|
24
|
+
padding: 20px;
|
|
25
|
+
margin: 20px;
|
|
26
|
+
text-align: center;
|
|
27
|
+
vertical-align: middle;
|
|
28
|
+
display: table-cell;
|
|
29
|
+
|
|
30
|
+
.vco-message-content {
|
|
31
|
+
color: #666;
|
|
32
|
+
text-align: center;
|
|
33
|
+
font-size: 11px;
|
|
34
|
+
line-height: 13px;
|
|
35
|
+
text-transform: uppercase;
|
|
36
|
+
margin-top: 7.5px;
|
|
37
|
+
margin-bottom: 7.5px;
|
|
38
|
+
text-shadow: 1px 1px 1px #fff;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.vco-loading-icon {
|
|
42
|
+
width: 30px;
|
|
43
|
+
height: 30px;
|
|
44
|
+
background-color: #666;
|
|
45
|
+
vertical-align: middle;
|
|
46
|
+
|
|
47
|
+
@include box-shadow(inset 0 1px 2px rgba(255, 255, 255, 0.1));
|
|
48
|
+
|
|
49
|
+
margin-left: auto;
|
|
50
|
+
margin-right: auto;
|
|
51
|
+
|
|
52
|
+
// margin: 100px auto;
|
|
53
|
+
animation: rotateplane 1.2s infinite ease-in-out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@keyframes rotateplane {
|
|
57
|
+
0% {
|
|
58
|
+
transform: perspective(120px) rotateX(0deg) rotateY(0deg);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
50% {
|
|
62
|
+
transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
100% {
|
|
66
|
+
transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.vco-message-full {
|
|
73
|
+
@include translucent-background(#333, 0.8);
|
|
74
|
+
|
|
75
|
+
[class^="vco-icon-"],
|
|
76
|
+
[class*=" vco-icon-"] {
|
|
77
|
+
// width: 48px;
|
|
78
|
+
// height: 48px;
|
|
79
|
+
color: #fff;
|
|
80
|
+
font-size: 72px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.vco-message-container {
|
|
84
|
+
.vco-message-content {
|
|
85
|
+
font-size: 22px;
|
|
86
|
+
line-height: 22px;
|
|
87
|
+
text-shadow: none;
|
|
88
|
+
color: #fff;
|
|
89
|
+
text-transform: none;
|
|
90
|
+
font-weight: bold;
|
|
91
|
+
|
|
92
|
+
.vco-button {
|
|
93
|
+
display: inline-block;
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
background-color: #fff;
|
|
96
|
+
color: #333;
|
|
97
|
+
padding: 10px;
|
|
98
|
+
margin-top: 10px;
|
|
99
|
+
|
|
100
|
+
@include border-radius(7px);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
package/src/site/site.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* Example cards for the landing page. Curated, non-`issue-*` fixtures from
|
|
2
|
+
* public/examples plus remote showcase examples; each card opens the storymap
|
|
3
|
+
* in the built embed player. The page chrome is site.css (compiled from
|
|
4
|
+
* src/scss/site/site.scss by the sitegen vite plugin) — this script only adds
|
|
5
|
+
* the cards and the demo height adjustment. */
|
|
6
|
+
|
|
7
|
+
interface ExampleEntry {
|
|
8
|
+
/** Local fixture name (public/examples/<id>.json), or a remote storymap JSON url */
|
|
9
|
+
id?: string;
|
|
10
|
+
url?: string;
|
|
11
|
+
/** Thumbnail name under thumbs/ (defaults to the fixture id) */
|
|
12
|
+
thumb?: string;
|
|
13
|
+
title: string;
|
|
14
|
+
kind: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const EXAMPLES: ExampleEntry[] = [
|
|
18
|
+
{ id: "katrina", title: "Hurricane Katrina", kind: "Map" },
|
|
19
|
+
{ id: "population", title: "US Population Shifts", kind: "Map" },
|
|
20
|
+
{ id: "marktwain", title: "Mark Twain's Travels", kind: "Map" },
|
|
21
|
+
{ id: "seurat", title: "A Sunday on La Grande Jatte", kind: "Gigapixel" },
|
|
22
|
+
{ id: "iiif-wellcome", title: "Wellcome Collection (IIIF)", kind: "IIIF" },
|
|
23
|
+
{ id: "president", title: "A Month in the Life of President Obama", kind: "Map" },
|
|
24
|
+
// remote showcase examples, rendered by the embed player
|
|
25
|
+
{
|
|
26
|
+
url: "https://s3.amazonaws.com/uploads.knightlab.com/storymapjs/a1a349b51799ee49e96bed10cc235e7f/garden-of-earthly-delights/published.json",
|
|
27
|
+
thumb: "bosch-garden",
|
|
28
|
+
title: "The Garden of Earthly Delights – Hieronymus Bosch",
|
|
29
|
+
kind: "Knight Lab example",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
url: "https://uploads.knightlab.com/storymapjs/3df56e350378e51781746bfb2a2ea428/test/published.json",
|
|
33
|
+
thumb: "southern-literary-trail",
|
|
34
|
+
title: "Southern Literary Trail",
|
|
35
|
+
kind: "Georgia Humanities",
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
function card(ex: ExampleEntry): HTMLAnchorElement {
|
|
40
|
+
const target = ex.url ?? "examples/" + ex.id + ".json";
|
|
41
|
+
const thumbId = ex.thumb ?? ex.id ?? "";
|
|
42
|
+
|
|
43
|
+
const a = document.createElement("a");
|
|
44
|
+
a.className = "card";
|
|
45
|
+
a.href = `./embed/index.html?url=${encodeURIComponent(target)}`;
|
|
46
|
+
|
|
47
|
+
const header = document.createElement("span");
|
|
48
|
+
header.className = "header-image";
|
|
49
|
+
const media = document.createElement("span");
|
|
50
|
+
media.className = "header-image-background bw";
|
|
51
|
+
// build-time screenshots (tasks/build-thumbnails.mjs); fall back to a
|
|
52
|
+
// deterministic two-tone gradient when a thumb is missing
|
|
53
|
+
media.style.backgroundImage = `url('./thumbs/${thumbId}.jpg'), linear-gradient(135deg, #b8b8b8, #646464)`;
|
|
54
|
+
media.setAttribute("role", "img");
|
|
55
|
+
media.setAttribute("aria-label", ex.title);
|
|
56
|
+
header.append(media);
|
|
57
|
+
|
|
58
|
+
const body = document.createElement("article");
|
|
59
|
+
body.className = "card-content";
|
|
60
|
+
const title = document.createElement("h3");
|
|
61
|
+
title.textContent = ex.title;
|
|
62
|
+
body.append(title);
|
|
63
|
+
|
|
64
|
+
const footer = document.createElement("footer");
|
|
65
|
+
footer.textContent = ex.kind;
|
|
66
|
+
|
|
67
|
+
a.append(header, body, footer);
|
|
68
|
+
return a;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const target = document.getElementById("examples-cards");
|
|
72
|
+
if (target) {
|
|
73
|
+
target.classList.add("cards-link");
|
|
74
|
+
for (const ex of EXAMPLES) {
|
|
75
|
+
target.append(card(ex));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// the original sizes the homepage demo to the window height minus 20px
|
|
80
|
+
const frame = document.getElementById("demo-frame") as HTMLIFrameElement | null;
|
|
81
|
+
if (frame) {
|
|
82
|
+
const size = () => {
|
|
83
|
+
frame.style.height = window.innerHeight - 20 + "px";
|
|
84
|
+
};
|
|
85
|
+
size();
|
|
86
|
+
window.addEventListener("resize", size);
|
|
87
|
+
}
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import { mergeData } from "../core/Util";
|
|
2
|
+
import { DomMixed, Evented, type EventedInstance } from "../core/mixins";
|
|
3
|
+
import { DomEvent } from "../dom/DomEvent";
|
|
4
|
+
import Dom from "../dom/Dom";
|
|
5
|
+
import { easeInSpline } from "../animation/easings";
|
|
6
|
+
import MediaType from "../media/MediaType";
|
|
7
|
+
import Text from "../media/types/Text";
|
|
8
|
+
import { Browser } from "../core/Browser";
|
|
9
|
+
import { MediaTypeMatch, StorymapSlide, StorymapSlideBackground } from "../types";
|
|
10
|
+
/* VCO.Slide
|
|
11
|
+
Creates a slide. Takes a data object and
|
|
12
|
+
populates the slide with content.
|
|
13
|
+
================================================== */
|
|
14
|
+
|
|
15
|
+
/* Slide options: the fields Slide itself sets or reads; everything
|
|
16
|
+
else merged in from the StorySlider options is absorbed by the
|
|
17
|
+
index signature. */
|
|
18
|
+
interface SlideOptions {
|
|
19
|
+
duration: number;
|
|
20
|
+
slide_padding_lr: number;
|
|
21
|
+
ease: unknown;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
skinny_size: number;
|
|
25
|
+
media_name?: string;
|
|
26
|
+
media_type?: string;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Media instance surface as used by Slide; instances are created
|
|
31
|
+
dynamically via the matched MediaTypeMatch.cls constructor. Media is
|
|
32
|
+
Evented — "media_loaded" signals that content heights may have changed. */
|
|
33
|
+
interface MediaInstance {
|
|
34
|
+
addTo: (container: HTMLElement) => void;
|
|
35
|
+
loadMedia: () => void;
|
|
36
|
+
stopMedia: () => void;
|
|
37
|
+
updateDisplay: (w?: number, h?: number, l?: string) => void;
|
|
38
|
+
on?: EventedInstance["on"];
|
|
39
|
+
_state?: { loaded?: boolean };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface SlideHas {
|
|
43
|
+
headline: boolean;
|
|
44
|
+
text: boolean;
|
|
45
|
+
media: boolean;
|
|
46
|
+
title: boolean;
|
|
47
|
+
background: {
|
|
48
|
+
image: boolean;
|
|
49
|
+
color: boolean;
|
|
50
|
+
color_value: string;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
class SlideBase {
|
|
55
|
+
declare "_el": Record<string, HTMLElement>;
|
|
56
|
+
declare "_media": MediaInstance | null;
|
|
57
|
+
declare "_mediaclass": unknown;
|
|
58
|
+
declare "_text": Text;
|
|
59
|
+
declare "_state": { loaded: boolean };
|
|
60
|
+
declare "_scroll_hint": HTMLElement | null;
|
|
61
|
+
declare "_scroll_hint_dismissed": boolean;
|
|
62
|
+
declare "has": SlideHas;
|
|
63
|
+
declare "title": string;
|
|
64
|
+
declare "data": StorymapSlide;
|
|
65
|
+
declare "options": SlideOptions;
|
|
66
|
+
declare "active": boolean;
|
|
67
|
+
declare "animator": unknown;
|
|
68
|
+
declare "fire": EventedInstance["fire"];
|
|
69
|
+
declare "onLoaded": () => void;
|
|
70
|
+
|
|
71
|
+
//_el: {},
|
|
72
|
+
|
|
73
|
+
/* Constructor
|
|
74
|
+
================================================== */
|
|
75
|
+
constructor(data: StorymapSlide, options: Record<string, unknown>, title_slide?: boolean) {
|
|
76
|
+
// DOM Elements
|
|
77
|
+
this._el = {
|
|
78
|
+
container: {} as HTMLElement,
|
|
79
|
+
scroll_container: {} as HTMLElement,
|
|
80
|
+
background: {} as HTMLElement,
|
|
81
|
+
content_container: {} as HTMLElement,
|
|
82
|
+
content: {} as HTMLElement,
|
|
83
|
+
call_to_action: null,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Components
|
|
87
|
+
this._media = null;
|
|
88
|
+
this._mediaclass = {};
|
|
89
|
+
this._text = {} as Text;
|
|
90
|
+
|
|
91
|
+
// State
|
|
92
|
+
this._state = {
|
|
93
|
+
loaded: false,
|
|
94
|
+
};
|
|
95
|
+
this._scroll_hint = null;
|
|
96
|
+
this._scroll_hint_dismissed = false;
|
|
97
|
+
|
|
98
|
+
this.has = {
|
|
99
|
+
headline: false,
|
|
100
|
+
text: false,
|
|
101
|
+
media: false,
|
|
102
|
+
title: false,
|
|
103
|
+
background: {
|
|
104
|
+
image: false,
|
|
105
|
+
color: false,
|
|
106
|
+
color_value: "",
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
this.has.title = title_slide;
|
|
111
|
+
|
|
112
|
+
this.title = "";
|
|
113
|
+
|
|
114
|
+
// Data
|
|
115
|
+
this.data = {
|
|
116
|
+
uniqueid: null,
|
|
117
|
+
background: null,
|
|
118
|
+
date: null,
|
|
119
|
+
location: null,
|
|
120
|
+
text: null,
|
|
121
|
+
media: null,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Options
|
|
125
|
+
this.options = {
|
|
126
|
+
// animation
|
|
127
|
+
duration: 1000,
|
|
128
|
+
slide_padding_lr: 40,
|
|
129
|
+
ease: easeInSpline,
|
|
130
|
+
width: 600,
|
|
131
|
+
height: 600,
|
|
132
|
+
skinny_size: 650,
|
|
133
|
+
media_name: "",
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// Actively Displaying
|
|
137
|
+
this.active = false;
|
|
138
|
+
|
|
139
|
+
// Animation Object
|
|
140
|
+
this.animator = {};
|
|
141
|
+
|
|
142
|
+
// Merge Data and Options
|
|
143
|
+
mergeData(this.options, options);
|
|
144
|
+
mergeData(this.data, data);
|
|
145
|
+
|
|
146
|
+
this._initLayout();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* Adding, Hiding, Showing etc
|
|
150
|
+
================================================== */
|
|
151
|
+
show() {
|
|
152
|
+
// Positioning is handled by StorySlider._updateDisplay
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
hide() {}
|
|
156
|
+
|
|
157
|
+
setActive(is_active: boolean) {
|
|
158
|
+
this.active = is_active;
|
|
159
|
+
|
|
160
|
+
if (this.active) {
|
|
161
|
+
if (this.data.background) {
|
|
162
|
+
this.fire("background_change", this.has.background);
|
|
163
|
+
}
|
|
164
|
+
// the hint may re-appear on a revisit if the content still
|
|
165
|
+
// overflows (the dismissed flag resets per activation)
|
|
166
|
+
this._scroll_hint_dismissed = false;
|
|
167
|
+
this.loadMedia();
|
|
168
|
+
this._updateScrollHint();
|
|
169
|
+
} else {
|
|
170
|
+
this.stopMedia();
|
|
171
|
+
this._hideScrollHint();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
updateDisplay(w?: number, h?: number, l?: string) {
|
|
176
|
+
this._updateDisplay(w, h, l);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
loadMedia() {
|
|
180
|
+
if (this._media && !this._state.loaded) {
|
|
181
|
+
this._media.loadMedia();
|
|
182
|
+
this._state.loaded = true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
stopMedia() {
|
|
187
|
+
if (this._media && this._state.loaded) {
|
|
188
|
+
try {
|
|
189
|
+
this._media.stopMedia();
|
|
190
|
+
} catch (e: unknown) {
|
|
191
|
+
// Some sort of race condition or other ordering condition can cause
|
|
192
|
+
// an error when the preview tab is selected in the editor due to
|
|
193
|
+
// the stopped media not being properly formed.
|
|
194
|
+
if (
|
|
195
|
+
(e as Error).message === "this._el.content_item.querySelector is not a function"
|
|
196
|
+
) {
|
|
197
|
+
console.log("Ignoring error in editor context: " + (e as Error).message);
|
|
198
|
+
} else {
|
|
199
|
+
throw e;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// If the media load never started (its pending timer was
|
|
203
|
+
// cancelled on a quick pass-through), allow a revisit to retry
|
|
204
|
+
if (!this._media._state?.loaded) {
|
|
205
|
+
this._state.loaded = false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getBackground() {
|
|
211
|
+
return this.has.background;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
scrollToTop() {
|
|
215
|
+
this._el.container.scrollTop = 0;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* Scroll hint
|
|
219
|
+
================================================== */
|
|
220
|
+
/**
|
|
221
|
+
* Show a bouncing downward arrow when the slide content overflows
|
|
222
|
+
* (scrollable) — the hint of "more below" for scrollbars that are not
|
|
223
|
+
* visible until touched. Hidden after the first scroll of any kind;
|
|
224
|
+
* tappable to scroll down one step.
|
|
225
|
+
*/
|
|
226
|
+
_updateScrollHint() {
|
|
227
|
+
if (!this.active || this._scroll_hint_dismissed) {
|
|
228
|
+
this._hideScrollHint();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const el = this._el.container;
|
|
232
|
+
const overflows = el.scrollHeight > el.clientHeight + 1;
|
|
233
|
+
if (!overflows) {
|
|
234
|
+
this._hideScrollHint();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (!this._scroll_hint) {
|
|
238
|
+
this._scroll_hint = Dom.create(
|
|
239
|
+
"div",
|
|
240
|
+
"vco-slide-scroll-hint",
|
|
241
|
+
el,
|
|
242
|
+
);
|
|
243
|
+
this._scroll_hint.innerHTML = "<span class='vco-icon-arrow-down'></span>";
|
|
244
|
+
DomEvent.addListener(this._scroll_hint, "click", this._onScrollHintClick, this);
|
|
245
|
+
}
|
|
246
|
+
this._scroll_hint.style.display = "flex";
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
_hideScrollHint() {
|
|
250
|
+
if (this._scroll_hint) {
|
|
251
|
+
this._scroll_hint.style.display = "none";
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
_onScrollHintClick() {
|
|
256
|
+
// scroll down one step, then hide (the scroll event dismisses too)
|
|
257
|
+
const el = this._el.container;
|
|
258
|
+
el.scrollBy({ top: el.clientHeight * 0.8, behavior: "smooth" });
|
|
259
|
+
this._scroll_hint_dismissed = true;
|
|
260
|
+
this._hideScrollHint();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
_onSlideScroll() {
|
|
264
|
+
// programmatic scrollToTop on preloaded (inactive) slides must not
|
|
265
|
+
// dismiss the hint
|
|
266
|
+
if (!this.active || this._scroll_hint_dismissed) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
this._scroll_hint_dismissed = true;
|
|
270
|
+
this._hideScrollHint();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
addCallToAction(str: string) {
|
|
274
|
+
this._el.call_to_action = Dom.create(
|
|
275
|
+
"div",
|
|
276
|
+
"vco-slide-calltoaction",
|
|
277
|
+
this._el.content_container,
|
|
278
|
+
);
|
|
279
|
+
this._el.call_to_action.innerHTML =
|
|
280
|
+
"<span class='vco-slide-calltoaction-button-text'>" + str + "</span>";
|
|
281
|
+
DomEvent.addListener(this._el.call_to_action, "click", this._onCallToAction, this);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/* Events
|
|
285
|
+
================================================== */
|
|
286
|
+
_onCallToAction(e: Event) {
|
|
287
|
+
this.fire("call_to_action", e);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/* Private Methods
|
|
291
|
+
================================================== */
|
|
292
|
+
_initLayout() {
|
|
293
|
+
// Create Layout
|
|
294
|
+
this._el.container = Dom.create("div", "vco-slide");
|
|
295
|
+
if (this.data.uniqueid) {
|
|
296
|
+
this._el.container.id = this.data.uniqueid;
|
|
297
|
+
}
|
|
298
|
+
this._el.scroll_container = Dom.create(
|
|
299
|
+
"div",
|
|
300
|
+
"vco-slide-scrollable-container",
|
|
301
|
+
this._el.container,
|
|
302
|
+
);
|
|
303
|
+
this._el.content_container = Dom.create(
|
|
304
|
+
"div",
|
|
305
|
+
"vco-slide-content-container",
|
|
306
|
+
this._el.scroll_container,
|
|
307
|
+
);
|
|
308
|
+
this._el.content = Dom.create("div", "vco-slide-content", this._el.content_container);
|
|
309
|
+
this._el.background = Dom.create("div", "vco-slide-background", this._el.container);
|
|
310
|
+
// first scroll of any kind hides the scroll hint (passive: the
|
|
311
|
+
// listener never prevents default)
|
|
312
|
+
this._el.container.addEventListener("scroll", this._onSlideScroll.bind(this), {
|
|
313
|
+
passive: true,
|
|
314
|
+
});
|
|
315
|
+
// Style Slide Background
|
|
316
|
+
if (this.data.background) {
|
|
317
|
+
const background = this.data.background as StorymapSlideBackground & {
|
|
318
|
+
text_background?: unknown;
|
|
319
|
+
};
|
|
320
|
+
if (background.url) {
|
|
321
|
+
this.has.background.image = true;
|
|
322
|
+
this._el.container.className += " vco-full-image-background";
|
|
323
|
+
this.has.background.color_value = "#000";
|
|
324
|
+
this._el.background.style.backgroundImage = "url('" + background.url + "')";
|
|
325
|
+
this._el.background.style.display = "block";
|
|
326
|
+
}
|
|
327
|
+
if (background.color) {
|
|
328
|
+
this.has.background.color = true;
|
|
329
|
+
this._el.container.className += " vco-full-color-background";
|
|
330
|
+
this.has.background.color_value = background.color;
|
|
331
|
+
}
|
|
332
|
+
if (background.text_background) {
|
|
333
|
+
this._el.container.className += " vco-text-background";
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Determine Assets for layout and loading
|
|
338
|
+
if (this.data.media && this.data.media.url && this.data.media.url !== "") {
|
|
339
|
+
this.has.media = true;
|
|
340
|
+
}
|
|
341
|
+
if (this.data.text && this.data.text.text) {
|
|
342
|
+
this.has.text = true;
|
|
343
|
+
}
|
|
344
|
+
if (this.data.text && this.data.text.headline) {
|
|
345
|
+
this.has.headline = true;
|
|
346
|
+
this.title = this.data.text.headline;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Create Media
|
|
350
|
+
if (this.has.media) {
|
|
351
|
+
// Determine the media type
|
|
352
|
+
this.data.media.mediatype = MediaType(this.data.media) as MediaTypeMatch;
|
|
353
|
+
this.options.media_name = this.data.media.mediatype.name;
|
|
354
|
+
this.options.media_type = this.data.media.mediatype.type;
|
|
355
|
+
|
|
356
|
+
// Create a media object using the matched class name
|
|
357
|
+
this._media = new this.data.media.mediatype.cls(
|
|
358
|
+
this.data.media,
|
|
359
|
+
this.options,
|
|
360
|
+
) as MediaInstance;
|
|
361
|
+
// loaded media changes the content height — the scroll hint
|
|
362
|
+
// may appear or disappear
|
|
363
|
+
this._media.on?.("media_loaded", () => this._updateScrollHint());
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Create Text
|
|
367
|
+
if (this.has.text || this.has.headline) {
|
|
368
|
+
this._text = new Text(this.data.text, {
|
|
369
|
+
title: this.has.title,
|
|
370
|
+
text_align: this.options.text_align as string | undefined,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Add to DOM
|
|
375
|
+
if (!this.has.text && !this.has.headline && this.has.media) {
|
|
376
|
+
this._el.container.className += " vco-slide-media-only";
|
|
377
|
+
this._media.addTo(this._el.content);
|
|
378
|
+
} else if (this.has.headline && this.has.media && !this.has.text) {
|
|
379
|
+
this._el.container.className += " vco-slide-media-only";
|
|
380
|
+
this._text.addTo(this._el.content);
|
|
381
|
+
this._media.addTo(this._el.content);
|
|
382
|
+
} else if (this.has.text && this.has.media) {
|
|
383
|
+
this._media.addTo(this._el.content);
|
|
384
|
+
this._text.addTo(this._el.content);
|
|
385
|
+
} else if (this.has.text || this.has.headline) {
|
|
386
|
+
this._el.container.className += " vco-slide-text-only";
|
|
387
|
+
this._text.addTo(this._el.content);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Fire event that the slide is loaded
|
|
391
|
+
this.onLoaded();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Update Display
|
|
395
|
+
_updateDisplay(width?: number, height?: number, layout?: string) {
|
|
396
|
+
let pad_left, pad_right, new_width;
|
|
397
|
+
|
|
398
|
+
if (width) {
|
|
399
|
+
this.options.width = width;
|
|
400
|
+
} else {
|
|
401
|
+
this.options.width = this._el.container.offsetWidth;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (Browser.mobile && this.options.width <= this.options.skinny_size) {
|
|
405
|
+
pad_left = 0 + "px";
|
|
406
|
+
pad_right = 0 + "px";
|
|
407
|
+
new_width = this.options.width - 0 + "px";
|
|
408
|
+
} else if (layout === "landscape") {
|
|
409
|
+
pad_left = 40 + "px";
|
|
410
|
+
pad_right = 75 + "px";
|
|
411
|
+
new_width = this.options.width - (75 + 40) + "px";
|
|
412
|
+
} else if (this.options.width <= this.options.skinny_size) {
|
|
413
|
+
pad_left = this.options.slide_padding_lr + "px";
|
|
414
|
+
pad_right = this.options.slide_padding_lr + "px";
|
|
415
|
+
new_width = this.options.width - this.options.slide_padding_lr * 2 + "px";
|
|
416
|
+
} else {
|
|
417
|
+
pad_left = this.options.slide_padding_lr + "px";
|
|
418
|
+
pad_right = this.options.slide_padding_lr + "px";
|
|
419
|
+
new_width = this.options.width - this.options.slide_padding_lr * 2 + "px";
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
this._el.content.style.paddingLeft = pad_left;
|
|
423
|
+
this._el.content.style.paddingRight = pad_right;
|
|
424
|
+
this._el.content.style.width = new_width;
|
|
425
|
+
|
|
426
|
+
if (this._el.call_to_action) {
|
|
427
|
+
this._el.call_to_action.style.paddingLeft = pad_left;
|
|
428
|
+
this._el.call_to_action.style.paddingRight = pad_right;
|
|
429
|
+
this._el.call_to_action.style.width = new_width;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (height) {
|
|
433
|
+
this.options.height = height;
|
|
434
|
+
//this._el.scroll_container.style.height = this.options.height + "px";
|
|
435
|
+
} else {
|
|
436
|
+
this.options.height = this._el.container.offsetHeight;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (this._media) {
|
|
440
|
+
if (!this.has.text && this.has.headline) {
|
|
441
|
+
this._media.updateDisplay(
|
|
442
|
+
this.options.width,
|
|
443
|
+
this.options.height - this._text.headlineHeight(),
|
|
444
|
+
layout,
|
|
445
|
+
);
|
|
446
|
+
} else {
|
|
447
|
+
this._media.updateDisplay(this.options.width, this.options.height, layout);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// layout/resize changes move the overflow boundary
|
|
452
|
+
this._updateScrollHint();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
export default class Slide extends DomMixed(Evented(SlideBase)) {
|
|
457
|
+
constructor(...args: ConstructorParameters<typeof SlideBase>) {
|
|
458
|
+
super(...args);
|
|
459
|
+
}
|
|
460
|
+
}
|