diff --git a/record-and-playback/slides/playback/slides/lib/popcorn-complete.min.js b/record-and-playback/slides/playback/slides/lib/popcorn-complete.min.js old mode 100644 new mode 100755 diff --git a/record-and-playback/slides/playback/slides/lib/popcorn.altimage.js b/record-and-playback/slides/playback/slides/lib/popcorn.altimage.js new file mode 100755 index 0000000000000000000000000000000000000000..ed2e7acb0ca961463f2110556c0fa1c022d91e8a --- /dev/null +++ b/record-and-playback/slides/playback/slides/lib/popcorn.altimage.js @@ -0,0 +1,253 @@ +// PLUGIN: ALTIMAGE + +(function ( Popcorn ) { + +/** + * Images popcorn plug-in + * Shows an image element + * Options parameter will need a start, end, href, target and src. + * Start is the time that you want this plug-in to execute + * End is the time that you want this plug-in to stop executing + * href is the url of the destination of a anchor - optional + * Target is the id of the document element that the iframe needs to be attached to, + * this target element must exist on the DOM + * Src is the url of the image that you want to display + * text is the overlayed text on the image - optional + * + * @param {Object} options + * + * Example: + var p = Popcorn('#video') + .altimage({ + start: 5, // seconds + end: 15, // seconds + href: 'http://www.drumbeat.org/', + src: 'http://www.drumbeat.org/sites/default/files/domain-2/drumbeat_logo.png', + text: 'DRUMBEAT', + target: 'imagediv' + } ) + * + */ + + var VIDEO_OVERLAY_Z = 2000, + CHECK_INTERVAL_DURATION = 10; + + function trackMediaElement( mediaElement ) { + var checkInterval = -1, + container = document.createElement( "div" ), + videoZ = getComputedStyle( mediaElement ).zIndex; + + container.setAttribute( "data-popcorn-helper-container", true ); + + container.style.position = "absolute"; + + if ( !isNaN( videoZ ) ) { + container.style.zIndex = videoZ + 1; + } + else { + container.style.zIndex = VIDEO_OVERLAY_Z; + } + + document.body.appendChild( container ); + + function check() { + var mediaRect = mediaElement.getBoundingClientRect(), + containerRect = container.getBoundingClientRect(); + + if ( containerRect.left !== mediaRect.left ) { + container.style.left = mediaRect.left + "px"; + } + if ( containerRect.top !== mediaRect.top ) { + container.style.top = mediaRect.top + "px"; + } + } + + return { + element: container, + start: function() { + checkInterval = setInterval( check, CHECK_INTERVAL_DURATION ); + }, + stop: function() { + clearInterval( checkInterval ); + checkInterval = -1; + }, + destroy: function() { + document.body.removeChild( container ); + if ( checkInterval !== -1 ) { + clearInterval( checkInterval ); + } + } + }; + } + + Popcorn.plugin( "altimage", { + manifest: { + about: { + name: "Popcorn image Plugin", + version: "0.1", + author: "Scott Downe, Chad Pilkey", + }, + options: { + start: { + elem: "input", + type: "number", + label: "Start" + }, + end: { + elem: "input", + type: "number", + label: "End" + }, + src: { + elem: "input", + type: "url", + label: "Image URL", + "default": "http://mozillapopcorn.org/wp-content/themes/popcorn/images/for_developers.png" + }, + href: { + elem: "input", + type: "url", + label: "Link", + "default": "http://mozillapopcorn.org/wp-content/themes/popcorn/images/for_developers.png", + optional: true + }, + target: "image-container", + text: { + elem: "input", + type: "text", + label: "Caption", + "default": "Popcorn.js", + optional: true + }, + alt: { + elem: "input", + type: "text", + label: "Alt-Text", + optional: true + } + } + }, + _setup: function( options ) { + var img = document.createElement( "img" ), + target = document.getElementById( options.target ); + + options.anchor = document.createElement( "a" ); + options.anchor.style.position = "relative"; + options.anchor.style.textDecoration = "none"; + options.anchor.style.display = "none"; + options.anchor.setAttribute('aria-hidden', true); + + // add the widget's div to the target div. + // if target is <video> or <audio>, create a container and routinely + // update its size/position to be that of the media + if ( target ) { + if ( [ "VIDEO", "AUDIO" ].indexOf( target.nodeName ) > -1 ) { + options.trackedContainer = trackMediaElement( target ); + options.trackedContainer.element.appendChild( options.anchor ); + } + else { + target && target.appendChild( options.anchor ); + } + } + + img.addEventListener( "load", function() { + + // borders look really bad, if someone wants it they can put it on their div target + img.style.borderStyle = "none"; + + options.anchor.href = options.href || options.src || "#"; + options.anchor.target = "_blank"; + + var fontHeight, divText; + + // these cause a slide to get distorted if slide isn't near square + img.style.height = target.style.height; + //img.style.width = target.style.width; + img.style.display = "block"; + img.style.marginLeft = "auto"; + img.style.marginRight = "auto"; + + options.anchor.appendChild( img ); + + // If display text was provided, display it: + if ( options.text ) { + fontHeight = ( img.height / 12 ) + "px"; + divText = document.createElement( "div" ); + + Popcorn.extend( divText.style, { + color: "black", + fontSize: fontHeight, + fontWeight: "bold", + position: "relative", + textAlign: "center", + width: img.style.width || img.width + "px", + zIndex: "10" + }); + + divText.innerHTML = options.text || ""; + + divText.style.top = ( ( img.style.height.replace( "px", "" ) || img.height ) / 2 ) - ( divText.offsetHeight / 2 ) + "px"; + options.anchor.insertBefore( divText, img ); + } + }, false ); + + img.src = options.src; + + // currently the alt text that is passed is only a URI for the text file not the contents + // so the contents have to be retrieved + if (options.alt && window.XMLHttpRequest) { + var txtFile = new XMLHttpRequest(); + txtFile.open("GET", options.alt, true); + txtFile.onreadystatechange = function() { + if (txtFile.readyState === 4) { + if (txtFile.status === 200) { + img.setAttribute("alt", txtFile.responseText); + } + } + } + txtFile.send(null); + } + + options.toString = function() { + var string = options.src || options._natives.manifest.options.src[ "default" ], + match = string.replace( /.*\//g, "" ); + return match.length ? match : string; + }; + }, + + /** + * @member image + * The start function will be executed when the currentTime + * of the video reaches the start time provided by the + * options variable + */ + start: function( event, options ) { + options.anchor.style.display = "inline"; + //options.anchor.setAttribute('aria-hidden', false); + if ( options.trackedContainer ) { + options.trackedContainer.start(); + } + }, + /** + * @member image + * The end function will be executed when the currentTime + * of the video reaches the end time provided by the + * options variable + */ + end: function( event, options ) { + options.anchor.style.display = "none"; + //options.anchor.setAttribute('aria-hidden', true); + if ( options.trackedContainer ) { + options.trackedContainer.stop(); + } + }, + _teardown: function( options ) { + if ( options.trackedContainer ) { + options.trackedContainer.destroy(); + } + else if ( options.anchor.parentNode ) { + options.anchor.parentNode.removeChild( options.anchor ); + } + } + }); +})( Popcorn ); diff --git a/record-and-playback/slides/playback/slides/lib/popcorn.chat-timeline.js b/record-and-playback/slides/playback/slides/lib/popcorn.chattimeline.js similarity index 74% rename from record-and-playback/slides/playback/slides/lib/popcorn.chat-timeline.js rename to record-and-playback/slides/playback/slides/lib/popcorn.chattimeline.js index b050118ca363b72a4ee1446c826f7b6311c176d9..549ae3633008e3c7fc5dfcc735c999cf2e0fd7ec 100755 --- a/record-and-playback/slides/playback/slides/lib/popcorn.chat-timeline.js +++ b/record-and-playback/slides/playback/slides/lib/popcorn.chattimeline.js @@ -26,23 +26,14 @@ var i = 1; - Popcorn.plugin( "chat-timeline" , function( options ) { + Popcorn.plugin( "chattimeline" , function( options ) { var target = document.getElementById( options.target ), contentDiv = document.createElement( "div" ), - container, goingUp = true; - if ( target && !target.firstChild ) { - target.appendChild ( container = document.createElement( "div" ) ); - container.style.width = "inherit"; - container.style.height = "inherit"; - container.style.overflow = "auto"; - } else { - container = target.firstChild; - } - contentDiv.style.display = "none"; + contentDiv.setAttribute('aria-hidden', true); contentDiv.id = "timelineDiv" + i; // Default to up if options.direction is non-existant or not up or down @@ -52,15 +43,15 @@ goingUp = false; } - if ( target && container ) { + if ( target ) { // if this isnt the first div added to the target div if( goingUp ){ // insert the current div before the previous div inserted - container.insertBefore( contentDiv, container.firstChild ); + target.insertBefore( contentDiv, target.firstChild ); } else { - container.appendChild( contentDiv ); + target.appendChild( contentDiv ); } } @@ -70,25 +61,26 @@ // Default to empty if not used //options.innerHTML = options.innerHTML || ""; - contentDiv.innerHTML = "<span><strong>" + options.name + ": </strong>" + options.message + "</span>"; + contentDiv.innerHTML = "<p><strong>" + options.name + ":</strong>" + options.message + "</p>"; return { start: function( event, options ) { contentDiv.style.display = "block"; - + contentDiv.setAttribute('aria-hidden', false); if( options.direction === "down" ) { - container.scrollTop = container.scrollHeight; + target.scrollTop = target.scrollHeight; } }, end: function( event, options ) { contentDiv.style.display = "none"; + contentDiv.setAttribute('aria-hidden', true); }, _teardown: function( options ) { - ( container && contentDiv ) && container.removeChild( contentDiv ) && !container.firstChild && target.removeChild( container ); + ( target && contentDiv ) && target.removeChild( contentDiv ) && !target.firstChild } }; }, diff --git a/record-and-playback/slides/playback/slides/playback.html b/record-and-playback/slides/playback/slides/playback.html index 848d37b50ecd66b7357b658bacd63dea08f2caa3..f1b5a14a8fbcdff83f8ceec894351d562b6bb6f4 100755 --- a/record-and-playback/slides/playback/slides/playback.html +++ b/record-and-playback/slides/playback/slides/playback.html @@ -52,15 +52,16 @@ }, false); </script> <script src="lib/popcorn-custom.min.js"></script> - <script src="lib/popcorn.chat-timeline.js"></script> + <script src="lib/popcorn.chattimeline.js"></script> + <script src="lib/popcorn.altimage.js"></script> </head> <body> <div id="playbackArea"> - <div style="width:800px; height:650px;" id="slide"></div> + <div style="width:800px; height:650px;" id="slide" aria-live="polite" role="region" aria-label="Presentation slides"></div> <audio id="audioRecording" controls > You must use an HTML5 capable browser to view this page. This playback requires modern browser, please use FF, Safari, Chrome</audio> <div class="chatcontainer"> - <div id="chat"></div> + <div id="chat" aria-live="polite" role="region" aria-label="Chat messages"></div> </div> </div> <p id="footer">Recorded with <a href="http://bigbluebutton.org/">BigBlueButton</a>.</p> diff --git a/record-and-playback/slides/scripts/process/slides.rb b/record-and-playback/slides/scripts/process/slides.rb index 0ff0c80fe507a48e9561af8dcfa0fae0910fbec4..b8753320ed7e659c45c916509c8439baf5ed01c4 100755 --- a/record-and-playback/slides/scripts/process/slides.rb +++ b/record-and-playback/slides/scripts/process/slides.rb @@ -47,13 +47,15 @@ if not FileTest.directory?(target_dir) target_pres_dir = "#{processed_pres_dir}/#{pres}" FileUtils.mkdir_p target_pres_dir - + FileUtils.mkdir_p "#{target_pres_dir}/textfiles" + images=Dir.glob("#{pres_dir}/#{pres}.{jpg,png,gif}") if images.empty? 1.upto(num_pages) do |page| pdf_page = "#{pres_dir}/slide-#{page}.pdf" BigBlueButton::Presentation.extract_page_from_pdf(page, pres_pdf, pdf_page) BigBlueButton::Presentation.convert_pdf_to_png(pdf_page, "#{target_pres_dir}/slide-#{page}.png") + FileUtils.cp("#{pres_dir}/textfiles/slide-#{page}.txt", "#{target_pres_dir}/textfiles") end else ext = File.extname("#{images[0]}") diff --git a/record-and-playback/slides/scripts/publish/slides.rb b/record-and-playback/slides/scripts/publish/slides.rb index f92d859bd07f82df556a586cf281223f22f88a54..1b7608a4685f44baafde9724be047f6776f5885d 100755 --- a/record-and-playback/slides/scripts/publish/slides.rb +++ b/record-and-playback/slides/scripts/publish/slides.rb @@ -93,7 +93,7 @@ if (playback == "slides") slides_doc = Nokogiri::XML::Builder.new do |xml| xml.popcorn { xml.timeline { - xml.image(:in => 0, :out => first_slide_start, :src => "logo.png", :target => "slide", :width => 200, :width => 200 ) + xml.altimage(:in => 0, :out => first_slide_start, :src => "logo.png", :target => "slide", :width => 200, :width => 200 ) slides_events.each do |node| eventname = node['eventname'] if eventname == "SharePresentationEvent" @@ -103,13 +103,18 @@ if (playback == "slides") slide_start = (slide_timestamp.to_i - meeting_start.to_i) / 1000 slide_number = node.xpath(".//slide")[0].text() slide_src = "#{presentation_url}/#{presentation_name}/slide-#{slide_number.to_i + 1}.png" + slide_alt = "#{presentation_url}/#{presentation_name}/textfiles/slide-#{slide_number.to_i + 1}.txt" + #The txt files don't seem to appear in time for this script to attach the contents to slide_alt + #if FileTest.exist?("#{presentation_url}/#{presentation_name}/textfiles/slide-#{slide_number.to_i + 1}.txt") + # slide_alt = File.open("#{presentation_url}/#{presentation_name}/textfiles/slide-#{slide_number.to_i + 1}.txt", 'r') { |f| f.read } + #end current_index = slides_events.index(node) if( current_index + 1 < slides_events.length) slide_end = ( slides_events[current_index + 1]['timestamp'].to_i - meeting_start.to_i ) / 1000 else slide_end = ( meeting_end.to_i - meeting_start.to_i ) / 1000 end - xml.image(:in => slide_start, :out => slide_end, :src => slide_src, :target => "slide", :width => 200, :width => 200 ) + xml.altimage(:in => slide_start, :out => slide_end, :src => slide_src, :alt => slide_alt, :target => "slide", :width => 200, :width => 200 ) puts "#{slide_src} : #{slide_start} -> #{slide_end}" end end @@ -119,7 +124,7 @@ if (playback == "slides") chat_sender = node.xpath(".//sender")[0].text() chat_message = node.xpath(".//message")[0].text() chat_start = (chat_timestamp.to_i - meeting_start.to_i) / 1000 - xml.timeline(:in => chat_start, :direction => "down", :innerHTML => "<span><strong>#{chat_sender}:</strong> #{chat_message}</span>", :target => "chat" ) + xml.chattimeline(:in => chat_start, :direction => "down", :name => chat_sender, :message => chat_message, :target => "chat" ) end } end