/* * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * For more information, please refer to */ // Data var segmentMap = SegmentMap; var bv = bandersnatch.videos['80988062'].interactiveVideoMoments.value; var choicePoints = bv.choicePointNavigatorMetadata.choicePointsMetadata.choicePoints; var momentsBySegment = bv.momentsBySegment; var segmentGroups = bv.segmentGroups; // Persistent state var ls = window.localStorage || {}; if (!('initialized' in ls)) { for (let k in bv.stateHistory) ls["persistentState_" + k] = JSON.stringify(bv.stateHistory[k]); ls['initialized'] = 't'; } function msToString(ms) { return new Date(ms).toUTCString().split(' ')[4]; } function getCurrentMs() { return Math.round(document.getElementById("video").currentTime * 1000.0); } function preconditionToJS(cond) { if (cond[0] == 'persistentState') { return 'JSON.parse(ls["persistentState_' + cond[1] + '"])'; } else if (cond[0] == 'not') { return '!(' + preconditionToJS(cond[1]) + ')'; } else if (cond[0] == 'and') { return '(' + cond.slice(1).map(preconditionToJS).join(' && ') + ')'; } else if (cond[0] == 'or') { return '(' + cond.slice(1).map(preconditionToJS).join(' || ') + ')'; } else if (cond[0] == 'eql' && cond.length == 3) { return '(' + cond.slice(1).map(preconditionToJS).join(' == ') + ')'; } else if (cond === false) { return 'false'; } else if (cond === true) { return 'true'; } else if (typeof cond === 'string') { return JSON.stringify(cond); } else { console.log('unsupported condition!', cond); return 'true'; } } function evalPrecondition(precondition, text) { if (precondition) { let cond = preconditionToJS(precondition); let match = eval(cond); console.log('precondition', text, ':', cond, '==', match); return match; } return true; } function checkPrecondition(preconditionId) { return evalPrecondition(bv.preconditions[preconditionId], preconditionId); } function resolveSegmentGroup(sg) { let results = []; for (let v of segmentGroups[sg]) { if (v.precondition) { if (!checkPrecondition(v.precondition)) continue; } if (v.segmentGroup) { results.push(resolveSegmentGroup(v.segmentGroup)); } else if (v.segment) { results.push(v.segment); } else { if (!checkPrecondition(v)) continue; results.push(v); } } console.log('segment group', sg, '=>', results); return results[0]; } /// Returns the segment ID at the given timestamp. /// There will be exactly one segment for any timestamp within the video file. function getSegmentId(ms) { for (const [k, v] of Object.entries(segmentMap.segments)) { if (ms >= v.startTimeMs && (!v.endTimeMs || ms < v.endTimeMs)) { return k; } } return null; } function getSegmentMs(segmentId) { return segmentMap.segments[segmentId].startTimeMs; } function getMoments(segmentId, ms) { let result = {}; let moments = momentsBySegment[segmentId] || []; for (let i = 0; i < moments.length; i++) { let m = moments[i]; let momentId = segmentId + '/' + i; if (ms >= m.startMs && ms < m.endMs && evalPrecondition(m.precondition, 'moment ' + momentId)) { result[momentId] = m; } } return result; } function newList(id) { var ul = document.getElementById(id); while (ul.firstChild) { ul.removeChild(ul.firstChild); } return ul; } function addItem(ul, text, url) { var li = document.createElement("li"); var a = document.createElement("a"); a.textContent = text; a.setAttribute('href', url); li.appendChild(a); ul.appendChild(li); } var nextChoice = -1; var nextSegment = null; function addZones(segmentId) { var ul = newList("interactionZones"); let caption = 'currentSegment(' + segmentId + ')'; addItem(ul, caption, 'javascript:playSegment("' + segmentId + '")'); var segment = segmentMap.segments[segmentId]; if (segment && segment.ui && segment.ui.interactionZones) { var index = 0; for (var z of segment.ui.interactionZones) { var startMs = z[0]; var stopMs = z[1]; let caption = segmentId + ' interactionZone ' + index; addItem(ul, caption, 'javascript:seek(' + startMs + ')'); index++; } } ul = newList("nextSegments"); if (segment) { for (const [k, v] of Object.entries(segment.next)) { let caption = k; if (segment.defaultNext == k) caption = '[' + caption + ']'; addItem(ul, caption, 'javascript:playSegment("' + k + '")'); } } } var currentChoiceMoment = null; function addChoices(r) { currentChoiceMoment = r; nextChoice = -1; var ul = newList("choices"); document.getElementById("choiceCaption").innerHTML = ''; if (!r) return; nextChoice = r.defaultChoiceIndex; let index = 0; for (let x of r.choices) { var caption = r.defaultChoiceIndex == index ? '[' + x.text + ']' : x.text; addItem(ul, caption, 'javascript:choice(' + index + ')'); index++; } if (r.id in choicePoints) document.getElementById("choiceCaption").innerHTML = choicePoints[r.id].description; } function momentStart(m, seeked) { console.log('momentStart', m, seeked); if (m.choices) { addChoices(m); } if (!seeked) applyImpression(m.impressionData); } function momentUpdate(m, ms) { //console.log('momentUpdate', m); if (m.choices) { var p = 100 - ((ms - m.startMs) * 100.0 / (m.endMs - m.startMs)); document.getElementById("progress").style.width = p + '%'; } } function momentEnd(m, seeked) { console.log('momentEnd', m, seeked); if (m.choices) { addChoices(null); document.getElementById("progress").style.width = 0; } } var timerId = 0; var lastMs = 0; var currentSegment; var lastSegment = null; var prevSegment = null; // for breadcrumbs var segmentTransition = false; var lastMoments = []; function ontimeupdate(evt) { var ms = getCurrentMs(); currentSegment = getSegmentId(ms); let segment = segmentMap.segments[currentSegment]; if (timerId) { clearTimeout(timerId); timerId = 0; } // Distinguish between the user seeking manually with