-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathVideoAugment.js
357 lines (289 loc) · 11.6 KB
/
VideoAugment.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
$(document).ready(function(){
// Declaring semi-global variables for later use.
var video = $('.video');
var state = video.data('video-player-state'); // Sometimes this fails and that's ok.
var time;
var augmentCounter = 0;
var augmentWidth = 60;
var skipEmAll = false;
var problemsBeingShown = 0;
var augmentTimer = [];
var overkill = false;
// Note that global variable AugmentOptions is defined on the HTML page.
// Set default options here.
if (typeof AugmentOptions == 'undefined'){
var AugmentOptions = {
'effect': 'fade',
'hide': {'direction':'left'},
'show': {'direction':'right'},
'speed': 500
}
}
console.log('working');
var preferredLocation = $('.wrapper-downloads')
preferredLocation.before('<div id="augmenttray"></div>');
preferredLocation.before('<div id="augmenttext"></div>');
// Put all the augments in the tray and make a timer for them.
$('#augments').children().each(function(index){
var currentAugment = $(this);
var augmentTitle = currentAugment.find('.title');
var augmentTime = currentAugment.attr('data-time');
// This is sort of over-logging. Left in for debug.
// Logger.log('harvardx.video_augments', {'display_augment': augmentTitle.text(),'time': augmentTime});
// console.log('displaying augment: ' + augmentTitle.text() + ' at time ' + augmentTime);
// Get the icon for the augment so I can use it in the tray.
var augmentIcon = currentAugment.find('.augmenticon');
// Give it an alt tag
augmentIcon.attr('alt', 'Icon: ' + augmentTitle.text() + ', ' + augmentTime + ' seconds');
// Put it in place
$('#augmenttray').append(augmentIcon);
// Put a div around it for styling purposes
augmentIcon.wrap('<div class="augmenttab" '
+ 'id="augment'
+ index
+ '" data-time="'
+ augmentTime
+ '"></div>');
// Wrap a link around the image so screen readers can click it
augmentIcon.wrap('<a href="" class="augmentlink"></a>');
// Give it a title
augmentIcon.parent().attr('title', augmentTitle.text() + ', ' + augmentTime + ' seconds');
// Move the augment itself to the tray.
$('#augmenttext').append(currentAugment);
// Prep the augment for use:
// Give it the augment class.
currentAugment.addClass('augment');
// Give it a unique ID
currentAugment.attr('id', 'augmentdetail'+index);
// Build the augment timer from the divs in the page.
augmentTimer[index] = {};
augmentTimer[index].time = Number($(this).attr('data-time'));
});
// Hide all the detail text until it's relevant.
$('.augment').hide();
augmentTimer.sort(timeCompare); // Uses a custom function to sort by time.
// I could have just done an array of times, but this will be more flexible later.
console.log(augmentTimer);
// Log play/pause events from the player.
video.on('pause', function () {
Logger.log("harvardx.video_augments", {"video_event": "pause"});
console.log('pause');
});
video.on('play', function () {
Logger.log("harvardx.video_augments", {"video_event": "play"});
console.log('play');
});
// Check to see whether the video is ready before continuing.
var waitForVid = setInterval(function(){
state = video.data('video-player-state'); // Sometimes this fails and that's ok.
if (state.videoPlayer.isCued()){
console.log('video data loaded');
clearInterval(waitForVid);
var pause = setTimeout(function(){
console.log('done waiting');
setUpData();
mainLoop();
}, 0);
}
}, 100);
// Checks local storage and gets data from the video.
// Also sets up a few listeners.
function setUpData(){
console.log('setting up data');
// Get the video data.
video = $('.video');
state = video.data('video-player-state');
time = state.videoPlayer.currentTime;
// If we start at a later time, set the counter appropriately.
setAugmentCounter(time);
// Reset the counter properly if we seek.
// Don't double-count for our own ISaidGoTo function.
video.on('seek', function(event, ui) {
if(!overkill){
setAugmentCounter(ui);
}else{
overkill = false;
}
});
// If someone clicks on one of the augment tabs, go to the appropriate time.
$('.augmentlink').on('click tap', function(event){
event.preventDefault();
var thisTime = $(this).parent().attr('data-time');
console.log(this);
setAugmentCounter(thisTime);
showAugment(thisTime, state);
ISaidGoTo(thisTime);
});
// If the first augment has zero or negative time, make it visible right away.
var firstTime = $('#augmentdetail0').attr('data-time')
if(firstTime <= 0){
showAugment(firstTime, state);
}
// Add listener for pop-up links.
$('.popup').on('click tap', function(event){
event.preventDefault();
var linkTitle = $(this).attr('href');
if(!state.videoPlayer.isPlaying()){
overkill = true; // Don't restart the video if it was paused.
}
popUpProblem(linkTitle, state);
});
// Add listener for outside links that should pause the video
$('.pausevideo').on('click tap', function(event){
console.log(event);
state.videoPlayer.pause();
});
/*******************************************
* NOTE
* The jQuery on('focus', function) exists.
* I should use that for when people are going through the augments via screen reader.
*******************************************/
}
// Every 500 ms, check to see whether we're going to add a new augment.
function mainLoop(){
var timeChecker = setInterval(function(){
try{
state.videoPlayer.update(); // Forced update of time. Required for Safari.
}
catch(err){
// If this fails, shut down this loop.
// it's probably because we moved to a new tab.
clearInterval(timeChecker);
}
time = state.videoPlayer.currentTime;
// Don't run off the end of the counter.
if(augmentCounter < augmentTimer.length){
// If we pass a new augment, display it and update the counter.
if(time > augmentTimer[augmentCounter].time){
if(!skipEmAll){
console.log('passed an augment, number ' + augmentCounter);
showAugment(augmentTimer[augmentCounter].time, state);
updateAugmentCounter(augmentCounter+1);
}else{
// We're still incrementing and tracking even if we skip items.
updateAugmentCounter(augmentCounter+1);
}
}
}
}, 500);
}
// This resets the augment counter to match the time.
function setAugmentCounter(soughtTime){
Logger.log('harvardx.video_embedded_problems', {'control_event': 'seek_to_' + soughtTime});
console.log('setAugmentCounter for time ' + soughtTime);
// Count up the augment timer until we're above the sought time.
for(var i = 0; i < augmentTimer.length; i++){
if(augmentTimer[i].time > soughtTime){
updateAugmentCounter(i);
break;
}
}
console.log('Augment counter is now ' + augmentCounter);
}
// This will have more in it if we go back to using local storage
function updateAugmentCounter(number){
augmentCounter = number;
console.log('updateAugmentCounter: ' + augmentCounter);
}
// Move to the current augment and highlight it.
function showAugment(augTime, state){
/*******************************************
* Can I have a nice small sound play when a video augment shows up?
* /static/91926__corsica-s__ding.wav is uploaded. Use that for now.
* reference: https://www.freesound.org/people/Corsica_S/sounds/91926/
* http://www.w3schools.com/tags/tag_audio.asp
* Alternatively: aria-live attributes:
* A value of polite will notify the user of the content change as soon as he/she is done with the current task. This might take the form of a beep or an audio indication of the update. The user can then choose to directly jump to the updated content. This value would be the most common for content updates, especially for things like status notification, weather or stock updates, chat messages, etc.
* An aria-live value of assertive will result in the user being alerted to the content change immediately or as soon as possible. Assertive would be used for important updates, such as error messages
*******************************************/
console.log('showAugment at time ' + augTime);
// First move the icon bar to the right location.
var currentIcon = $('[data-time="'+augTime+'"]');
var tray = $('#augmenttray');
var idnum = currentIcon.attr('id').replace('augment', '');
var newlocation = Math.max(augmentWidth * idnum - 10, 0); // Subtracting 10 to show a little to the left.
console.log('idnum: ' + idnum + ' newlocation: ' + newlocation);
tray.animate({scrollLeft: newlocation}, 700);
$('.augmenttab').addClass('greyout');
currentIcon.removeClass('greyout');
console.log('scrolled to ' + newlocation);
// Next, replace the text below it.
// Get the new text, hide the old one, and show the new one.
var currentAugment = $('.augment:visible');
currentAugment.hide(AugmentOptions.effect, AugmentOptions.hide, AugmentOptions.speed);
setTimeout(function(){
var newTextBlock = $('#augmentdetail' + idnum);
newTextBlock.show(AugmentOptions.effect, AugmentOptions.show, AugmentOptions.speed);
// Pause if it says to pause.
if(newTextBlock.attr('data-pause')){ state.videoPlayer.pause(); }
}, 500);
}
// Does the work of creating the dialogue.
// It pulls a question from lower down in the page, and puts it back when we're done.
function popUpProblem(title, state){
// Find the div for the problem based on its title.
augmentDiv = $('h2:contains(' + title + ')').parent().parent();
var augmentID = $('h2:contains(' + title + ')').parent().attr('id');
Logger.log('harvardx.video_augments', {'display_problem': title, 'problem_id': augmentID,'time': time});
console.log('displaying item: ' + title + ' ' + augmentID);
// Make a modal dialog out of the chosen problem.
augmentDiv.dialog({
modal: true,
dialogClass: "no-close",
resizable: true,
width: 800,
show: {
effect: 'fade',
duration: 200
},
buttons: {
'Skip': function() {
dialogDestroyer('skip_problem');
$( this ).dialog( 'destroy' ); // Put the problem back when we're done.
},
'Done': function() {
dialogDestroyer('mark_done');
$( this ).dialog( 'destroy' ); // Put the problem back when we're done.
},
},
open: function() {
// Highlight various controls.
$('span.ui-button-text:contains("Done")').addClass('answeredButton');
$('input.check.Check').attr('style', ' background: linear-gradient(to top, #9df 0%,#7bd 20%,#adf 100%); background-color:#ACF; text-shadow: none;');
state.videoPlayer.pause();
},
close: function(){
if(!overkill){
state.videoPlayer.play();
overkill = false;
}
Logger.log('harvardx.video_augments', {'unusual_event': 'dialog_closed_unmarked'});
console.log('dialog closed'); // Should be pretty rare. I took out the 'close' button.
}
});
}
// Log the destruction of the dialog and play the video if there are no more dialogs up.
function dialogDestroyer(message){
Logger.log('harvardx.video_augments', {'control_event': message});
console.log(message);
$('input.check.Check').removeAttr('style'); // un-blue the check button.
if(!overkill){
state.videoPlayer.play();
overkill = false;
}
}
// I blame multiple Javascript timing issues.
function ISaidGoTo(thisTime){
console.log('I said go to ' + thisTime);
overkill = true; // We're about to seek. Don't trigger our seek listener twice.
time = Math.max(+thisTime, 0); // Using + to cast as number.
state.videoPlayer.seekTo(time);
}
function timeCompare(a,b){
if (a.time < b.time)
return -1;
if (a.time > b.time)
return 1;
return 0;
}
});