/* ShowOff JS Logic */ var ShowOff = {}; var preso_started = false var slidenum = 0 var slideTotal = 0 var slides var currentSlide var totalslides = 0 var slidesLoaded = false var incrSteps = 0 var incrElem var incrCurr = 0 var incrCode = false var debugMode = false var gotoSlidenum = 0 var lastMessageGuid = 0 var query; var section = 'handouts'; // default to showing handout notes for display view var slideStartTime = new Date().getTime() var activityIncomplete = false; // slides won't advance when this is on var loadSlidesBool var loadSlidesPrefix var mode = { track: true, follow: true }; // since javascript doesn't have a built-in way to get to cookies easily, // let's just add our own data structure. document.cookieHash = {} document.cookie.split(';').forEach( function(item) { var pos = item.indexOf('='); var key = item.slice(0,pos).trim(); var val = item.slice(pos+1).trim(); try { val = JSON.parse(val); } catch(e) { } document.cookieHash[key] = val; }); $(document).on('click', 'code.execute', executeCode); function setupPreso(load_slides, prefix) { if (preso_started) { alert("already started"); return; } preso_started = true; if (! cssPropertySupported('flex') ) { window.location = 'unsupported.html'; } if (! cssPropertySupported('zoom') ) { $('body').addClass('no-zoom'); } // save our query string as an object for later use query = $.parseQuery(); // Load slides fetches images loadSlidesBool = load_slides; loadSlidesPrefix = prefix || '/'; loadSlides(loadSlidesBool, loadSlidesPrefix); setupSideMenu(); doDebugStuff(); // bind event handlers toggleKeybinding('on'); $('#preso').addSwipeEvents(). // bind('tap', swipeLeft). // next bind('swipeleft', swipeLeft). // next bind('swiperight', swipeRight); // prev $('#buttonNav #buttonPrev').click(prevStep); $('#buttonNav #buttonNext').click(nextStep); // give us the ability to disable tracking via url parameter if(query.track == 'false') mode.track = false; // Make sure the slides always look right. // Better would be dynamic calculations, but this is enough for now. zoom(); $(window).resize(function() {zoom();}); // yes, this is a global annotations = new Annotate(); $("#help-modal").dialog({ autoOpen: false, dialogClass: "no-close", draggable: false, height: 640, modal: true, resizable: false, width: 640, buttons: { Close: function() { $( this ).dialog( "close" ); } } }); // wait until the presentation is loaded to hook up the previews. $("body").bind("showoff:loaded", function (event) { $('#navigation li a.navItem').hover(function() { var position = $(this).position(); $('#navigationHover').css({top: position.top, left: position.left + $('#navigation').width() + 5}) $('#navigationHover').html(slides.eq($(this).attr('rel')).html()); $('#navigationHover').show(); },function() { $('#navigationHover').hide(); }); }); // Open up our control socket connectControlChannel(); } function loadSlides(load_slides, prefix, reload, hard) { var url = loadSlidesPrefix + "slides"; if (reload) { url += "?cache=clear"; } //load slides offscreen, wait for images and then initialize $('body').addClass('busy'); if (load_slides) { $("#slides").load(url, false, function(){ if(hard) { location.reload(true); } else { $("#slides img").batchImageLoad({ loadingCompleteCallback: initializePresentation(prefix) }); } }) } else { $("#slides img").batchImageLoad({ loadingCompleteCallback: initializePresentation(prefix) }) } } function initializePresentation(prefix) { // unhide for height to work in static mode $("#slides").show(); //copy into presentation area $("#preso").empty() $('#slides > .slide').appendTo($("#preso")) //populate vars slides = $('#preso > .slide') slideTotal = slides.size() //setup manual jquery cycle $('#preso').cycle({ timeout: 0 }) setupMenu(); if (slidesLoaded) { showSlide() } else { showFirstSlide(); slidesLoaded = true } setupSlideParamsCheck(); // Remove spinner in case we're reloading $('body').removeClass('busy'); $('pre.highlight code').each(function(i, block) { try { hljs.highlightBlock(block); // Highlight requested lines block.innerHTML = block.innerHTML.split(/\r?\n/).map(function (line, i) { if (line.indexOf('* ') === 0) { return line.replace(/^\*(.*)$/, '
$1
'); } return line; }).join('\n'); } catch(e) { console.log('Syntax highlighting failed on ' + $(this).closest('div.slide').attr('id')); console.log('Syntax highlighting failed for ' + $(this).attr('class')); console.log(e); } }); $(".content form").submit(function(e) { e.preventDefault(); submitForm($(this)); }); // suspend hotkey handling $(".content form :input").focus( function() { toggleKeybinding(); }); $(".content form :input").blur( function() { toggleKeybinding(); }); $(".content form :input").change(function(e) { enableForm($(this)); }); $(".content form div.tools input.display").click(function(e) { try { // If we're a presenter, try to bust open the slave display slaveWindow.renderForm($(this).closest('form').attr('id')); } catch (e) { console.log(e); renderForm($(this).closest('form')); } }); // The display window doesn't need the extra chrome if(typeof(presenterView) != 'undefined') { $('.slide.activity').removeClass('activity').children('.activityToggle').remove(); } $('.slide.activity .activityToggle input.activity').checkboxradio(); $('.slide.activity .activityToggle input.activity').change(toggleComplete); // initialize mermaid, but don't render yet since the slide sizes are indeterminate mermaid.initialize({startOnLoad:false}); $("#preso").trigger("showoff:loaded"); } function zoom(presenter) { var preso = $("#preso"); var hSlide = parseFloat(preso.height()); var wSlide = parseFloat(preso.width()); var hBody = parseFloat(preso.parent().height()); var wBody = parseFloat(preso.parent().width()); var newZoom = Math.min(hBody/hSlide, wBody/wSlide); // match the 65/35 split in the stylesheet for the side-by-side layout if($("#preview").hasClass("beside")) { wBody *= 0.64; newZoom = Math.min(hBody/hSlide, wBody/wSlide); } // Calculate margins to center the thing *before* scaling // Vertically center on presenter, top align everywhere else if(presenter) { var hMargin = (hBody - hSlide) /2; } else { // (center of slide to top) - (half of the zoomed slide) //var hMargin = (hSlide/2 * newZoom) - (hSlide / 2); var hMargin = (hSlide * newZoom - hSlide) / 2; } var wMargin = (wBody - wSlide) /2; preso.css("margin", hMargin + "px " + wMargin + "px"); preso.css("transform", "scale(" + newZoom + ")"); // correct the zoom factor for the presenter if (presenter) { // We only want to zoom if the canvas is actually zoomed. Firefox and IE // should *not* be zoomed, so we want to exclude them. We do that by reading // back the zoom property. It will return a string percentage in IE, which // won't parse as a number, and Firefox simply returns undefined. // Because reasons. // TODO: When we fix the presenter on IE so the viewport isn't all wack, we // may have to revisit this. var zoomLevel = Number( preso.css('zoom') ) || 1; annotations.zoom = 1 / zoomLevel } } function setupSideMenu() { $("#hamburger").click(function() { $('#feedbackSidebar, #sidebarExit').toggle(); toggleKeybinding(); }); $("#navToggle").click(function() { $("#navigation").toggle(); updateMenuChevrons(); }); $('#fileDownloads').click(function() { closeMenu(); window.open('/download'); }) $("#paceSlower").click(function() { sendPace('slower'); }); $("#paceFaster").click(function() { sendPace('faster'); }); $('#questionToggle').click(function() { if ( ! $(this).hasClass('disabled') ) { $('#questionSubmenu').toggle(); } }); $("#askQuestion").click(function() { if ( ! $(this).hasClass('disabled') ) { var question = $("#question").val() var qid = askQuestion(question); feedback_response(this, "Sending..."); $("#question").val(''); var questionItem = $('
  • ').text(question).attr('id', qid); questionItem.click( function(e) { cancelQuestion($(this).attr('id')); $(this).remove(); }); $("#askedQuestions").append(questionItem); } }); $('#feedbackToggle').click(function() { if ( ! $(this).hasClass('disabled') ) { $('#feedbackSubmenu').toggle(); } }); $("#sendFeedback").click(function() { if ( ! $(this).hasClass('disabled') ) { sendFeedback($( "input:radio[name=rating]:checked" ).val(), $("#feedback").val()); feedback_response(this, "Sending..."); $("#feedback").val(''); } }); $("#editSlide").click(function() { editSlide(); closeMenu(); }); $('#clearAnnotations').click(function() { annotations.erase(); }); $('#closeMenu, #sidebarExit').click(function() { closeMenu(); }); function closeMenu() { $('#feedbackSidebar, #sidebarExit').hide(); toggleKeybinding('on'); } function feedback_response(elem, response) { var originalText = $(elem).text(); $(elem).text(response); window.setTimeout(function() { $(elem).parent().hide(); closeMenu(); $(elem).text(originalText); }, 1000); } } function updateQuestionIndicator(count) { if(count == 0) { $('#questionsIndicator').hide(); } else { $('#questionsIndicator').show(); $('#questionsIndicator').text(count); } } function updateMenuChevrons() { $(".navSection + ul:not(:visible)") .siblings('a') .children('i') .attr('class', 'fa fa-angle-down'); $(".navSection + ul:visible") .siblings('a') .children('i') .attr('class', 'fa fa-angle-up'); } function setupMenu() { var nav = $("