├── .gitignore
├── .prettierrc
├── HXHelper Files.zip
├── HXHelper Files
├── HXEditor.js
├── HXPopUpProblems.js
├── HXVideoChime.js
├── HXVideoLinks.js
├── VideoLinks.css
├── ajax-loader.gif
├── hx_tiny_white.png
├── imageMapResizer.min.js
├── intro_accessible.js
├── introjs.css
├── prism.css
├── prism.js
├── quote-left.png
├── quote-right.png
├── slick-theme.css
├── slick.css
├── slick.eot
├── slick.js
├── slick.svg
├── slick.ttf
├── slick.woff
├── summernote-lite.min.css
├── summernote-lite.min.js
├── summernote.eot
├── summernote.ttf
├── summernote.woff
├── summernote.woff2
├── word_cloud_access.js
└── word_cloud_access_new.js
├── LICENSE
├── README.md
├── datamap
├── File-BlankMap-World6.svg - Wikimedia Commons.webloc
├── File-Reported Bigfoot sightings (updated).svg - Wikimedia Commons.webloc
├── For_EdX.html
├── For_files_and_uploads.zip
├── US_Canada_vector_map.svg
├── World_Data_Map.html
├── World_Map_Blank_for_data.svg
├── jquery-3.3.1.min.js
├── jquery.svg.package-1.5.0
│ ├── jquery.svg.css
│ ├── jquery.svg.js
│ ├── jquery.svg.min.js
│ ├── jquery.svganim.js
│ ├── jquery.svganim.min.js
│ ├── jquery.svgdom.js
│ ├── jquery.svgdom.min.js
│ ├── jquery.svgfilter.js
│ ├── jquery.svgfilter.min.js
│ ├── jquery.svggraph.js
│ ├── jquery.svggraph.min.js
│ ├── jquery.svgplot.js
│ ├── jquery.svgplot.min.js
│ ├── lion.svg
│ └── svgBasic.html
├── jquery.svgdom.js
├── map_base_style.css
├── map_data.js
├── papaparse.min.js
└── sample data
│ ├── Alzheimers.csv
│ ├── Malaria_dalys.csv
│ ├── Malaria_dalys.xlsx
│ ├── Musculoskeletal.csv
│ └── Unintentional_Injury.csv
├── form to table
├── For_EdX.html
├── formtable_csv.js
└── papaparse.min.js
├── hx-min.css
├── hx-min.js
├── hx-min.zip
├── hx-standard.zip
├── hx.css
├── hx.js
├── hxGlobalOptions.js
├── misc_zipfiles
└── introjs.zip
├── prism LICENSE.txt
├── public_html
├── bookmarklet_list.html
├── course_staff_adder.js
├── erase_all_uploads.js
└── flashcards
│ ├── flashcards.css
│ ├── flashcards.js
│ ├── index.html
│ ├── lib
│ ├── hammer.min.js
│ ├── hammer.min.js.map
│ └── papaparse.min.js
│ └── source.csv
├── summernote LICENSE.txt
└── text slider
├── ClimateImpactExplorer.csv
├── README.md
├── Slider Test.html
├── hx-text-slider-old.css
├── hx-text-slider-old.js
├── hx-text-slider.css
├── hx-text-slider.js
├── leads-to.jpg
├── leads-to.pxm
└── papaparse.js
/.gitignore:
--------------------------------------------------------------------------------
1 | ._DS_STORE
2 | somewhat unrelated/.DS_Store
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true
3 | }
4 |
--------------------------------------------------------------------------------
/HXHelper Files.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Colin-Fredericks/hx-js/b3f64a79a595b0ab8f91d8316313b91a52f4ef40/HXHelper Files.zip
--------------------------------------------------------------------------------
/HXHelper Files/HXPopUpProblems.js:
--------------------------------------------------------------------------------
1 | // This version is designed to be called by hx.js
2 | // Global variable HXPUPTimer is defined in the HTML.
3 | // The hmsToTime converter function is defined in hx.js
4 |
5 | var HXPopUpProblems = function (HXpopUpOptions, HXPUPTimer) {
6 | // Declaring semi-global variables for later use.
7 | var video = $('.video');
8 | var state;
9 | var time;
10 | var problemCounter;
11 | var skipEmAll;
12 | var protectedTime = false;
13 | var problemsBeingShown = 0;
14 |
15 | // Convert mm:ss format in HXPUPTimer to seconds.
16 | for (var i = 0; i < HXPUPTimer.length; i++) {
17 | for (var key in HXPUPTimer[i]) {
18 | if (key == 'time') {
19 | HXPUPTimer[i].time = hmsToTime(HXPUPTimer[i].time);
20 | }
21 | }
22 | }
23 |
24 | // Sort the pop-up timer.
25 | HXPUPTimer.sort(timeCompare); // Uses a custom function to sort by time.
26 | // Ditch any questions that are missing from the screen.
27 | HXPUPTimer = stripMissingQuestions(HXPUPTimer);
28 |
29 | // Log play/pause events from the player.
30 | // Also set the play/pause external control properly.
31 | video.on('pause', function () {
32 | logThatThing({ video_event: 'pause' });
33 | $('#hx-playpauseicon').html('‣');
34 | $('#hx-playpauseword').html('Play');
35 | });
36 |
37 | video.on('play', function () {
38 | logThatThing({ video_event: 'play' });
39 | // Also set the play/pause external control properly.
40 | $('#hx-playpauseicon').html('||'); // Need a better-looking pause icon.
41 | $('#hx-playpauseword').html('Pause');
42 | });
43 |
44 | // Check to see whether the video is ready before continuing.
45 | var waitForVid = setInterval(function () {
46 | try {
47 | state = video.data('video-player-state'); // Sometimes this fails and that's ok.
48 |
49 | if (typeof state.videoPlayer.player.getPlayerState() !== 'undefined') {
50 | clearInterval(waitForVid);
51 | setUpData();
52 | setUpControls();
53 | mainLoop();
54 | }
55 | } catch (err) {
56 | console.log('waiting for first video to be ready');
57 | }
58 | }, 200);
59 |
60 | // Take out any problems that don't appear on the page.
61 | function stripMissingQuestions(timer) {
62 | // Get an array with all the text of all the H3's (trimming whitespace)
63 | let allH3 = $('h3')
64 | .map((x, e) => $(e).text().trim())
65 | .toArray();
66 |
67 | // Remove any items from the timer that aren't in the list of H3's
68 | let newTimer = timer.filter((x) => allH3.includes(x.title));
69 |
70 | // Return the new timer.
71 | return newTimer;
72 | }
73 |
74 | // Checks local storage and gets data from the video.
75 | function setUpData() {
76 | // Get the video data.
77 | video = $('.video');
78 | state = video.data('video-player-state');
79 | time = state.videoPlayer.currentTime;
80 |
81 | // Storing a separate problem counter for each video.
82 | // Create the counter if it doesn't exist.
83 | if (!localStorage[state.id + '-counter']) {
84 | localStorage[state.id + '-counter'] = '0';
85 | localStorage[state.id + '-skip'] = 'false';
86 |
87 | // If the counter didn't exist, we're on a new browser.
88 | // Clear the questions from before the current time.
89 | clearOlderPopUps(time);
90 | }
91 |
92 | // Which problem are we on? Using + to cast as integer.
93 | problemCounter = +localStorage[state.id + '-counter'];
94 |
95 | // Are we currently skipping problems?
96 | skipEmAll = localStorage[state.id + '-skip'] === 'true';
97 | // If so, let's update the button.
98 | if (skipEmAll) {
99 | $('#hx-sunmoon').html('☾');
100 | $('#hx-onoff').html('Problems are Off');
101 | logThatThing({
102 | reload_event: 'turn_problems_off',
103 | time: time,
104 | });
105 | }
106 |
107 | // Let's avoid situations where we're faced with a question
108 | // mere seconds after the page loads, shall we?
109 | // Unless we're past the last problem...
110 | if (time < HXPUPTimer[HXPUPTimer.length - 1].time) {
111 | if (problemCounter > 0) {
112 | // Go to just after the previous question...
113 | ISaidGoTo(HXPUPTimer[problemCounter - 1].time + 1);
114 | } else {
115 | // Or all the way back to the beginning.
116 | ISaidGoTo(0);
117 | }
118 | }
119 | }
120 |
121 | // Makes the buttons work and sets up event handlers.
122 | function setUpControls() {
123 | // If they seek to a specific position, set the problem counter appropriately
124 | // so that earlier problems don't gang up on them.
125 | video.on('seek', function (event, ui) {
126 | clearOlderPopUps(ui);
127 | });
128 |
129 | // Let someone go through the problems again if they want.
130 | // Also useful for debugging.
131 | $('#hx-popUpReset').on('click tap', function () {
132 | updateProblemCounter(0);
133 | ISaidGoTo(0);
134 | logThatThing({ control_event: 'reset counter and set t=0' });
135 |
136 | // If problems are currently on, turn them off for two seconds after we go back.
137 | // This addresses a bug that appears in Mobile Safari.
138 | // Note that you can't put questions in the first two seconds of the video
139 | // because of this.
140 | if (!skipEmAll) {
141 | skipEmAll = true;
142 | var dontSpamProblemsEarly = setTimeout(function () {
143 | skipEmAll = false;
144 | }, 2000);
145 | }
146 | });
147 |
148 | // Go back to one second after the previous problem.
149 | $('#hx-backOneProblem').on('click tap', function () {
150 | if (problemCounter > 1) {
151 | var newTime = HXPUPTimer[problemCounter - 2].time + 1;
152 | ISaidGoTo(newTime);
153 | logThatThing({ control_event: 'go back one' });
154 | } else {
155 | updateProblemCounter(0);
156 | ISaidGoTo(0);
157 | logThatThing({ control_event: 'go back one to start' });
158 | }
159 | });
160 |
161 | // Play or pause the video
162 | $('#hx-popUpPlayPause').on('click tap', function () {
163 | if (state.videoPlayer.isPlaying()) {
164 | state.videoPlayer.pause();
165 | $('#hx-playpauseicon').html('‣');
166 | $('#hx-playpauseword').html('Play');
167 | logThatThing({ control_event: 'play' });
168 | } else {
169 | state.videoPlayer.play();
170 | $('#hx-playpauseicon').html('||');
171 | $('#hx-playpauseword').html('Pause');
172 | logThatThing({ control_event: 'pause' });
173 | }
174 | });
175 |
176 | // Let someone turn the pop-up questions on and off.
177 | // Give visual indication by changing the button.
178 | $('#hx-problemToggle').on('click tap', function () {
179 | if (skipEmAll) {
180 | skipEmAll = false;
181 | localStorage[state.id + '-skip'] = 'false';
182 | $('#hx-sunmoon').html('☼');
183 | $('#hx-onoff').html('Problems are On');
184 | logThatThing({
185 | control_event: 'turn_problems_on',
186 | time: time,
187 | });
188 | } else {
189 | skipEmAll = true;
190 | localStorage[state.id + '-skip'] = 'true';
191 | $('#hx-sunmoon').html('☾');
192 | $('#hx-onoff').html('Problems are Off');
193 | logThatThing({
194 | control_event: 'turn_problems_off',
195 | time: time,
196 | });
197 | }
198 | });
199 | }
200 |
201 | // Every 500 ms, check to see whether we're going to add a new problem.
202 | function mainLoop() {
203 | var timeChecker = setInterval(function () {
204 | try {
205 | state.videoPlayer.update(); // Forced update of time. Required for Safari.
206 | } catch (err) {
207 | // If the update fails, shut down this loop.
208 | // It's probably because we moved to a new tab.
209 | clearInterval(timeChecker);
210 | }
211 | time = state.videoPlayer.currentTime;
212 |
213 | if (problemCounter < HXPUPTimer.length) {
214 | if (time > HXPUPTimer[problemCounter].time) {
215 | if (!skipEmAll && !protectedTime) {
216 | popUpProblem(HXPUPTimer[problemCounter].title, state);
217 | }
218 | // We're still incrementing and tracking even if we skip problems.
219 | updateProblemCounter(problemCounter + 1);
220 | }
221 | }
222 | }, 500);
223 | }
224 |
225 | // Set all the time-related stuff to a particular time and then seek there.
226 | // Does the work of creating the dialogue.
227 | // It pulls a question from lower down in the page, and puts it back when we're done.
228 | function popUpProblem(title, state) {
229 | // Strip leading and trailing whitespace, 'cause it's the most common typo.
230 | title = title.trim();
231 |
232 | // Find the div for the problem based on its title.
233 | var problemDiv = $('h3:contains(' + title + ')').closest('.vert');
234 |
235 | var problemID = $('h3:contains(' + title + ')')
236 | .parent()
237 | .attr('id');
238 |
239 | var tempDiv;
240 | var dialogDiv = problemDiv;
241 | var includenext = false;
242 |
243 | // Sometimes we can't find an h3 to latch onto.
244 | // We put includenext into an HTML bit before it.
245 | // The dialog then displays the next item, and appends a clone of the HTML before it.
246 | if (problemDiv.find('span.hx-includer').text() == 'includenext') {
247 | dialogDiv = problemDiv.next();
248 | includenext = true;
249 | }
250 |
251 | logThatThing({
252 | display_problem: title,
253 | problem_id: problemID,
254 | time: time,
255 | });
256 |
257 | // Pause the video.
258 | state.videoPlayer.pause();
259 |
260 | // Make a modal dialog out of the chosen problem.
261 | dialogDiv.dialog({
262 | modal: true,
263 | dialogClass: 'hx-popup no-close',
264 | resizable: true,
265 | width: HXpopUpOptions.width,
266 | show: {
267 | effect: HXpopUpOptions.effect,
268 | duration: HXpopUpOptions.effectlength,
269 | },
270 | position: {
271 | my: 'top',
272 | at: 'top',
273 | of: video,
274 | },
275 | // position: {
276 | // my: HXpopUpOptions.myPosition,
277 | // at: HXpopUpOptions.atPosition,
278 | // of: HXpopUpOptions.ofTarget,
279 | // },
280 | buttons: {
281 | Skip: function () {
282 | if (includenext) {
283 | tempDiv.remove();
284 | } // We added it, we should erase it.
285 | dialogDestroyed('skip_problem');
286 | $(this).dialog('destroy'); // Put the problem back when we're done.
287 | },
288 | Done: function () {
289 | if (includenext) {
290 | tempDiv.remove();
291 | } // We added it, we should erase it.
292 | dialogDestroyed('mark_done');
293 | $(this).dialog('destroy'); // Put the problem back when we're done.
294 | },
295 | },
296 | open: function () {
297 | // If we're including two divs, append a clone of the first one above.
298 | if (includenext) {
299 | tempDiv = problemDiv.clone();
300 | dialogDiv.prepend(tempDiv);
301 | }
302 | // Highlight various controls.
303 | $('span.ui-button-text:contains("Done")').addClass('answeredButton');
304 | $('input.check.Check').attr(
305 | 'style',
306 | ' background: linear-gradient(to top, #9df 0%,#7bd 20%,#adf 100%); background-color:#ACF; text-shadow: none;'
307 | );
308 | problemsBeingShown++;
309 | },
310 | close: function () {
311 | state.videoPlayer.play();
312 | if (includenext) {
313 | tempDiv.remove();
314 | } // We added it, we should erase it.
315 | logThatThing({ unusual_event: 'dialog closed unmarked' }); // Should be pretty rare. I took out the 'close' button.
316 | },
317 | });
318 | }
319 |
320 | // Log the destruction of the dialog and play the video if there are no more dialogs up.
321 | function dialogDestroyed(message) {
322 | logThatThing({ control_event: message });
323 | $('input.check.Check').removeAttr('style'); // un-blue the check button.
324 | problemsBeingShown--;
325 | if (problemsBeingShown < 1) {
326 | state.videoPlayer.play();
327 | }
328 | }
329 |
330 | // This resets the problem counter to match the time.
331 | function clearOlderPopUps(soughtTime) {
332 | logThatThing({ control_event: 'seek to ' + soughtTime });
333 | updateProblemCounter(0); // Resetting fresh.
334 | for (var i = 0; i < HXPUPTimer.length; i++) {
335 | if (soughtTime > HXPUPTimer[i].time) {
336 | updateProblemCounter(i + 1);
337 | } else {
338 | break;
339 | }
340 | }
341 | }
342 |
343 | // I blame multiple Javascript timing issues.
344 | function ISaidGoTo(thisTime) {
345 | time = thisTime;
346 | state.videoPlayer.player.seekTo(thisTime);
347 | logThatThing({ seek_to: thisTime });
348 | }
349 |
350 | // Keep the counter and the local storage in sync.
351 | function updateProblemCounter(number) {
352 | problemCounter = number;
353 | localStorage[state.id + '-counter'] = number.toString();
354 | logThatThing({ problem_counter_set: problemCounter });
355 | }
356 |
357 | // This is a sorting function for my timer.
358 | function timeCompare(a, b) {
359 | if (a.time < b.time) return -1;
360 | if (a.time > b.time) return 1;
361 | return 0;
362 | }
363 |
364 | return true;
365 | };
366 |
--------------------------------------------------------------------------------
/HXHelper Files/HXVideoChime.js:
--------------------------------------------------------------------------------
1 | var HXVideoChime = function(hxChimeOptions) {
2 | // Declaring semi-global variables for later use.
3 | // HXChimeTimer is defined in HTML on the page.
4 | var video = $('.video');
5 | var chimebox = $('.hx-video-chimebox');
6 |
7 | logThatThing('Video chimes starting');
8 |
9 | // Let people turn the chimes on and off.
10 | let chimetoggle = $('');
11 |
12 | chimebox.append(chimetoggle);
13 | chimetoggle.wrap('
');
14 | if (localStorage.hxChimesOff === 'true') {
15 | chimetoggle.text('Turn video chimes off');
16 | } else {
17 | chimetoggle.text('Turn video chimes on');
18 | }
19 | chimetoggle.on('click tap', function() {
20 | localStorage.hxChimesOff = localStorage.hxChimesOff !== 'true';
21 | chimetoggle.text(
22 | localStorage.hxChimesOff === 'true'
23 | ? 'Turn video chimes off'
24 | : 'Turn video chimes on'
25 | );
26 | });
27 |
28 | // Mark each video and set of controls with a class and anchor
29 | // that will let us handle each of them separately.
30 | // Numbering from 1 to make things easier for course creators.
31 | video.each(function(index) {
32 | $(this).addClass('for-video-' + (index + 1));
33 | });
34 | video.each(function(index) {
35 | $(this)
36 | .parent()
37 | .prepend('');
38 | });
39 |
40 | video.each(function(vidnumber) {
41 | var thisVid = $(this);
42 | var chimeData = setUpLists(vidnumber);
43 | // Check to see whether the video is ready before continuing.
44 | var waitForVid = setInterval(function() {
45 | try {
46 | var state = thisVid.data('video-player-state'); // Sometimes this fails and that's ok.
47 |
48 | if (typeof state.videoPlayer !== 'undefined') {
49 | if (
50 | typeof state.videoPlayer.player.getPlayerState() !== 'undefined'
51 | ) {
52 | console.log('video data loaded');
53 | mainLoop(state, chimeData, vidnumber);
54 | clearInterval(waitForVid);
55 | }
56 | }
57 | } catch (err) {
58 | console.log('waiting for video ' + (vidnumber + 1) + ' to be ready');
59 | }
60 | }, 250);
61 | });
62 |
63 | // Make a list of all the times where there would be chimes.
64 | function setUpLists(vidnumber) {
65 | console.log('setting up lists of chimes');
66 |
67 | let sortedTimer = HXChimeTimer[vidnumber].slice().sort(timeCompare);
68 | let chimeData = {
69 | text: sortedTimer.map(e => e.title),
70 | timer: sortedTimer.map(e => hmsToTime(e.time)),
71 | sounds: sortedTimer.map(e => e.sound),
72 | played: []
73 | };
74 |
75 | // Write a list on-screen.
76 | chimeData.text.forEach(function(e, i) {
77 | let ch = $('');
78 | ch.text(timeToHMS(chimeData.timer[i]) + ' - ' + chimeData.text[i]);
79 | $(chimebox[vidnumber]).append(ch);
80 | $(chimebox[vidnumber]).append($('
'));
81 | });
82 |
83 | return chimeData;
84 | }
85 |
86 | // Every 250 ms, check to see whether we're going to play a chime.
87 | function mainLoop(state, chimeData, vidnumber) {
88 | console.log('start main loop for chimes');
89 | let time = 0;
90 | let lastTime = 0;
91 | let twoAgo = 0;
92 | let chimeTime = 0;
93 | let okToPlay = true;
94 |
95 | console.log(state);
96 |
97 | var timeChecker = setInterval(function() {
98 | try {
99 | state.videoPlayer.update(); // Forced update of time. Required for Safari.
100 | } catch (err) {
101 | // If this fails, shut down this loop.
102 | // It's probably because we moved to a new tab.
103 | clearInterval(timeChecker);
104 | }
105 |
106 | twoAgo = lastTime;
107 | lastTime = time;
108 | time = state.videoPlayer.currentTime;
109 | chimeTime = chimeData.timer[nextChime(chimeData, lastTime)];
110 |
111 | // Don't chime on initial play event.
112 | // Note: The time when the "play" event actually fires is late.
113 | // Checking the time is more reliable.
114 | if (twoAgo === lastTime) {
115 | okToPlay = false;
116 | } else {
117 | okToPlay = true;
118 | }
119 |
120 | // console.log(lastTime, chimeTime, time);
121 | // If the chime time is the median value, play a chime.
122 | if (
123 | lastTime <= chimeTime &&
124 | time >= chimeTime &&
125 | okToPlay &&
126 | localStorage.hxChimesOff !== 'false'
127 | ) {
128 | playChime(chimeData, lastTime, vidnumber);
129 | }
130 | }, 250);
131 | }
132 |
133 | // Play the current chime.
134 | function playChime(chimeData, time, vidnumber) {
135 | let n = nextChime(chimeData, time);
136 | let assetURL = getAssetURL(window.location.href, 'complete');
137 | let audio = new Audio(assetURL + chimeData.sounds[n]);
138 | audio.play();
139 | console.log(
140 | 'Playing chime ' +
141 | (n + 1) +
142 | ', ' +
143 | chimeData.sounds[n] +
144 | ', for video ' +
145 | (vidnumber + 1)
146 | );
147 | }
148 |
149 | function nextChime(chimeData, time) {
150 | let next = 0;
151 | for (let n = 0; n < chimeData.timer.length; n++) {
152 | if (chimeData.timer[n] < time) {
153 | next++;
154 | }
155 | }
156 | return next;
157 | }
158 |
159 | // This is a sorting function for my timer.
160 | function timeCompare(a, b) {
161 | if (hmsToTime(a.time) < hmsToTime(b.time)) return -1;
162 | if (hmsToTime(a.time) > hmsToTime(b.time)) return 1;
163 | return 0;
164 | }
165 |
166 | return true;
167 | };
168 |
--------------------------------------------------------------------------------
/HXHelper Files/HXVideoLinks.js:
--------------------------------------------------------------------------------
1 | var HXVideoLinks = function(hxLinkOptions) {
2 | // Declaring semi-global variables for later use.
3 | var video = $('.video');
4 | var vidWrappers = $('.video-wrapper');
5 | var time;
6 | var linkTimer = [];
7 | var linkBeingShown = [];
8 |
9 | logThatThing('Video Links starting');
10 |
11 | // Mark each video and set of controls with a class and anchor
12 | // that will let us handle each of them separately.
13 | // Numbering from 1 to make things easier for course creators.
14 | video.each(function(index) {
15 | $(this).addClass('for-video-' + (index + 1));
16 | });
17 | video.each(function(index) {
18 | $(this)
19 | .parent()
20 | .prepend('');
21 | });
22 | vidWrappers.each(function(index) {
23 | $(this).addClass('for-video-' + (index + 1));
24 | });
25 |
26 | video.each(function(vidnumber) {
27 | var thisVid = $(this);
28 | setUpLists(vidnumber);
29 |
30 | // Check to see whether the video is ready before continuing.
31 | var waitForVid = setInterval(function() {
32 | try {
33 | var state = thisVid.data('video-player-state'); // Sometimes this fails and that's ok.
34 |
35 | if (typeof state.videoPlayer !== 'undefined') {
36 | if (
37 | typeof state.videoPlayer.player.getPlayerState() !== 'undefined'
38 | ) {
39 | console.log('video data loaded');
40 |
41 | // Follow a video player link from an old page, if we have one.
42 | checkJumpFromOldPage();
43 |
44 | // We're positioning links based on the video.
45 | vidWrappers.addClass('link-positioner');
46 |
47 | setUpListeners(state);
48 | mainLoop(state, vidnumber);
49 | clearInterval(waitForVid);
50 | }
51 | }
52 | } catch (err) {
53 | console.log('waiting for video ' + (vidnumber + 1) + ' to be ready');
54 | }
55 | }, 200);
56 | });
57 |
58 | // Take the simple list in our HTML and make it FABULOUS
59 | function setUpLists(vidnumber) {
60 | // Let's copy the links to the appropriate location so we can position them there.
61 | var vidlinks = $('#hx-vidlinks-static-' + (vidnumber + 1))
62 | .clone()
63 | .prop('id', 'hx-vidlinks-live-' + (vidnumber + 1));
64 | vidlinks.appendTo('.video-wrapper.for-video-' + (vidnumber + 1));
65 |
66 | linkTimer[vidnumber] = [];
67 |
68 | // Each link needs a little bit added to it, to keep the author view simple.
69 | // This preps the links that we're going to display on the video.
70 | $('#hx-vidlinks-live-' + (vidnumber + 1))
71 | .children()
72 | .each(function(index) {
73 | var thisLinkBox = $(this);
74 | var thisLink = $(this).find('a');
75 |
76 | // Give the link a class and a unique ID
77 | thisLinkBox.addClass('hx-vidlink_' + hxLinkOptions.location);
78 | thisLinkBox.attr('id', 'link-card-live-' + index);
79 |
80 | // Give the images a class for styling purporses.
81 | thisLink.find('img').addClass('hx-vidlinkicon');
82 |
83 | // Make all the links open in new pages.
84 | thisLink.attr('target', '_blank');
85 | // Style all the links
86 | thisLink.addClass('hx-link-text-live');
87 |
88 | // Screen readers should skip these links. Rarely (but not never) an issue.
89 | thisLinkBox.attr('aria-hidden', 'true');
90 |
91 | // Build the link timer from the divs.
92 | var tempTimer = {
93 | time: hmsToTime(thisLinkBox.attr('data-time')),
94 | shown: false
95 | };
96 | linkTimer[vidnumber].push(tempTimer);
97 | });
98 |
99 | // This preps the ones that are visible all the time.
100 | $('#hx-vidlinks-static-' + (vidnumber + 1))
101 | .children()
102 | .each(function(index) {
103 | var thisLinkBox = $(this);
104 | var thisLink = $(this).find('a');
105 |
106 | // Give the link a class and a unique ID
107 | thisLinkBox.addClass('hx-vidlink-static');
108 | thisLinkBox.attr('id', 'link-card-static-' + index);
109 |
110 | // Remove the images.
111 | thisLink.find('img').remove();
112 |
113 | // Add time links before.
114 | let timelink = $('');
115 | timelink.attr('href', thisLinkBox.attr('data-time'));
116 | timelink.addClass('jumptime');
117 | timelink.text(timeToHMS(hmsToTime(thisLinkBox.attr('data-time'))));
118 | thisLinkBox.prepend(' - ');
119 | thisLinkBox.prepend(timelink);
120 | console.log('timelink added');
121 | });
122 |
123 | // Finish making the unordered list.
124 | $('#hx-vidlinks-static-' + (vidnumber + 1) + ' .hx-vidlink-static').wrapAll(
125 | ''
126 | );
127 |
128 | linkTimer[vidnumber].sort(timeCompare); // Uses a custom function to sort by time.
129 | }
130 |
131 | // Set up listeners for the live links.
132 | function setUpListeners(state) {
133 | // If they click on one of the live links, pause the video.
134 | $('.hx-link-text-live').on('click tap', function() {
135 | state.videoPlayer.pause();
136 | });
137 | }
138 |
139 | // Every 500 ms, check to see whether we're going to show a new link.
140 | function mainLoop(state, vidnumber) {
141 | var timeChecker = setInterval(function() {
142 | try {
143 | state.videoPlayer.update(); // Forced update of time. Required for Safari.
144 | } catch (err) {
145 | // If this fails, shut down this loop.
146 | // It's probably because we moved to a new tab.
147 | clearInterval(timeChecker);
148 | }
149 | time = state.videoPlayer.currentTime;
150 |
151 | // If we should be showing a link:
152 | if (currentLink(time, vidnumber) != -1) {
153 | // ...and there's something being shown,
154 | if (linkBeingShown[vidnumber]) {
155 | // but it's not the one that should be shown,
156 | if (currentLink(time, vidnumber) != currentLinkShown(vidnumber)) {
157 | // then hide it.
158 | hideLink(currentLinkShown(vidnumber), vidnumber);
159 | }
160 | }
161 |
162 | // ...and there's nothing being shown,
163 | else {
164 | // then show the one we should be showing.
165 | showLink(currentLink(time, vidnumber), vidnumber);
166 | }
167 |
168 | // If we should NOT be showing a link,
169 | } else {
170 | // ...and one is showing, hide it.
171 | if (currentLinkShown(vidnumber) != -1) {
172 | hideLink(currentLinkShown(vidnumber), vidnumber);
173 | }
174 | }
175 | }, 500);
176 | }
177 |
178 | // Show the link on the video. While we're at it, bold the one in the list too.
179 | function showLink(n, vidnumber) {
180 | console.log('showing link ' + (n + 1) + ' for video ' + (vidnumber + 1));
181 | $('#hx-vidlinks-live-' + (vidnumber + 1) + ' #link-card-live-' + n).show(
182 | hxLinkOptions.effect,
183 | hxLinkOptions.show,
184 | hxLinkOptions.speed
185 | );
186 | $('#hx-vidlinks-static-' + (vidnumber + 1) + ' #link-card-static-' + n)
187 | .children()
188 | .addClass('hx-boldlink');
189 | linkTimer[vidnumber][n].shown = true;
190 | linkBeingShown[vidnumber] = true;
191 | }
192 |
193 | // Hide the link on the video and un-bold the one on the list.
194 | function hideLink(n, vidnumber) {
195 | console.log('hiding link ' + (n + 1) + ' for video ' + (vidnumber + 1));
196 | $('#hx-vidlinks-live-' + (vidnumber + 1) + ' #link-card-live-' + n).hide(
197 | hxLinkOptions.effect,
198 | hxLinkOptions.show,
199 | hxLinkOptions.speed
200 | );
201 | $('#hx-vidlinks-static-' + (vidnumber + 1) + ' #link-card-static-' + n)
202 | .children()
203 | .removeClass('hx-boldlink');
204 | linkTimer[vidnumber][n].shown = false;
205 | linkBeingShown[vidnumber] = false;
206 | }
207 |
208 | function checkJumpFromOldPage() {
209 | console.log('check for jump');
210 | // If we have a jump-to-time link pending for this page,
211 | // go there and start the video playing.
212 | if (localStorage.HXVideoLinkGo === 'true') {
213 | if (window.location.href.indexOf(localStorage.HXVideoLinkUnit) != -1) {
214 | logThatThing({
215 | 'Jumping to video': {
216 | unit: localStorage.HXVideoLinkUnit,
217 | 'video number': localStorage.HXVideoLinkNumber,
218 | time: localStorage.HXVideoLinkTime
219 | }
220 | });
221 | // Make sure the video is ready before we try to go to the time.
222 | jumpToTime(
223 | localStorage.HXVideoLinkNumber,
224 | localStorage.HXVideoLinkTime
225 | );
226 | }
227 | } else {
228 | console.log('No video link to jump to.');
229 | }
230 |
231 | localStorage.HXVideoLinkGo = 'false';
232 | }
233 |
234 | // Which link SHOULD we be showing right now? Return -1 if none.
235 | // If we should be showing several, returns the first one.
236 | function currentLink(t, vidnumber) {
237 | var linkNumber = -1;
238 |
239 | for (var i = 0; i < linkTimer[vidnumber].length; i++) {
240 | if (
241 | t >= linkTimer[vidnumber][i].time &&
242 | t < linkTimer[vidnumber][i].time + hxLinkOptions.hideLinkAfter
243 | ) {
244 | linkNumber = i;
245 | break;
246 | }
247 | }
248 | return linkNumber;
249 | }
250 |
251 | // Which link are we ACTUALLY showing right now? Return -1 if none.
252 | // If we're showing several, returns the first one.
253 | function currentLinkShown(vidnumber) {
254 | var linkNumber = -1;
255 |
256 | for (var i = 0; i < linkTimer[vidnumber].length; i++) {
257 | if (linkTimer[vidnumber][i].shown) {
258 | linkNumber = i;
259 | break;
260 | }
261 | }
262 | return linkNumber;
263 | }
264 |
265 | // This is a sorting function for my timer.
266 | function timeCompare(a, b) {
267 | if (a.time < b.time) return -1;
268 | if (a.time > b.time) return 1;
269 | return 0;
270 | }
271 |
272 | return true;
273 | };
274 |
--------------------------------------------------------------------------------
/HXHelper Files/VideoLinks.css:
--------------------------------------------------------------------------------
1 | #vidlinks-static {
2 | }
3 |
4 | #vidlinks-live {
5 | }
6 |
7 | .link-text-live {
8 | }
9 |
10 | .vidlink_bl {
11 | display: none;
12 | opacity: 0.7;
13 | background-color: black;
14 |
15 | font-size: 16px;
16 | color: white;
17 |
18 | position: absolute;
19 | bottom: 50px;
20 |
21 | padding: 4px;
22 | border: 2px solid grey;
23 | margin: 2px;
24 |
25 | border-radius: 4px;
26 | box-shadow: 2px 2px 4px #111;
27 | }
28 |
29 | .vidlink_br {
30 | display: none;
31 | opacity: 0.7;
32 | background-color: black;
33 |
34 | font-size: 16px;
35 | color: white;
36 |
37 | position: absolute;
38 | bottom: 50px;
39 | right: 5px;
40 |
41 | padding: 4px;
42 | border: 2px solid grey;
43 | margin: 2px;
44 |
45 | border-radius: 4px;
46 | box-shadow: 2px 2px 4px #111;
47 | }
48 |
49 | .vidlink_tl {
50 | display: none;
51 | opacity: 0.7;
52 | background-color: black;
53 |
54 | font-size: 16px;
55 | color: white;
56 |
57 | position: absolute;
58 | top: 5px;
59 |
60 | padding: 4px;
61 | border: 2px solid grey;
62 | margin: 2px;
63 |
64 | border-radius: 4px;
65 | box-shadow: 2px 2px 4px #111;
66 | }
67 |
68 | .vidlink_tr {
69 | display: none;
70 | opacity: 0.7;
71 | background-color: black;
72 |
73 | font-size: 16px;
74 | color: white;
75 |
76 | position: absolute;
77 | top: 5px;
78 | right: 5px;
79 |
80 | padding: 4px;
81 | border: 2px solid grey;
82 | margin: 2px;
83 |
84 | border-radius: 4px;
85 | box-shadow: 2px 2px 4px #111;
86 | }
87 |
88 | .vidlink-static {
89 | display: list-item;
90 | }
91 |
92 | .vidlink_bl a:link {
93 | color: white;
94 | }
95 |
96 | .vidlink_tl a:link {
97 | color: white;
98 | }
99 |
100 | .vidlink_tr a:link {
101 | color: white;
102 | }
103 |
104 | .vidlink_br a:link {
105 | color: white;
106 | }
107 |
108 | .vidlink_bl a:visited {
109 | color: white;
110 | }
111 |
112 | .vidlink_tl a:visited {
113 | color: white;
114 | }
115 |
116 | .vidlink_tr a:visited {
117 | color: white;
118 | }
119 |
120 | .vidlink_br a:visited {
121 | color: white;
122 | }
123 |
124 | .link-positioner {
125 | position: relative !important;
126 | }
127 |
128 |
129 | .boldlink {
130 | font-weight: bold !important;
131 | }
--------------------------------------------------------------------------------
/HXHelper Files/ajax-loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Colin-Fredericks/hx-js/b3f64a79a595b0ab8f91d8316313b91a52f4ef40/HXHelper Files/ajax-loader.gif
--------------------------------------------------------------------------------
/HXHelper Files/hx_tiny_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Colin-Fredericks/hx-js/b3f64a79a595b0ab8f91d8316313b91a52f4ef40/HXHelper Files/hx_tiny_white.png
--------------------------------------------------------------------------------
/HXHelper Files/imageMapResizer.min.js:
--------------------------------------------------------------------------------
1 | /*! Image Map Resizer (imageMapResizer.min.js ) - v1.0.7 - 2018-05-01
2 | * Desc: Resize HTML imageMap to scaled image.
3 | * Copyright: (c) 2018 David J. Bradshaw - dave@bradshaw.net
4 | * License: MIT
5 | */
6 |
7 | !function(){"use strict";function a(){function a(){function a(a,d){function e(a){var d=1===(f=1-f)?"width":"height";return c[d]+Math.floor(Number(a)*b[d])}var f=0;j[d].coords=a.split(",").map(e).join(",")}var b={width:l.width/l.naturalWidth,height:l.height/l.naturalHeight},c={width:parseInt(window.getComputedStyle(l,null).getPropertyValue("padding-left"),10),height:parseInt(window.getComputedStyle(l,null).getPropertyValue("padding-top"),10)};k.forEach(a)}function b(a){return a.coords.replace(/ *, */g,",").replace(/ +/g,",")}function c(){clearTimeout(m),m=setTimeout(a,250)}function d(){l.width===l.naturalWidth&&l.height===l.naturalHeight||a()}function e(){l.addEventListener("load",a,!1),window.addEventListener("focus",a,!1),window.addEventListener("resize",c,!1),window.addEventListener("readystatechange",a,!1),document.addEventListener("fullscreenchange",a,!1)}function f(){return"function"==typeof i._resize}function g(a){return document.querySelector('img[usemap="'+a+'"]')}function h(){j=i.getElementsByTagName("area"),k=Array.prototype.map.call(j,b),l=g("#"+i.name)||g(i.name),i._resize=a}var i=this,j=null,k=null,l=null,m=null;f()?i._resize():(h(),e(),d())}function b(){function b(a){if(!a.tagName)throw new TypeError("Object is not a valid DOM element");if("MAP"!==a.tagName.toUpperCase())throw new TypeError("Expected