@internetstiftelsen/styleguide 2.24.30-beta.0.1 → 2.24.30-beta.0.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.
- package/dist/organisms/video-guide/VideoGuidePlayback.js +250 -0
- package/dist/organisms/video-guide/VideoGuideSubtitles.js +67 -0
- package/dist/organisms/video-guide/VideoGuideTimeline.js +100 -0
- package/dist/organisms/video-guide/getCurrentCueIndex.js +13 -0
- package/dist/organisms/video-guide/video-guide.js +73 -239
- package/package.json +1 -1
- package/src/assets/video/guidad-tur-nar-internet-kom-chapters.vtt +22 -0
- package/src/assets/video/guidad-tur-nar-internet-kom-metadata.vtt +89 -0
- package/src/assets/video/guidad-tur-nar-internet-kom-till-svenska-hem-undertexter.vtt +474 -0
- package/src/organisms/timeline/_timeline.scss +1 -0
- package/src/organisms/video-guide/VideoGuidePlayback.js +213 -0
- package/src/organisms/video-guide/VideoGuideSubtitles.js +43 -0
- package/src/organisms/video-guide/VideoGuideTimeline.js +76 -0
- package/src/organisms/video-guide/_video-guide.scss +13 -3
- package/src/organisms/video-guide/getCurrentCueIndex.js +6 -0
- package/src/organisms/video-guide/video-guide.config.js +3 -3
- package/src/organisms/video-guide/video-guide.js +48 -244
- package/src/assets/video/chapters.vtt +0 -25
- package/src/assets/video/metadata.vtt +0 -28
- package/src/assets/video/movie-webm.webm +0 -0
- package/src/assets/video/videoplayer.vtt +0 -25
|
@@ -1,269 +1,103 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var playBtn = document.querySelector('.js-play-btn');
|
|
5
|
-
var playIcon = document.querySelector('.js-play-icon');
|
|
6
|
-
var pauseIcon = document.querySelector('.js-pause-icon');
|
|
7
|
-
var subtitlesBtn = document.querySelector('.js-subtitles-btn');
|
|
8
|
-
var abortButton = document.querySelector('.js-abort-guide');
|
|
9
|
-
var subtitlesElement = document.getElementById('video-subtitles');
|
|
10
|
-
var subtitlesContainer = document.querySelector('.js-subtitles-container');
|
|
11
|
-
var chapterTrackElement = document.getElementById('video-chapters');
|
|
12
|
-
var trackMetadataElement = document.getElementById('video-metadata');
|
|
13
|
-
var subtitlesTrack = subtitlesElement === null ? '' : subtitlesElement.track;
|
|
14
|
-
var chapterTrack = chapterTrackElement === null ? '' : chapterTrackElement.track;
|
|
15
|
-
var metadataTrack = trackMetadataElement === null ? '' : trackMetadataElement.track;
|
|
16
|
-
var forwardsButton = document.querySelector('.js-next-chapter');
|
|
17
|
-
var backwardsButton = document.querySelector('.js-previous-chapter');
|
|
18
|
-
var timelinePosts = document.querySelectorAll('.js-timeline-post');
|
|
19
|
-
var navigationButton = document.querySelector('.js-show-timelineposts');
|
|
20
|
-
var timeLinePosts = document.querySelector('.js-timeline-posts');
|
|
21
|
-
var currentChapter = 0;
|
|
22
|
-
var manualStep = false;
|
|
3
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
23
4
|
|
|
24
|
-
|
|
25
|
-
function saveState() {
|
|
26
|
-
if (video.currentTime > 0) {
|
|
27
|
-
var currentGuideURL = window.location.href;
|
|
28
|
-
var currentGuideImage = video.dataset.featuredImage;
|
|
29
|
-
sessionStorage.setItem('InmsCurrentTime', video.currentTime);
|
|
30
|
-
sessionStorage.setItem('InmsDuration', video.duration); // Get totalt duration of video
|
|
31
|
-
sessionStorage.setItem('InmsCurrentGuideURL', currentGuideURL);
|
|
32
|
-
sessionStorage.setItem('InmsCurrentGuideImage', currentGuideImage);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
5
|
+
var _VideoGuideSubtitles = require('./VideoGuideSubtitles');
|
|
35
6
|
|
|
36
|
-
|
|
37
|
-
if (video) {
|
|
38
|
-
// Store current time in on page reload
|
|
39
|
-
window.addEventListener('visibilitychange', saveState);
|
|
40
|
-
window.addEventListener('beforeunload', saveState);
|
|
7
|
+
var _VideoGuideSubtitles2 = _interopRequireDefault(_VideoGuideSubtitles);
|
|
41
8
|
|
|
42
|
-
|
|
43
|
-
if (sessionStorage.getItem('InmsCurrentTime')) {
|
|
44
|
-
var videoCurrentTime = sessionStorage.getItem('InmsCurrentTime');
|
|
9
|
+
var _VideoGuideTimeline = require('./VideoGuideTimeline');
|
|
45
10
|
|
|
46
|
-
|
|
47
|
-
video.currentTime = videoCurrentTime;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Toggle subtitles
|
|
52
|
-
if (subtitlesBtn) {
|
|
53
|
-
subtitlesBtn.addEventListener('click', function () {
|
|
54
|
-
subtitlesBtn.classList.toggle('is-active');
|
|
55
|
-
subtitlesContainer.classList.toggle('is-visible');
|
|
56
|
-
});
|
|
57
|
-
}
|
|
11
|
+
var _VideoGuideTimeline2 = _interopRequireDefault(_VideoGuideTimeline);
|
|
58
12
|
|
|
59
|
-
|
|
60
|
-
if (playBtn) {
|
|
61
|
-
playBtn.addEventListener('click', function () {
|
|
62
|
-
if (video.paused) {
|
|
63
|
-
video.play();
|
|
64
|
-
pauseIcon.classList.remove('is-hidden');
|
|
65
|
-
playIcon.classList.add('is-hidden');
|
|
66
|
-
manualStep = false;
|
|
67
|
-
} else {
|
|
68
|
-
video.pause();
|
|
69
|
-
pauseIcon.classList.add('is-hidden');
|
|
70
|
-
playIcon.classList.remove('is-hidden');
|
|
71
|
-
manualStep = true;
|
|
72
|
-
}
|
|
73
|
-
});
|
|
13
|
+
var _VideoGuidePlayback = require('./VideoGuidePlayback');
|
|
74
14
|
|
|
75
|
-
|
|
76
|
-
pauseIcon.classList.remove('is-hidden');
|
|
77
|
-
playIcon.classList.add('is-hidden');
|
|
78
|
-
manualStep = false;
|
|
79
|
-
});
|
|
15
|
+
var _VideoGuidePlayback2 = _interopRequireDefault(_VideoGuidePlayback);
|
|
80
16
|
|
|
81
|
-
|
|
82
|
-
pauseIcon.classList.add('is-hidden');
|
|
83
|
-
playIcon.classList.remove('is-hidden');
|
|
84
|
-
video.currentTime = 0;
|
|
85
|
-
currentChapter = 1;
|
|
86
|
-
manualStep = false;
|
|
87
|
-
forwardsButton.removeAttribute('disabled');
|
|
88
|
-
subtitlesContainer.innerHTML = '';
|
|
89
|
-
sessionStorage.removeItem('InmsCurrentTime');
|
|
90
|
-
sessionStorage.removeItem('InmsDuration');
|
|
91
|
-
sessionStorage.removeItem('InmsCurrentGuideURL');
|
|
92
|
-
sessionStorage.removeItem('InmsCurrentGuideImage');
|
|
93
|
-
});
|
|
94
|
-
}
|
|
17
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
95
18
|
|
|
96
|
-
|
|
97
|
-
abortButton.addEventListener('click', function (e) {
|
|
98
|
-
e.preventDefault();
|
|
99
|
-
var urlTarget = abortButton.getAttribute('href');
|
|
100
|
-
video.pause();
|
|
101
|
-
video.currentTime = 0;
|
|
102
|
-
forwardsButton.removeAttribute('disabled');
|
|
103
|
-
currentChapter = 1;
|
|
104
|
-
manualStep = false;
|
|
105
|
-
sessionStorage.removeItem('InmsCurrentTime');
|
|
106
|
-
sessionStorage.removeItem('InmsDuration');
|
|
107
|
-
sessionStorage.removeItem('InmsCurrentGuideURL');
|
|
108
|
-
sessionStorage.removeItem('InmsCurrentGuideImage');
|
|
109
|
-
window.location.href = urlTarget;
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (navigationButton) {
|
|
114
|
-
navigationButton.addEventListener('click', function () {
|
|
115
|
-
navigationButton.classList.toggle('is-toggeled');
|
|
116
|
-
timeLinePosts.classList.toggle('is-visible');
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function displayChapters() {
|
|
122
|
-
if (chapterTrackElement && trackMetadataElement) {
|
|
123
|
-
// Set all track elements to hidden mode to allow scripting
|
|
124
|
-
[].forEach.call(video.textTracks, function (txtTrack) {
|
|
125
|
-
txtTrack.mode = 'hidden';
|
|
126
|
-
});
|
|
19
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
127
20
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
// Let data load
|
|
132
|
-
setTimeout(function () {
|
|
133
|
-
video.classList.remove('is-loading');
|
|
21
|
+
var VideoGuide = function () {
|
|
22
|
+
function VideoGuide(element) {
|
|
23
|
+
var _this = this;
|
|
134
24
|
|
|
135
|
-
|
|
136
|
-
if (!sessionStorage.getItem('InmsCurrentTime')) {
|
|
137
|
-
forwardsButton.setAttribute('data-id', chapterTrack.cues[0].endTime);
|
|
138
|
-
}
|
|
139
|
-
}, 100);
|
|
140
|
-
});
|
|
25
|
+
_classCallCheck(this, VideoGuide);
|
|
141
26
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
currentTime += 1;
|
|
147
|
-
video.currentTime = currentTime;
|
|
148
|
-
currentChapter += 1;
|
|
149
|
-
});
|
|
27
|
+
this.onLoadedMetadata = function () {
|
|
28
|
+
_this.playback = new _VideoGuidePlayback2.default(_this.element, _this.video);
|
|
29
|
+
_this.subtitles = new _VideoGuideSubtitles2.default(_this.element, _this.video);
|
|
30
|
+
_this.timeline = new _VideoGuideTimeline2.default(_this.element, _this.video);
|
|
150
31
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
var lastTime = parseInt(dataId, 10);
|
|
154
|
-
lastTime -= 1;
|
|
155
|
-
video.currentTime = lastTime;
|
|
156
|
-
forwardsButton.removeAttribute('disabled');
|
|
157
|
-
manualStep = true;
|
|
158
|
-
currentChapter -= 1;
|
|
32
|
+
_this.video.classList.remove('is-loading');
|
|
33
|
+
};
|
|
159
34
|
|
|
160
|
-
|
|
161
|
-
|
|
35
|
+
this.dispatchEvent = function (eventName) {
|
|
36
|
+
[_this.playback, _this.subtitles, _this.timeline].forEach(function (instance) {
|
|
37
|
+
if (instance && eventName in instance) {
|
|
38
|
+
instance[eventName]();
|
|
162
39
|
}
|
|
163
40
|
});
|
|
41
|
+
};
|
|
164
42
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (myCues.length > 0) {
|
|
169
|
-
var currentLocation = chapterTrack.activeCues[0].startTime;
|
|
170
|
-
var nextLocation = chapterTrack.activeCues[0].endTime;
|
|
171
|
-
var cueMatch = chapterTrack.activeCues[0].text;
|
|
172
|
-
var matchingCueArray = document.querySelectorAll('[rel="' + currentLocation + '"]');
|
|
173
|
-
|
|
174
|
-
// Set Forward and backwards buttons timestamps
|
|
175
|
-
forwardsButton.setAttribute('data-id', nextLocation);
|
|
176
|
-
backwardsButton.setAttribute('data-id', currentLocation);
|
|
177
|
-
|
|
178
|
-
// Add chapter stepping even when video is played
|
|
179
|
-
if (manualStep === false) {
|
|
180
|
-
currentChapter += 1;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Disable forwardsbutton when on last chapter
|
|
184
|
-
if (currentChapter >= chapterTrack.cues.length) {
|
|
185
|
-
forwardsButton.setAttribute('disabled', 'disabled');
|
|
186
|
-
}
|
|
43
|
+
this.onPlay = function () {
|
|
44
|
+
return _this.dispatchEvent('onPlay');
|
|
45
|
+
};
|
|
187
46
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (thisChapter.innerHTML === cueMatch) {
|
|
192
|
-
var chapter = thisChapter;
|
|
47
|
+
this.onPause = function () {
|
|
48
|
+
return _this.dispatchEvent('onPause');
|
|
49
|
+
};
|
|
193
50
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
51
|
+
this.onEnded = function () {
|
|
52
|
+
return _this.dispatchEvent('onEnded');
|
|
53
|
+
};
|
|
197
54
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
location.querySelector('a').classList.remove('is-current');
|
|
202
|
-
});
|
|
203
|
-
chapter.parentNode.classList.add('is-current-item');
|
|
204
|
-
chapter.classList.add('is-current');
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
}, false);
|
|
210
|
-
|
|
211
|
-
// Get timeline post IDs from metadata.vtt
|
|
212
|
-
metadataTrack.addEventListener('cuechange', function () {
|
|
213
|
-
var metadataCues = metadataTrack.activeCues;
|
|
214
|
-
var chapterCues = chapterTrack.activeCues[0];
|
|
215
|
-
|
|
216
|
-
if (metadataCues.length > 0) {
|
|
217
|
-
var metadataCueMatch = metadataTrack.activeCues[0].text;
|
|
55
|
+
this.onTimeUpdate = function () {
|
|
56
|
+
return _this.dispatchEvent('onTimeUpdate');
|
|
57
|
+
};
|
|
218
58
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
59
|
+
this.onAbort = function () {
|
|
60
|
+
_this.dispatchEvent('onAbort');
|
|
61
|
+
window.location.href = _this.abortBtn.href;
|
|
62
|
+
};
|
|
222
63
|
|
|
223
|
-
|
|
64
|
+
this.element = element;
|
|
65
|
+
this.video = element.querySelector('.js-video-guide');
|
|
66
|
+
this.abortBtn = element.querySelector('.js-abort-guide');
|
|
67
|
+
this.playback = null;
|
|
68
|
+
this.subtitles = null;
|
|
69
|
+
this.timeline = null;
|
|
224
70
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (chapterCues) {
|
|
230
|
-
var chapterStartTime = chapterCues.startTime;
|
|
71
|
+
// Set all track elements to hidden mode to allow scripting
|
|
72
|
+
[].forEach.call(this.video.textTracks, function (txtTrack) {
|
|
73
|
+
txtTrack.mode = 'hidden';
|
|
74
|
+
});
|
|
231
75
|
|
|
232
|
-
|
|
233
|
-
var listElement = void 0;
|
|
234
|
-
var timeOut = null;
|
|
76
|
+
this.attach();
|
|
235
77
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
78
|
+
// loadedmetadata might not be fired if the video is already loaded
|
|
79
|
+
if (this.video.readyState >= 1) {
|
|
80
|
+
this.onLoadedMetadata();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
239
83
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}, function () {});
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}, false);
|
|
84
|
+
_createClass(VideoGuide, [{
|
|
85
|
+
key: 'attach',
|
|
86
|
+
value: function attach() {
|
|
87
|
+
this.video.addEventListener('loadedmetadata', this.onLoadedMetadata);
|
|
88
|
+
this.video.addEventListener('play', this.onPlay);
|
|
89
|
+
this.video.addEventListener('pause', this.onPause);
|
|
90
|
+
this.video.addEventListener('ended', this.onEnded);
|
|
91
|
+
this.video.addEventListener('timeupdate', this.onTimeUpdate);
|
|
92
|
+
this.abortBtn.addEventListener('click', this.onAbort);
|
|
93
|
+
}
|
|
94
|
+
}]);
|
|
255
95
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
var subtitlesCues = subtitlesTrack.activeCues;
|
|
96
|
+
return VideoGuide;
|
|
97
|
+
}();
|
|
259
98
|
|
|
260
|
-
|
|
261
|
-
var subtitlesCuesMatch = subtitlesTrack.activeCues[0].text;
|
|
262
|
-
subtitlesContainer.innerHTML = '<span>' + subtitlesCuesMatch + '</span>';
|
|
263
|
-
}
|
|
264
|
-
}, false);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
99
|
+
var elements = document.querySelectorAll('[data-video-guide]');
|
|
268
100
|
|
|
269
|
-
|
|
101
|
+
elements.forEach(function (element) {
|
|
102
|
+
return new VideoGuide(element);
|
|
103
|
+
});
|
package/package.json
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
WEBVTT
|
|
2
|
+
|
|
3
|
+
00:00:00.000 --> 00:00:31.000
|
|
4
|
+
Kapitel 1 / 7: Introduktion
|
|
5
|
+
|
|
6
|
+
00:00:32.000 --> 00:02:18.000
|
|
7
|
+
Kapitel 2 / 7: Minns du ditt första möte med internet?
|
|
8
|
+
|
|
9
|
+
00:02:19.000 --> 00:03:44.000
|
|
10
|
+
Kapitel 3 / 7: Tidiga internet i hemmet
|
|
11
|
+
|
|
12
|
+
00:03:45.000 --> 00:05:59.000
|
|
13
|
+
Kapitel 4 / 7: De första hemsidorna
|
|
14
|
+
|
|
15
|
+
00:06:00.000 --> 00:08:21.000
|
|
16
|
+
Kapitel 5 / 7: Lunarstorm och nätet innan sociala medier
|
|
17
|
+
|
|
18
|
+
00:08:22.000 --> 00:09:36.000
|
|
19
|
+
Kapitel 6 / 7: Flash-explosionen
|
|
20
|
+
|
|
21
|
+
00:09:37.000 --> 00:11:36.000
|
|
22
|
+
Kapitel 7 / 7: 2000-talet
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
WEBVTT
|
|
2
|
+
|
|
3
|
+
00:00:00.000 --> 00:00:08.000
|
|
4
|
+
95494
|
|
5
|
+
|
|
6
|
+
A second headline
|
|
7
|
+
00:00:08.000 --> 00:00:32.000
|
|
8
|
+
95497
|
|
9
|
+
|
|
10
|
+
A third headline
|
|
11
|
+
00:00:32.000 --> 00:01:12.000
|
|
12
|
+
95463
|
|
13
|
+
|
|
14
|
+
00:01:12.000 --> 00:01:24.000
|
|
15
|
+
95461
|
|
16
|
+
|
|
17
|
+
00:01:24.000 --> 00:01:40.000
|
|
18
|
+
95475
|
|
19
|
+
|
|
20
|
+
00:01:40.000 --> 00:02:02.000
|
|
21
|
+
95459
|
|
22
|
+
|
|
23
|
+
00:02:02.000 --> 00:02:19.000
|
|
24
|
+
95450
|
|
25
|
+
|
|
26
|
+
00:02:19.000 --> 00:02:47.000
|
|
27
|
+
76402
|
|
28
|
+
|
|
29
|
+
00:02:47.000 --> 00:03:17.000
|
|
30
|
+
76293
|
|
31
|
+
|
|
32
|
+
00:03:17.000 --> 00:03:45.000
|
|
33
|
+
80889
|
|
34
|
+
|
|
35
|
+
00:03:45.000 --> 00:04:08.000
|
|
36
|
+
95472
|
|
37
|
+
|
|
38
|
+
00:04:08.000 --> 00:04:24.000
|
|
39
|
+
95471
|
|
40
|
+
|
|
41
|
+
00:04:24.000 --> 00:04:50.000
|
|
42
|
+
90431
|
|
43
|
+
|
|
44
|
+
00:04:50.000 --> 00:05:30.000
|
|
45
|
+
95464
|
|
46
|
+
|
|
47
|
+
00:05:30.000 --> 00:06:00.000
|
|
48
|
+
95448
|
|
49
|
+
|
|
50
|
+
00:06:00.000 --> 00:06:53.000
|
|
51
|
+
76309
|
|
52
|
+
|
|
53
|
+
00:06:53.000 --> 00:07:16.000
|
|
54
|
+
95456
|
|
55
|
+
|
|
56
|
+
00:07:16.000 --> 00:07:43.000
|
|
57
|
+
76300
|
|
58
|
+
|
|
59
|
+
00:07:43.000 --> 00:07:57.000
|
|
60
|
+
95447
|
|
61
|
+
|
|
62
|
+
00:07:57.000 --> 00:08:22.000
|
|
63
|
+
80888
|
|
64
|
+
|
|
65
|
+
00:08:22.000 --> 00:08:56.000
|
|
66
|
+
80747
|
|
67
|
+
|
|
68
|
+
00:08:56.000 --> 00:09:37.000
|
|
69
|
+
84790
|
|
70
|
+
|
|
71
|
+
00:09:37.000 --> 00:10:15.000
|
|
72
|
+
95455
|
|
73
|
+
|
|
74
|
+
00:10:15.000 --> 00:10:27.000
|
|
75
|
+
80637
|
|
76
|
+
|
|
77
|
+
00:10:27.000 --> 00:10:50.000
|
|
78
|
+
95477
|
|
79
|
+
|
|
80
|
+
00:10:50.000 --> 00:11:00.000
|
|
81
|
+
95466
|
|
82
|
+
|
|
83
|
+
00:11:00.000 --> 00:11:37.000
|
|
84
|
+
95467
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|