diff --git a/static/coffee/src/courseware.coffee b/static/coffee/src/courseware.coffee index ec1d067b6d..d7469cec04 100644 --- a/static/coffee/src/courseware.coffee +++ b/static/coffee/src/courseware.coffee @@ -3,6 +3,7 @@ class window.Courseware new CoursewareNavigation new Calculator new FeedbackForm + Logger.bind() @renderModules() @start: -> diff --git a/static/coffee/src/main.coffee b/static/coffee/src/main.coffee index 71f7e55d7d..5c658f3415 100644 --- a/static/coffee/src/main.coffee +++ b/static/coffee/src/main.coffee @@ -1,11 +1,22 @@ $ -> $.ajaxSetup headers : { 'X-CSRFToken': $.cookie 'csrftoken' } + dataType: 'json' window.onTouchBasedDevice = -> navigator.userAgent.match /iPhone|iPod|iPad/i $("a[rel*=leanModal]").leanModal() + $('#csrfmiddlewaretoken').attr 'value', $.cookie('csrftoken') if $('body').hasClass('courseware') Courseware.start() + + # Preserved for backward compatibility + window.submit_circuit = (circuit_id) -> + $("input.schematic").each (index, element) -> + element.schematic.update_value() + + schematic_value $("#schematic_#{circuit_id}").attr("value") + $.post "/save_circuit/#{circuit_id}", schematic: schematic_value, (data) -> + alert('Saved') if data.results == 'success' diff --git a/static/js/video_player.js b/static/js/video_player.js deleted file mode 100644 index 5b3b9377a9..0000000000 --- a/static/js/video_player.js +++ /dev/null @@ -1,499 +0,0 @@ -// Things to abstract out to another file - -// We do sync AJAX for just the page close event. -// TODO: This should _really_ not be a global. -var log_close_event = false; - -function log_close() { - var d=new Date(); - var t=d.getTime(); - //close_event_logged = "waiting"; - log_close_event = true; - log_event('page_close', {}); - log_close_event = false; - // Google Chrome will close without letting the event go through. - // This causes the page close to be delayed until we've hit the - // server. The code below fixes it, but breaks Firefox. - // TODO: Check what happens with no network. - /*while((close_event_logged != "done") && (d.getTime() < t+500)) { - console.log(close_event_logged); - }*/ -} - -window.onbeforeunload = log_close; - -function getCookie(name) { - var cookieValue = null; - if (document.cookie && document.cookie != '') { - var cookies = document.cookie.split(';'); - for (var i = 0; i < cookies.length; i++) { - var cookie = jQuery.trim(cookies[i]); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) == (name + '=')) { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; -} - -function postJSON(url, data, callback) { - $.ajax({type:'POST', - url: url, - dataType: 'json', - data: data, - success: callback, - headers : {'X-CSRFToken':getCookie('csrftoken')} - }); -} - -function postJSONAsync(url, data, callback) { - $.ajax({type:'POST', - url: url, - dataType: 'json', - data: data, - success: callback, - headers : {'X-CSRFToken':getCookie('csrftoken')}, - async:true - }); -} - -// For easy embedding of CSRF in forms -$(function() { - $('#csrfmiddlewaretoken').attr("value", getCookie('csrftoken')) -}); - -// For working with circuits in wiki: - -function submit_circuit(circuit_id) { - $("input.schematic").each(function(index,element){ element.schematic.update_value(); }); - postJSON('/save_circuit/'+circuit_id, - {'schematic': $('#schematic_'+circuit_id).attr("value")}, - function(data){ if (data.results=='success') alert("Saved");}); - return false; -} - -// Video player - -var load_id = 0; -var caption_id; -var video_speed = "1.0"; - -var updateytPlayerInterval; -var ajax_videoInterval; - -function change_video_speed(speed, youtube_id) { - new_position = ytplayer.getCurrentTime() * video_speed / speed; - video_speed = speed; - ytplayer.loadVideoById(youtube_id, new_position); - syncPlayButton(); - log_event("speed", {"new_speed":speed, "clip":youtube_id}); - - $.cookie("video_speed", speed, {'expires':3650, 'path':'/'}); -} - -function caption_at(index) { - if (captions==0) - return ""; - - text_array=captions.text - - if ((index>=text_array.length) || (index < 0)) - return ""; - return text_array[index]; -} - -function caption_time_at(index) { - if (captions==0) - return 0; - - time_array=captions.start; - - if (index < 0) - return 0; - if (index>=time_array.length) - return ytplayer.getDuration(); - - return time_array[index] / 1000.0 / video_speed; -} - -function caption_index(now) { - // Returns the index of the current caption, given a time - now = now * video_speed; - - if (captions==0) - return 0; - - time_array=captions.start - - // TODO: Bisection would be better, or something incremental - var i; - for(i=0;i(now*1000)) { - return i-1; - } - } - return i-1; -} - -function format_time(t) -{ - seconds = Math.floor(t); - minutes = Math.floor(seconds / 60); - hours = Math.floor(minutes / 60); - seconds = seconds % 60; - minutes = minutes % 60; - - if (hours) { - return hours+":"+((minutes < 10)?"0":"")+minutes+":"+((seconds < 10)?"0":"")+(seconds%60); - } else { - return minutes+":"+((seconds < 10)?"0":"")+(seconds%60); - } -} - -function update_captions(t) { - var i=caption_index(t); - $("#vidtime").html(format_time(ytplayer.getCurrentTime())+' / '+format_time(ytplayer.getDuration())); - var j; - for(j=1; j<9; j++) { - $("#std_n"+j).html(caption_at(i-j)); - $("#std_p"+j).html(caption_at(i+j)); - } - $("#std_0").html(caption_at(i)); -} - -function title_seek(i) { - // Seek video forwards or backwards by i subtitles - current=caption_index(getCurrentTime()); - new_time=caption_time_at(current+i); - - ytplayer.seekTo(new_time, true); -} - -function updateHTML(elmId, value) { - document.getElementById(elmId).innerHTML = value; -} - -function setytplayerState(newState) { - // updateHTML("playerstate", newState); -} - -// Updates server with location in video so we can resume from the same place -// IMPORTANT TODO: Load test -// POSSIBLE FIX: Move to unload() event and similar -var ajax_video=function(){}; -var ytplayer; - -function onYouTubePlayerReady(playerId) { - ytplayer = document.getElementById("myytplayer"); - updateytplayerInfoInterval = setInterval(updateytplayerInfo, 500); - ajax_videoInterval = setInterval(ajax_video,5000); - ytplayer.addEventListener("onStateChange", "onytplayerStateChange"); - ytplayer.addEventListener("onError", "onPlayerError"); - if((typeof load_id != "undefined") && (load_id != 0)) { - var id=load_id; - loadNewVideo(caption_id, id, 0); - } -} - -/* HTML5 YouTube iFrame API Specific */ -function onYouTubePlayerAPIReady() { - ytplayer = new YT.Player('html5_player', { - events: { - 'onReady': onPlayerReady, - 'onStateChange': onPlayerStateChange - } - }); - updateytplayerInfoInterval = setInterval(updateHTML5ytplayerInfo, 200); - //ajax_videoInterval = setInterval(ajax_video, 5000); -} - -// Need this function to call the API ready callback when we switch to a tab with AJAX that has a video -// That callback is not being fired when we switch tabs. -function loadHTML5Video() { - if (!ytplayer && switched_tab){ - onYouTubePlayerAPIReady(); - } -} - -function isiOSDevice(){ - var iphone = "iphone"; - var ipod = "ipod"; - var ipad = "ipad"; - var uagent = navigator.userAgent.toLowerCase(); - - //alert(uagent); - if (uagent.search(ipad) > -1 || uagent.search(iphone) > -1 - || uagent.search(ipod) > -1) { - return true; - } - return false; -} - -function onPlayerReady(event) { - //do not want to autoplay on iOS devices since its not enabled - //and leads to confusing behavior for the user - if (!isiOSDevice()) { - event.target.playVideo(); - } -} - -function onPlayerStateChange(event) { - if (event.data == YT.PlayerState.PLAYING) { - } -} - -/* End HTML5 Specific */ - - -var switched_tab = false; // switch to true when we destroy so we know to call onYouTubePlayerAPIReady() -// clear pings to video status when we switch to a different sequence tab with ajax -function videoDestroy(id) { -// postJSON('/modx/video/'+id+'/goto_position', -// {'position' : ytplayer.getCurrentTime()}); - - load_id = 0; - clearInterval(updateytplayerInfoInterval); - clearInterval(ajax_videoInterval); - ytplayer = false; - switched_tab = true; -} - -function log_event(e, d) { - data = { - "event_type" : e, - "event" : JSON.stringify(d), - "page" : document.URL - } - $.ajax({type:'GET', - url: '/event', - dataType: 'json', - data: data, - async: !log_close_event, // HACK: See comment on log_close_event - success: function(){}, - headers : {'X-CSRFToken':getCookie('csrftoken')} - }); - - /*, // Commenting out Chrome bug fix, since it breaks FF - function(data) { - console.log("closing"); - if (close_event_logged == "waiting") { - close_event_logged = "done"; - console.log("closed"); - } - });*/ -} - -function seek_slide(type,oe,value) { - //log_event('video', [type, value]); - if(type=='slide') { - // HACK/TODO: Youtube recommends this be false for slide and true for stop. - // Works better on my system with true/true. - // We should test both configurations on low/high bandwidth - // connections, and different browsers - // One issue is that we query the Youtube window every 250ms for position/state - // With false, it returns the old one (ignoring the new seek), and moves the - // scroll bar to the wrong spot. - ytplayer.seekTo(value, true); - } else if (type=='stop') { - ytplayer.seekTo(value, true); - log_event('video', [type, value]); - } - - update_captions(value); -} - -function get_state() { - if (ytplayer) - return [ytplayer.getPlayerState(), - ytplayer.getVideoUrl(), - ytplayer.getDuration(), ytplayer.getCurrentTime(), - ytplayer.getVideoBytesLoaded(), ytplayer.getVideoBytesTotal(), - ytplayer.getVideoStartBytes(), - ytplayer.getVolume(),ytplayer.isMuted(), - ytplayer.getPlaybackQuality(), - ytplayer.getAvailableQualityLevels()]; - return []; -} - -function onytplayerStateChange(newState) { - setytplayerState(newState); - log_event('video', ['State Change',newState, get_state()]); -} - -function onPlayerError(errorCode) { - // alert("An error occured: " + errorCode); - log_event("player_error", {"error":errorCode}); -} - -// Currently duplicated to check for if video control changed by clicking the video for HTML5 -// Hacky b/c of lack of control over YT player -function updateHTML5ytplayerInfo() { - var player_state = getPlayerState(); - if(player_state != 3) { - $("#slider").slider("option","max",ytplayer.getDuration()); - $("#slider").slider("option","value",ytplayer.getCurrentTime()); - } - if (player_state == 1){ - update_captions(getCurrentTime()); - } - if (player_state == 1 && $("#video_control").hasClass("play")) - $("#video_control").removeClass().addClass("pause"); - else if (player_state == 2 && $("#video_control").hasClass("pause")) - $("#video_control").removeClass().addClass("play"); -} - -function updateytplayerInfo() { - var player_state = getPlayerState(); - if(player_state != 3) { - $("#slider").slider("option","max",ytplayer.getDuration()); - $("#slider").slider("option","value",ytplayer.getCurrentTime()); - } - if (player_state == 1){ - update_captions(getCurrentTime()); - handle = $('.ui-slider-handle', $('#slider')); - handle.qtip('option', 'content.text', '' + format_time(getCurrentTime())); - } - // updateHTML("videoduration", getDuration()); - // updateHTML("videotime", getCurrentTime()); - // updateHTML("startbytes", getStartBytes()); - // updateHTML("volume", getVolume()); -} - -// functions for the api calls -function loadNewVideo(cap_id, id, startSeconds) { - captions={"start":[0],"end":[0],"text":["Attempting to load captions..."]}; - $.getJSON("/static/subs/"+cap_id+".srt.sjson", function(data) { - captions=data; - }); - caption_id = cap_id; - load_id = id; - //if ((typeof ytplayer != "undefined") && (ytplayer.type=="application/x-shockwave-flash")) { - // Try it every time. If we fail, we want the error message for now. - // TODO: Add try/catch - try { - ytplayer.loadVideoById(id, parseInt(startSeconds)); - load_id=0; - } - catch(e) { - window['console'].log(JSON.stringify(e)); - } - log_event("load_video", {"id":id,"start":startSeconds}); - //$("#slider").slider("option","value",startSeconds); - //seekTo(startSeconds); -} - -function syncPlayButton(){ - var state = getPlayerState(); - if (state == 1 || state == 3) { - $("#video_control").removeClass("play").addClass("pause"); - } else if (state == 2 || state == -1 || state == 0){ - $("#video_control").removeClass("pause").addClass("play"); - } -} - -function cueNewVideo(id, startSeconds) { - if (ytplayer) { - ytplayer.cueVideoById(id, startSeconds); - } -} - -function play() { - if (ytplayer) { - ytplayer.playVideo(); - } - log_event("play_video", {"id":getCurrentTime(), "code":getEmbedCode()}); -} - -function pause() { - if (ytplayer) { - ytplayer.pauseVideo(); - } - log_event("pause_video", {"id":getCurrentTime(), "code":getEmbedCode()}); -} - -function stop() { - if (ytplayer) { - ytplayer.stopVideo(); - } - log_event("stop_video", {"id":getCurrentTime(), "code":getEmbedCode()}); -} - -function getPlayerState() { - if (ytplayer) { - return ytplayer.getPlayerState(); - } -} - -function seekTo(seconds) { - if (ytplayer) { - ytplayer.seekTo(seconds, true); - } -} - -function getBytesTotal() { - if (ytplayer) { - return ytplayer.getVideoBytesTotal(); - } -} - -function getCurrentTime() { - if (ytplayer) { - return ytplayer.getCurrentTime(); - } -} - -function getDuration() { - if (ytplayer) { - return ytplayer.getDuration(); - } -} - -function getStartBytes() { - if (ytplayer) { - return ytplayer.getVideoStartBytes(); - } -} - -function mute() { - if (ytplayer) { - ytplayer.mute(); - } -} - -function unMute() { - if (ytplayer) { - ytplayer.unMute(); - } -} - -function getEmbedCode() { - if(ytplayer) { - ytplayer.getVideoEmbedCode(); - } -} - -function getVideoUrl() { - if(ytplayer) { - ytplayer.getVideoUrl(); - } -} - -function setVolume(newVolume) { - if (ytplayer) { - ytplayer.setVolume(newVolume); - } -} - -function getVolume() { - if (ytplayer) { - return ytplayer.getVolume(); - } -} - -function clearVideo() { - if (ytplayer) { - ytplayer.clearVideo(); - } -} diff --git a/templates/coffee/src/logger.coffee b/templates/coffee/src/logger.coffee new file mode 100644 index 0000000000..729cb54982 --- /dev/null +++ b/templates/coffee/src/logger.coffee @@ -0,0 +1,20 @@ +class @Logger + @log: (event_type, data) -> + $.getJSON '/event', + event_type: event_type + event: JSON.stringify(data) + page: window.location.href + + @bind: -> + window.onunload = -> + $.ajax + url: '/event' + data: + event_type: 'page_close' + event: '' + page: window.location.href + async: false + return true + +# Keeping this for conpatibility issue only. +@log_event = Logger.log diff --git a/templates/video_init.js b/templates/video_init.js deleted file mode 100644 index bcbaecd249..0000000000 --- a/templates/video_init.js +++ /dev/null @@ -1,156 +0,0 @@ -var streams=${ streams } -var params = { allowScriptAccess: "always", bgcolor: "#cccccc", wmode: "transparent", allowFullScreen: "true" }; -var atts = { id: "myytplayer" }; - -// If the user doesn't have flash, use the HTML5 Video instead. YouTube's -// iFrame API which supports HTML5 is still developmental so it is not default -if (swfobject.hasFlashPlayerVersion("10.1")){ - swfobject.embedSWF(document.location.protocol + "//www.youtube.com/apiplayer?enablejsapi=1&playerapiid=ytplayer?wmode=transparent", - "ytapiplayer", "640", "385", "8", null, null, params, atts); -} else { - - //end of this URL may need &origin=http://..... once pushed to production to prevent XSS - $("#html5_player").attr("src", document.location.protocol + "//www.youtube.com/embed/" + streams["1.0"] + "?enablejsapi=1&controls=0"); - $("#html5_player").show(); - - var tag = document.createElement('script'); - tag.src = document.location.protocol + "//www.youtube.com/player_api"; - var firstScriptTag = document.getElementsByTagName('script')[0]; - firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); - // Make sure the callback is called once API ready, YT seems to be buggy - loadHTML5Video(); -} - -var captions=0; - -/* Cache a reference to our slider element */ -var slider = $('#slider') - -.slider({ - range: "min", - slide: function(event,ui) { - var slider_time = format_time(ui.value) - - seek_slide('slide',event.originalEvent,ui.value); - handle.qtip('option', 'content.text', '' + slider_time); - }, - stop:function(event,ui){seek_slide('stop',event.originalEvent,ui.value);} -}), - -/* Grab and cache the newly created slider handle */ -handle = $('.ui-slider-handle', slider); - -/* - * Selector needs changing here to match your elements. - * - * Notice the second argument to the $() constructor, which tells - * jQuery to use that as the top-level element to seareh down from. - */ - handle.qtip({ - content: '' + slider.slider('option', 'value'), // Use the current value of the slider - position: { - my: 'bottom center', - at: 'top center', - container: handle // Stick it inside the handle element so it keeps the position synched up - }, - hide: { - delay: 700 // Give it a longer delay so it doesn't hide frequently as we move the handle - }, - style: { - classes: 'ui-tooltip-slider', - widget: true // Make it Themeroller compatible - } - }); - -function good() { - window['console'].log(ytplayer.getCurrentTime()); -} - -ajax_video=good; - -// load the same video speed your last video was at in a sequence -// if the last speed played on video doesn't exist on another video just use 1.0 as default - -function add_speed(key, stream) { - var id = 'speed_' + stream; - - if (key == video_speed) { - $("#video_speeds").append('
  • '+key+'x
  • '); - $("p.active").text(key + 'x'); - } else { - $("#video_speeds").append('
  • '+key+'x
  • '); - } - - $("#"+id).click(function(){ - change_video_speed(key, stream); - $(this).siblings().removeClass("active"); - $(this).addClass("active"); - var active = $(this).text(); - $("p.active").text(active); - }); - -} - -var l=[] -for (var key in streams) { - l.push(key); -} - -function sort_by_value(a,b) { - var x=parseFloat(a); - var y=parseFloat(b); - var r=((x < y) ? -1 : ((x > y) ? 1 : 0)); - return r; -} - -l.sort(sort_by_value); - -$(document).ready(function() { - video_speed = $.cookie("video_speed"); - - //ugly hack to account for different formats in vid speed in the XML (.75 vs 0.75, 1.5 vs 1.50); - if (( !video_speed ) || ( !streams[video_speed] && !streams[video_speed + "0"]) && !streams[video_speed.slice(0,-1)] && !streams[video_speed.slice(1)] && !streams["0" + video_speed]) { - video_speed = "1.0"; - } - - if (streams[video_speed + "0"]){ - video_speed = video_speed + "0"; - } else if (streams[video_speed.slice(0, -1)]){ - video_speed = video_speed.slice(0, -1); - } else if (streams[video_speed.slice(1)]) { - video_speed = video_speed.slice(1); - } else if (streams["0" + video_speed]) { - video_speed = "0" + video_speed; - } - - loadNewVideo(streams["1.0"], streams[video_speed], ${ position }); - - for(var i=0; i