From e98aeda29ee420cdcc5e989e0e901bc68c00879c Mon Sep 17 00:00:00 2001 From: Brian Jacobel Date: Thu, 15 Dec 2016 14:15:36 -0500 Subject: [PATCH 1/5] Move sequence nav display.coffee to new js location, and remove dead code --- .eslintignore | 1 - .../sequence/{display.coffee => display.js} | 0 .../js/src/sequence/display/.gitignore | 1 - .../src/sequence/display/jquery.sequence.js | 74 ------------------- common/lib/xmodule/xmodule/seq_module.py | 3 +- lms/templates/seq_module.html | 7 -- 6 files changed, 1 insertion(+), 85 deletions(-) rename common/lib/xmodule/xmodule/js/src/sequence/{display.coffee => display.js} (100%) delete mode 100644 common/lib/xmodule/xmodule/js/src/sequence/display/.gitignore delete mode 100644 common/lib/xmodule/xmodule/js/src/sequence/display/jquery.sequence.js diff --git a/.eslintignore b/.eslintignore index 8ee8cc947c..7bec88e2cb 100644 --- a/.eslintignore +++ b/.eslintignore @@ -52,7 +52,6 @@ common/lib/xmodule/xmodule/js/src/html/edit.js common/lib/xmodule/xmodule/js/src/raw/edit/json.js common/lib/xmodule/xmodule/js/src/raw/edit/metadata-only.js common/lib/xmodule/xmodule/js/src/raw/edit/xml.js -common/lib/xmodule/xmodule/js/src/sequence/display.js common/lib/xmodule/xmodule/js/src/sequence/edit.js common/lib/xmodule/xmodule/js/src/tabs/tabs-aggregator.js common/lib/xmodule/xmodule/js/src/vertical/edit.js diff --git a/common/lib/xmodule/xmodule/js/src/sequence/display.coffee b/common/lib/xmodule/xmodule/js/src/sequence/display.js similarity index 100% rename from common/lib/xmodule/xmodule/js/src/sequence/display.coffee rename to common/lib/xmodule/xmodule/js/src/sequence/display.js diff --git a/common/lib/xmodule/xmodule/js/src/sequence/display/.gitignore b/common/lib/xmodule/xmodule/js/src/sequence/display/.gitignore deleted file mode 100644 index d4aa116a26..0000000000 --- a/common/lib/xmodule/xmodule/js/src/sequence/display/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!*.js diff --git a/common/lib/xmodule/xmodule/js/src/sequence/display/jquery.sequence.js b/common/lib/xmodule/xmodule/js/src/sequence/display/jquery.sequence.js deleted file mode 100644 index 91ca3fbe53..0000000000 --- a/common/lib/xmodule/xmodule/js/src/sequence/display/jquery.sequence.js +++ /dev/null @@ -1,74 +0,0 @@ -var SequenceNav = function($element) { - var _this = this; - var $element = $element; - var $wrapper = $element.find('.sequence-list-wrapper'); - var $list = $element.find('#sequence-list'); - var $arrows = $element.find('.sequence-nav-button'); - var maxScroll = $list.width() - $wrapper.width(); - var $body = $('body'); - var listOrigin; - var mouseOrigin; - - var startDrag = function(e) { - updateWidths(); - mouseOrigin = e.pageX; - listOrigin = $list.position().left; - $body.css('-webkit-user-select', 'none'); - $body.bind('mousemove', moveDrag); - $body.bind('mouseup', stopDrag); - }; - - var moveDrag = function(e) { - var offset = e.pageX - mouseOrigin; - var targetLeft = clamp(listOrigin + offset, -maxScroll, 0); - - updateHorizontalPosition(targetLeft); - }; - - var stopDrag = function(e) { - $body.css('-webkit-user-select', 'auto'); - $body.unbind('mousemove', moveDrag); - $body.unbind('mouseup', stopDrag); - }; - - var clamp = function(val, min, max) { - if(val > max) return max; - if(val < min) return min; - return val; - }; - - var updateWidths = function(e) { - maxScroll = $list.width() - $wrapper.width(); - var targetLeft = clamp($list.position().left, -maxScroll, 0); - updateHorizontalPosition(targetLeft); - }; - - var updateHorizontalPosition = function(left) { - $list.css({ - 'left': left + 'px' - }); - }; - - var checkPosition = function(e) { - var $active = $element.find('.active'); - if(!$active[0]) { - return; - } - if($active.position().left + $active.width() > $wrapper.width() - $list.position().left) { - $list.animate({ - 'left': (-$active.position().left + $wrapper.width() - $active.width() - 10) + 'px' - }, {}); - } else if($active.position().left < -$list.position().left) { - $list.animate({ - 'left': (-$active.position().left + 10) + 'px' - }, {}); - } - }; - - $wrapper.bind('mousedown', startDrag); - $arrows.bind('click', checkPosition); - $(window).bind('resize', updateWidths); - setTimeout(function() { - checkPosition(); - }, 200); -}; diff --git a/common/lib/xmodule/xmodule/seq_module.py b/common/lib/xmodule/xmodule/seq_module.py index 18ab86e525..ca588eee33 100644 --- a/common/lib/xmodule/xmodule/seq_module.py +++ b/common/lib/xmodule/xmodule/seq_module.py @@ -161,8 +161,7 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule): Layout module which lays out content in a temporal sequence """ js = { - 'coffee': [resource_string(__name__, 'js/src/sequence/display.coffee')], - 'js': [resource_string(__name__, 'js/src/sequence/display/jquery.sequence.js')], + 'js': [resource_string(__name__, 'js/src/sequence/display.js')], } css = { 'scss': [resource_string(__name__, 'css/sequence/display.scss')], diff --git a/lms/templates/seq_module.html b/lms/templates/seq_module.html index 47c6aac7d6..7ac6d7274a 100644 --- a/lms/templates/seq_module.html +++ b/lms/templates/seq_module.html @@ -64,10 +64,3 @@ - - From be18f9e93bddcc8430ad11bb7ea2f27f45c2fb38 Mon Sep 17 00:00:00 2001 From: Brian Jacobel Date: Thu, 15 Dec 2016 16:04:20 -0500 Subject: [PATCH 2/5] Do a little cleanup of this coffee file before conversion --- .../xmodule/js/src/sequence/display.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/common/lib/xmodule/xmodule/js/src/sequence/display.js b/common/lib/xmodule/xmodule/js/src/sequence/display.js index 0dd87c706c..5cf9653c05 100644 --- a/common/lib/xmodule/xmodule/js/src/sequence/display.js +++ b/common/lib/xmodule/xmodule/js/src/sequence/display.js @@ -125,22 +125,22 @@ class @Sequence is_first_tab = @position == 1 previous_button_class = '.sequence-nav-button.button-previous' @updateButtonState( - previous_button_class, # bound element - @selectPrevious, # action - 'Previous', # label prefix - is_first_tab, # is boundary? - @prevUrl # boundary_url + previous_button_class, + @selectPrevious, + 'Previous', + is_first_tab, + @prevUrl ) # next button is_last_tab = @position >= @contents.length # use inequality in case contents.length is 0 and position is 1. next_button_class = '.sequence-nav-button.button-next' @updateButtonState( - next_button_class, # bound element - @selectNext, # action - 'Next', # label prefix - is_last_tab, # is boundary? - @nextUrl # boundary_url + next_button_class, + @selectNext, + 'Next', + is_last_tab, + @nextUrl ) render: (new_position) -> @@ -252,7 +252,7 @@ class @Sequence window.location.href = @prevUrl else # If the bottom nav is used, scroll to the top of the page on change. - if is_bottom_nav + if is_bottom_nav $.scrollTo 0, 150 offset = next: 1 From 20290d2b779f6259d4b47d9b44f3b95d522f70b9 Mon Sep 17 00:00:00 2001 From: Brian Jacobel Date: Thu, 15 Dec 2016 16:16:03 -0500 Subject: [PATCH 3/5] Convert display.js to javascript, preserving comments --- .../xmodule/js/src/sequence/display.js | 651 +++++++++++------- 1 file changed, 413 insertions(+), 238 deletions(-) diff --git a/common/lib/xmodule/xmodule/js/src/sequence/display.js b/common/lib/xmodule/xmodule/js/src/sequence/display.js index 5cf9653c05..899c7bcc20 100644 --- a/common/lib/xmodule/xmodule/js/src/sequence/display.js +++ b/common/lib/xmodule/xmodule/js/src/sequence/display.js @@ -1,288 +1,463 @@ -class @Sequence - constructor: (element) -> - @updatedProblems = {} - @requestToken = $(element).data('request-token') - @el = $(element).find('.sequence') - @path = $('.path') - @contents = @$('.seq_contents') - @content_container = @$('#seq_content') - @sr_container = @$('.sr-is-focusable') - @num_contents = @contents.length - @id = @el.data('id') - @ajaxUrl = @el.data('ajax-url') - @nextUrl = @el.data('next-url') - @prevUrl = @el.data('prev-url') - @base_page_title = " | " + document.title - @initProgress() - @bind() - @render parseInt(@el.data('position')) +// Generated by CoffeeScript 1.6.1 +(function() { + var _this = this; - $: (selector) -> - $(selector, @el) + this.Sequence = (function() { - bind: -> - @$('#sequence-list .nav-item').click @goto - @el.on 'bookmark:add', @addBookmarkIconToActiveNavItem - @el.on 'bookmark:remove', @removeBookmarkIconFromActiveNavItem - @$('#sequence-list .nav-item').on('focus mouseenter', @displayTabTooltip) - @$('#sequence-list .nav-item').on('blur mouseleave', @hideTabTooltip) + function Sequence(element) { + var _this = this; + this.removeBookmarkIconFromActiveNavItem = function(event) { + return Sequence.prototype.removeBookmarkIconFromActiveNavItem.apply(_this, arguments); + }; + this.addBookmarkIconToActiveNavItem = function(event) { + return Sequence.prototype.addBookmarkIconToActiveNavItem.apply(_this, arguments); + }; + this._change_sequential = function(direction, event) { + return Sequence.prototype._change_sequential.apply(_this, arguments); + }; + this.selectPrevious = function(event) { + return Sequence.prototype.selectPrevious.apply(_this, arguments); + }; + this.selectNext = function(event) { + return Sequence.prototype.selectNext.apply(_this, arguments); + }; + this.goto = function(event) { + return Sequence.prototype.goto.apply(_this, arguments); + }; + this.toggleArrows = function() { + return Sequence.prototype.toggleArrows.apply(_this, arguments); + }; + this.updateProgress = function() { + return Sequence.prototype.updateProgress.apply(_this, arguments); + }; + this.addToUpdatedProblems = function(problem_id, new_content_state, new_state) { + return Sequence.prototype.addToUpdatedProblems.apply(_this, arguments); + }; + this.hideTabTooltip = function(event) { + return Sequence.prototype.hideTabTooltip.apply(_this, arguments); + }; + this.displayTabTooltip = function(event) { + return Sequence.prototype.displayTabTooltip.apply(_this, arguments); + }; + this.updatedProblems = {}; + this.requestToken = $(element).data('request-token'); + this.el = $(element).find('.sequence'); + this.path = $('.path'); + this.contents = this.$('.seq_contents'); + this.content_container = this.$('#seq_content'); + this.sr_container = this.$('.sr-is-focusable'); + this.num_contents = this.contents.length; + this.id = this.el.data('id'); + this.ajaxUrl = this.el.data('ajax-url'); + this.nextUrl = this.el.data('next-url'); + this.prevUrl = this.el.data('prev-url'); + this.base_page_title = " | " + document.title; + this.initProgress(); + this.bind(); + this.render(parseInt(this.el.data('position'))); + } - displayTabTooltip: (event) => - $(event.currentTarget).find('.sequence-tooltip').removeClass('sr') + Sequence.prototype.$ = function(selector) { + return $(selector, this.el); + }; - hideTabTooltip: (event) => - $(event.currentTarget).find('.sequence-tooltip').addClass('sr') + Sequence.prototype.bind = function() { + this.$('#sequence-list .nav-item').click(this.goto); + this.el.on('bookmark:add', this.addBookmarkIconToActiveNavItem); + this.el.on('bookmark:remove', this.removeBookmarkIconFromActiveNavItem); + this.$('#sequence-list .nav-item').on('focus mouseenter', this.displayTabTooltip); + return this.$('#sequence-list .nav-item').on('blur mouseleave', this.hideTabTooltip); + }; - initProgress: -> - @progressTable = {} # "#problem_#{id}" -> progress + Sequence.prototype.displayTabTooltip = function(event) { + return $(event.currentTarget).find('.sequence-tooltip').removeClass('sr'); + }; - updatePageTitle: -> - # update the page title to include the current section - position_link = @link_for(@position) - if position_link and position_link.data('page-title') - document.title = position_link.data('page-title') + @base_page_title + Sequence.prototype.hideTabTooltip = function(event) { + return $(event.currentTarget).find('.sequence-tooltip').addClass('sr'); + }; - hookUpContentStateChangeEvent: -> - $('.problems-wrapper').bind( - 'contentChanged', - (event, problem_id, new_content_state, new_state) => - @addToUpdatedProblems problem_id, new_content_state, new_state - ) + Sequence.prototype.initProgress = function() { + /* + "#problem_#{id}" -> progress + */ + return this.progressTable = {}; + }; - addToUpdatedProblems: (problem_id, new_content_state, new_state) => - # Used to keep updated problem's state temporarily. - # params: - # 'problem_id' is problem id. - # 'new_content_state' is the updated content of the problem. - # 'new_state' is the updated state of the problem. + Sequence.prototype.updatePageTitle = function() { + /* + update the page title to include the current section + */ - # initialize for the current sequence if there isn't any updated problem - # for this position. - if not @anyUpdatedProblems @position - @updatedProblems[@position] = {} + var position_link; + position_link = this.link_for(this.position); + if (position_link && position_link.data('page-title')) { + return document.title = position_link.data('page-title') + this.base_page_title; + } + }; - # Now, put problem content and score against problem id for current active sequence. - @updatedProblems[@position][problem_id] = [new_content_state, new_state] + Sequence.prototype.hookUpContentStateChangeEvent = function() { + var _this = this; + return $('.problems-wrapper').bind('contentChanged', function(event, problem_id, new_content_state, new_state) { + return _this.addToUpdatedProblems(problem_id, new_content_state, new_state); + }); + }; - anyUpdatedProblems:(position) -> - # check for the updated problems for given sequence position. - # params: - # 'position' can be any sequence position. - return @updatedProblems[position] != undefined + Sequence.prototype.addToUpdatedProblems = function(problem_id, new_content_state, new_state) { + /* + Used to keep updated problem's state temporarily. + */ - hookUpProgressEvent: -> - $('.problems-wrapper').bind 'progressChanged', @updateProgress + /* + params: + */ - mergeProgress: (p1, p2) -> - # if either is "NA", return the other one - if p1 == "NA" - return p2 - if p2 == "NA" - return p1 + /* + 'problem_id' is problem id. + */ - # Both real progresses - if p1 == "done" and p2 == "done" - return "done" + /* + 'new_content_state' is the updated content of the problem. + */ - # not done, so if any progress on either, in_progress - w1 = p1 == "done" or p1 == "in_progress" - w2 = p2 == "done" or p2 == "in_progress" - if w1 or w2 - return "in_progress" + /* + 'new_state' is the updated state of the problem. + */ - return "none" + /* + initialize for the current sequence if there isn't any updated problem + */ - updateProgress: => - new_progress = "NA" - _this = this - $('.problems-wrapper').each (index) -> - progress = $(this).data 'progress_status' - new_progress = _this.mergeProgress progress, new_progress + /* + for this position. + */ + if (!this.anyUpdatedProblems(this.position)) { + this.updatedProblems[this.position] = {}; + } + /* + Now, put problem content and score against problem id for current active sequence. + */ - @progressTable[@position] = new_progress + return this.updatedProblems[this.position][problem_id] = [new_content_state, new_state]; + }; - enableButton: (button_class, button_action) -> - @$(button_class).removeClass('disabled').removeAttr('disabled').click(button_action) + Sequence.prototype.anyUpdatedProblems = function(position) { + /* + check for the updated problems for given sequence position. + */ - disableButton: (button_class) -> - @$(button_class).addClass('disabled').attr('disabled', true) + /* + params: + */ - setButtonLabel: (button_class, button_label) -> - @$(button_class + ' .sr').html(button_label) + /* + 'position' can be any sequence position. + */ + return this.updatedProblems[position] !== void 0; + }; - updateButtonState: (button_class, button_action, action_label_prefix, is_at_boundary, boundary_url) -> - if is_at_boundary and boundary_url == 'None' - @disableButton(button_class) - else - button_label = action_label_prefix + (if is_at_boundary then ' Subsection' else ' Unit') - @setButtonLabel(button_class, button_label) - @enableButton(button_class, button_action) + Sequence.prototype.hookUpProgressEvent = function() { + return $('.problems-wrapper').bind('progressChanged', this.updateProgress); + }; - toggleArrows: => - @$('.sequence-nav-button').unbind('click') + Sequence.prototype.mergeProgress = function(p1, p2) { + /* + if either is "NA", return the other one + */ - # previous button - is_first_tab = @position == 1 - previous_button_class = '.sequence-nav-button.button-previous' - @updateButtonState( - previous_button_class, - @selectPrevious, - 'Previous', - is_first_tab, - @prevUrl - ) + var w1, w2; + if (p1 === "NA") { + return p2; + } + if (p2 === "NA") { + return p1; + } + /* + Both real progresses + */ - # next button - is_last_tab = @position >= @contents.length # use inequality in case contents.length is 0 and position is 1. - next_button_class = '.sequence-nav-button.button-next' - @updateButtonState( - next_button_class, - @selectNext, - 'Next', - is_last_tab, - @nextUrl - ) + if (p1 === "done" && p2 === "done") { + return "done"; + } + /* + not done, so if any progress on either, in_progress + */ - render: (new_position) -> - if @position != new_position - if @position != undefined - @mark_visited @position - modx_full_url = "#{@ajaxUrl}/goto_position" - $.postWithPrefix modx_full_url, position: new_position + w1 = p1 === "done" || p1 === "in_progress"; + w2 = p2 === "done" || p2 === "in_progress"; + if (w1 || w2) { + return "in_progress"; + } + return "none"; + }; - # On Sequence change, fire custom event "sequence:change" on element. - # Added for aborting video bufferization, see ../video/10_main.js - @el.trigger "sequence:change" - @mark_active new_position + Sequence.prototype.updateProgress = function() { + var new_progress; + new_progress = "NA"; + _this = this; + $('.problems-wrapper').each(function(index) { + var progress; + progress = $(this).data('progress_status'); + return new_progress = _this.mergeProgress(progress, new_progress); + }); + return this.progressTable[this.position] = new_progress; + }; - current_tab = @contents.eq(new_position - 1) + Sequence.prototype.enableButton = function(button_class, button_action) { + return this.$(button_class).removeClass('disabled').removeAttr('disabled').click(button_action); + }; - bookmarked = if @el.find('.active .bookmark-icon').hasClass('bookmarked') then true else false - @content_container.html(current_tab.text()).attr("aria-labelledby", current_tab.attr("aria-labelledby")).data('bookmarked', bookmarked) + Sequence.prototype.disableButton = function(button_class) { + return this.$(button_class).addClass('disabled').attr('disabled', true); + }; - # update the data-attributes with latest contents only for updated problems. - if @anyUpdatedProblems new_position - $.each @updatedProblems[new_position], (problem_id, latest_data) => - latest_content = latest_data[0] - latest_response = latest_data[1] - @content_container - .find("[data-problem-id='#{ problem_id }']") - .data('content', latest_content) - .data('problem-score', latest_response.current_score) - .data('problem-total-possible', latest_response.total_possible) - .data('attempts-used', latest_response.attempts_used) + Sequence.prototype.setButtonLabel = function(button_class, button_label) { + return this.$(button_class + ' .sr').html(button_label); + }; - XBlock.initializeBlocks(@content_container, @requestToken) + Sequence.prototype.updateButtonState = function(button_class, button_action, action_label_prefix, is_at_boundary, boundary_url) { + var button_label; + if (is_at_boundary && boundary_url === 'None') { + return this.disableButton(button_class); + } else { + button_label = action_label_prefix + (is_at_boundary ? ' Subsection' : ' Unit'); + this.setButtonLabel(button_class, button_label); + return this.enableButton(button_class, button_action); + } + }; - window.update_schematics() # For embedded circuit simulator exercises in 6.002x + Sequence.prototype.toggleArrows = function() { + var is_first_tab, is_last_tab, next_button_class, previous_button_class; + this.$('.sequence-nav-button').unbind('click'); + /* + previous button + */ - @position = new_position - @toggleArrows() - @hookUpContentStateChangeEvent() - @hookUpProgressEvent() - @updatePageTitle() + is_first_tab = this.position === 1; + previous_button_class = '.sequence-nav-button.button-previous'; + this.updateButtonState(previous_button_class, this.selectPrevious, 'Previous', is_first_tab, this.prevUrl); + /* + next button + */ - sequence_links = @content_container.find('a.seqnav') - sequence_links.click @goto + /* + use inequality in case contents.length is 0 and position is 1. + */ - @path.text(@el.find('.nav-item.active').data('path')) + is_last_tab = this.position >= this.contents.length; + next_button_class = '.sequence-nav-button.button-next'; + return this.updateButtonState(next_button_class, this.selectNext, 'Next', is_last_tab, this.nextUrl); + }; - @sr_container.focus() + Sequence.prototype.render = function(new_position) { + var bookmarked, current_tab, modx_full_url, sequence_links, + _this = this; + if (this.position !== new_position) { + if (this.position !== void 0) { + this.mark_visited(this.position); + modx_full_url = "" + this.ajaxUrl + "/goto_position"; + $.postWithPrefix(modx_full_url, { + position: new_position + }); + } + /* + On Sequence change, fire custom event "sequence:change" on element. + */ - goto: (event) => - event.preventDefault() - if $(event.currentTarget).hasClass 'seqnav' # Links from courseware ..., was .target - new_position = $(event.currentTarget).attr('href') - else # Tab links generated by backend template - new_position = $(event.currentTarget).data('element') + /* + Added for aborting video bufferization, see ../video/10_main.js + */ - if (1 <= new_position) and (new_position <= @num_contents) - is_bottom_nav = $(event.target).closest('nav[class="sequence-bottom"]').length > 0 - if is_bottom_nav - widget_placement = 'bottom' - else - widget_placement = 'top' - Logger.log "edx.ui.lms.sequence.tab_selected", # Formerly known as seq_goto - current_tab: @position - target_tab: new_position - tab_count: @num_contents - id: @id + this.el.trigger("sequence:change"); + this.mark_active(new_position); + current_tab = this.contents.eq(new_position - 1); + bookmarked = this.el.find('.active .bookmark-icon').hasClass('bookmarked') ? true : false; + this.content_container.html(current_tab.text()).attr("aria-labelledby", current_tab.attr("aria-labelledby")).data('bookmarked', bookmarked); + /* + update the data-attributes with latest contents only for updated problems. + */ + + if (this.anyUpdatedProblems(new_position)) { + $.each(this.updatedProblems[new_position], function(problem_id, latest_data) { + var latest_content, latest_response; + latest_content = latest_data[0]; + latest_response = latest_data[1]; + return _this.content_container.find("[data-problem-id='" + problem_id + "']").data('content', latest_content).data('problem-score', latest_response.current_score).data('problem-total-possible', latest_response.total_possible).data('attempts-used', latest_response.attempts_used); + }); + } + XBlock.initializeBlocks(this.content_container, this.requestToken); + /* + For embedded circuit simulator exercises in 6.002x + */ + + window.update_schematics(); + this.position = new_position; + this.toggleArrows(); + this.hookUpContentStateChangeEvent(); + this.hookUpProgressEvent(); + this.updatePageTitle(); + sequence_links = this.content_container.find('a.seqnav'); + sequence_links.click(this.goto); + this.path.text(this.el.find('.nav-item.active').data('path')); + return this.sr_container.focus(); + } + }; + + Sequence.prototype.goto = function(event) { + var alert_template, alert_text, is_bottom_nav, new_position, widget_placement; + event.preventDefault(); + /* + Links from courseware ..., was .target + */ + + if ($(event.currentTarget).hasClass('seqnav')) { + new_position = $(event.currentTarget).attr('href'); + /* + Tab links generated by backend template + */ + + } else { + new_position = $(event.currentTarget).data('element'); + } + if ((1 <= new_position) && (new_position <= this.num_contents)) { + is_bottom_nav = $(event.target).closest('nav[class="sequence-bottom"]').length > 0; + if (is_bottom_nav) { + widget_placement = 'bottom'; + } else { + widget_placement = 'top'; + } + /* + Formerly known as seq_goto + */ + + Logger.log("edx.ui.lms.sequence.tab_selected", { + current_tab: this.position, + target_tab: new_position, + tab_count: this.num_contents, + id: this.id, + widget_placement: widget_placement + }); + /* + On Sequence change, destroy any existing polling thread + */ + + /* + for queued submissions, see ../capa/display.js + */ + + if (window.queuePollerID) { + window.clearTimeout(window.queuePollerID); + delete window.queuePollerID; + } + return this.render(new_position); + } else { + alert_template = gettext("Sequence error! Cannot navigate to %(tab_name)s in the current SequenceModule. Please contact the course staff."); + alert_text = interpolate(alert_template, { + tab_name: new_position + }, true); + return alert(alert_text); + } + }; + + Sequence.prototype.selectNext = function(event) { + return this._change_sequential('next', event); + }; + + Sequence.prototype.selectPrevious = function(event) { + return this._change_sequential('previous', event); + }; + + /* + `direction` can be 'previous' or 'next' + */ + + + Sequence.prototype._change_sequential = function(direction, event) { + /* + silently abort if direction is invalid. + */ + + var analytics_event_name, is_bottom_nav, new_position, offset, widget_placement; + if (direction !== 'previous' && direction !== 'next') { + return; + } + event.preventDefault(); + analytics_event_name = "edx.ui.lms.sequence." + direction + "_selected"; + is_bottom_nav = $(event.target).closest('nav[class="sequence-bottom"]').length > 0; + if (is_bottom_nav) { + widget_placement = 'bottom'; + } else { + widget_placement = 'top'; + } + /* + Formerly known as seq_next and seq_prev + */ + + Logger.log(analytics_event_name, { + id: this.id, + current_tab: this.position, + tab_count: this.num_contents, widget_placement: widget_placement + }); + if ((direction === 'next') && (this.position >= this.contents.length)) { + return window.location.href = this.nextUrl; + } else if ((direction === 'previous') && (this.position === 1)) { + return window.location.href = this.prevUrl; + } else { + /* + If the bottom nav is used, scroll to the top of the page on change. + */ - # On Sequence change, destroy any existing polling thread - # for queued submissions, see ../capa/display.js - if window.queuePollerID - window.clearTimeout(window.queuePollerID) - delete window.queuePollerID + if (is_bottom_nav) { + $.scrollTo(0, 150); + } + offset = { + next: 1, + previous: -1 + }; + new_position = this.position + offset[direction]; + return this.render(new_position); + } + }; - @render new_position - else - alert_template = gettext("Sequence error! Cannot navigate to %(tab_name)s in the current SequenceModule. Please contact the course staff.") - alert_text = interpolate(alert_template, {tab_name: new_position}, true) - alert alert_text + Sequence.prototype.link_for = function(position) { + return this.$("#sequence-list .nav-item[data-element=" + position + "]"); + }; - selectNext: (event) => @_change_sequential 'next', event + Sequence.prototype.mark_visited = function(position) { + /* + Don't overwrite class attribute to avoid changing Progress class + */ - selectPrevious: (event) => @_change_sequential 'previous', event + var element; + element = this.link_for(position); + return element.removeClass("inactive").removeClass("active").addClass("visited"); + }; - # `direction` can be 'previous' or 'next' - _change_sequential: (direction, event) => - # silently abort if direction is invalid. - return unless direction in ['previous', 'next'] + Sequence.prototype.mark_active = function(position) { + /* + Don't overwrite class attribute to avoid changing Progress class + */ - event.preventDefault() + var element; + element = this.link_for(position); + return element.removeClass("inactive").removeClass("visited").addClass("active"); + }; - analytics_event_name = "edx.ui.lms.sequence.#{direction}_selected" - is_bottom_nav = $(event.target).closest('nav[class="sequence-bottom"]').length > 0 + Sequence.prototype.addBookmarkIconToActiveNavItem = function(event) { + event.preventDefault(); + this.el.find('.nav-item.active .bookmark-icon').removeClass('is-hidden').addClass('bookmarked'); + return this.el.find('.nav-item.active .bookmark-icon-sr').text(gettext('Bookmarked')); + }; - if is_bottom_nav - widget_placement = 'bottom' - else - widget_placement = 'top' + Sequence.prototype.removeBookmarkIconFromActiveNavItem = function(event) { + event.preventDefault(); + this.el.find('.nav-item.active .bookmark-icon').removeClass('bookmarked').addClass('is-hidden'); + return this.el.find('.nav-item.active .bookmark-icon-sr').text(''); + }; - Logger.log analytics_event_name, # Formerly known as seq_next and seq_prev - id: @id - current_tab: @position - tab_count: @num_contents - widget_placement: widget_placement + return Sequence; - if (direction == 'next') and (@position >= @contents.length) - window.location.href = @nextUrl - else if (direction == 'previous') and (@position == 1) - window.location.href = @prevUrl - else - # If the bottom nav is used, scroll to the top of the page on change. - if is_bottom_nav - $.scrollTo 0, 150 - offset = - next: 1 - previous: -1 - new_position = @position + offset[direction] - @render new_position + })(); - link_for: (position) -> - @$("#sequence-list .nav-item[data-element=#{position}]") - - mark_visited: (position) -> - # Don't overwrite class attribute to avoid changing Progress class - element = @link_for(position) - element.removeClass("inactive") - .removeClass("active") - .addClass("visited") - - mark_active: (position) -> - # Don't overwrite class attribute to avoid changing Progress class - element = @link_for(position) - element.removeClass("inactive") - .removeClass("visited") - .addClass("active") - - addBookmarkIconToActiveNavItem: (event) => - event.preventDefault() - @el.find('.nav-item.active .bookmark-icon').removeClass('is-hidden').addClass('bookmarked') - @el.find('.nav-item.active .bookmark-icon-sr').text(gettext('Bookmarked')) - - removeBookmarkIconFromActiveNavItem: (event) => - event.preventDefault() - @el.find('.nav-item.active .bookmark-icon').removeClass('bookmarked').addClass('is-hidden') - @el.find('.nav-item.active .bookmark-icon-sr').text('') +}).call(this); From 36e008676958cb7b9ee99458d407626e6e5f6b10 Mon Sep 17 00:00:00 2001 From: Brian Jacobel Date: Fri, 16 Dec 2016 09:53:14 -0500 Subject: [PATCH 4/5] Clean up generated javascript for display.js --- .../xmodule/js/src/sequence/display.js | 781 ++++++++---------- 1 file changed, 336 insertions(+), 445 deletions(-) diff --git a/common/lib/xmodule/xmodule/js/src/sequence/display.js b/common/lib/xmodule/xmodule/js/src/sequence/display.js index 899c7bcc20..e914595b3b 100644 --- a/common/lib/xmodule/xmodule/js/src/sequence/display.js +++ b/common/lib/xmodule/xmodule/js/src/sequence/display.js @@ -1,463 +1,354 @@ -// Generated by CoffeeScript 1.6.1 +/* eslint-disable no-underscore-dangle */ +/* globals Logger, interpolate */ + (function() { - var _this = this; + 'use strict'; - this.Sequence = (function() { + this.Sequence = (function() { + function Sequence(element) { + var self = this; - function Sequence(element) { - var _this = this; - this.removeBookmarkIconFromActiveNavItem = function(event) { - return Sequence.prototype.removeBookmarkIconFromActiveNavItem.apply(_this, arguments); - }; - this.addBookmarkIconToActiveNavItem = function(event) { - return Sequence.prototype.addBookmarkIconToActiveNavItem.apply(_this, arguments); - }; - this._change_sequential = function(direction, event) { - return Sequence.prototype._change_sequential.apply(_this, arguments); - }; - this.selectPrevious = function(event) { - return Sequence.prototype.selectPrevious.apply(_this, arguments); - }; - this.selectNext = function(event) { - return Sequence.prototype.selectNext.apply(_this, arguments); - }; - this.goto = function(event) { - return Sequence.prototype.goto.apply(_this, arguments); - }; - this.toggleArrows = function() { - return Sequence.prototype.toggleArrows.apply(_this, arguments); - }; - this.updateProgress = function() { - return Sequence.prototype.updateProgress.apply(_this, arguments); - }; - this.addToUpdatedProblems = function(problem_id, new_content_state, new_state) { - return Sequence.prototype.addToUpdatedProblems.apply(_this, arguments); - }; - this.hideTabTooltip = function(event) { - return Sequence.prototype.hideTabTooltip.apply(_this, arguments); - }; - this.displayTabTooltip = function(event) { - return Sequence.prototype.displayTabTooltip.apply(_this, arguments); - }; - this.updatedProblems = {}; - this.requestToken = $(element).data('request-token'); - this.el = $(element).find('.sequence'); - this.path = $('.path'); - this.contents = this.$('.seq_contents'); - this.content_container = this.$('#seq_content'); - this.sr_container = this.$('.sr-is-focusable'); - this.num_contents = this.contents.length; - this.id = this.el.data('id'); - this.ajaxUrl = this.el.data('ajax-url'); - this.nextUrl = this.el.data('next-url'); - this.prevUrl = this.el.data('prev-url'); - this.base_page_title = " | " + document.title; - this.initProgress(); - this.bind(); - this.render(parseInt(this.el.data('position'))); - } + this.removeBookmarkIconFromActiveNavItem = function(event) { + return Sequence.prototype.removeBookmarkIconFromActiveNavItem.apply(self, [event]); + }; + this.addBookmarkIconToActiveNavItem = function(event) { + return Sequence.prototype.addBookmarkIconToActiveNavItem.apply(self, [event]); + }; + this._change_sequential = function(direction, event) { + return Sequence.prototype._change_sequential.apply(self, [direction, event]); + }; + this.selectPrevious = function(event) { + return Sequence.prototype.selectPrevious.apply(self, [event]); + }; + this.selectNext = function(event) { + return Sequence.prototype.selectNext.apply(self, [event]); + }; + this.goto = function(event) { + return Sequence.prototype.goto.apply(self, [event]); + }; + this.toggleArrows = function() { + return Sequence.prototype.toggleArrows.apply(self); + }; + this.addToUpdatedProblems = function(problemId, newContentState, newState) { + return Sequence.prototype.addToUpdatedProblems.apply(self, [problemId, newContentState, newState]); + }; + this.hideTabTooltip = function(event) { + return Sequence.prototype.hideTabTooltip.apply(self, [event]); + }; + this.displayTabTooltip = function(event) { + return Sequence.prototype.displayTabTooltip.apply(self, [event]); + }; - Sequence.prototype.$ = function(selector) { - return $(selector, this.el); - }; - - Sequence.prototype.bind = function() { - this.$('#sequence-list .nav-item').click(this.goto); - this.el.on('bookmark:add', this.addBookmarkIconToActiveNavItem); - this.el.on('bookmark:remove', this.removeBookmarkIconFromActiveNavItem); - this.$('#sequence-list .nav-item').on('focus mouseenter', this.displayTabTooltip); - return this.$('#sequence-list .nav-item').on('blur mouseleave', this.hideTabTooltip); - }; - - Sequence.prototype.displayTabTooltip = function(event) { - return $(event.currentTarget).find('.sequence-tooltip').removeClass('sr'); - }; - - Sequence.prototype.hideTabTooltip = function(event) { - return $(event.currentTarget).find('.sequence-tooltip').addClass('sr'); - }; - - Sequence.prototype.initProgress = function() { - /* - "#problem_#{id}" -> progress - */ - return this.progressTable = {}; - }; - - Sequence.prototype.updatePageTitle = function() { - /* - update the page title to include the current section - */ - - var position_link; - position_link = this.link_for(this.position); - if (position_link && position_link.data('page-title')) { - return document.title = position_link.data('page-title') + this.base_page_title; - } - }; - - Sequence.prototype.hookUpContentStateChangeEvent = function() { - var _this = this; - return $('.problems-wrapper').bind('contentChanged', function(event, problem_id, new_content_state, new_state) { - return _this.addToUpdatedProblems(problem_id, new_content_state, new_state); - }); - }; - - Sequence.prototype.addToUpdatedProblems = function(problem_id, new_content_state, new_state) { - /* - Used to keep updated problem's state temporarily. - */ - - /* - params: - */ - - /* - 'problem_id' is problem id. - */ - - /* - 'new_content_state' is the updated content of the problem. - */ - - /* - 'new_state' is the updated state of the problem. - */ - - /* - initialize for the current sequence if there isn't any updated problem - */ - - /* - for this position. - */ - if (!this.anyUpdatedProblems(this.position)) { - this.updatedProblems[this.position] = {}; - } - /* - Now, put problem content and score against problem id for current active sequence. - */ - - return this.updatedProblems[this.position][problem_id] = [new_content_state, new_state]; - }; - - Sequence.prototype.anyUpdatedProblems = function(position) { - /* - check for the updated problems for given sequence position. - */ - - /* - params: - */ - - /* - 'position' can be any sequence position. - */ - return this.updatedProblems[position] !== void 0; - }; - - Sequence.prototype.hookUpProgressEvent = function() { - return $('.problems-wrapper').bind('progressChanged', this.updateProgress); - }; - - Sequence.prototype.mergeProgress = function(p1, p2) { - /* - if either is "NA", return the other one - */ - - var w1, w2; - if (p1 === "NA") { - return p2; - } - if (p2 === "NA") { - return p1; - } - /* - Both real progresses - */ - - if (p1 === "done" && p2 === "done") { - return "done"; - } - /* - not done, so if any progress on either, in_progress - */ - - w1 = p1 === "done" || p1 === "in_progress"; - w2 = p2 === "done" || p2 === "in_progress"; - if (w1 || w2) { - return "in_progress"; - } - return "none"; - }; - - Sequence.prototype.updateProgress = function() { - var new_progress; - new_progress = "NA"; - _this = this; - $('.problems-wrapper').each(function(index) { - var progress; - progress = $(this).data('progress_status'); - return new_progress = _this.mergeProgress(progress, new_progress); - }); - return this.progressTable[this.position] = new_progress; - }; - - Sequence.prototype.enableButton = function(button_class, button_action) { - return this.$(button_class).removeClass('disabled').removeAttr('disabled').click(button_action); - }; - - Sequence.prototype.disableButton = function(button_class) { - return this.$(button_class).addClass('disabled').attr('disabled', true); - }; - - Sequence.prototype.setButtonLabel = function(button_class, button_label) { - return this.$(button_class + ' .sr').html(button_label); - }; - - Sequence.prototype.updateButtonState = function(button_class, button_action, action_label_prefix, is_at_boundary, boundary_url) { - var button_label; - if (is_at_boundary && boundary_url === 'None') { - return this.disableButton(button_class); - } else { - button_label = action_label_prefix + (is_at_boundary ? ' Subsection' : ' Unit'); - this.setButtonLabel(button_class, button_label); - return this.enableButton(button_class, button_action); - } - }; - - Sequence.prototype.toggleArrows = function() { - var is_first_tab, is_last_tab, next_button_class, previous_button_class; - this.$('.sequence-nav-button').unbind('click'); - /* - previous button - */ - - is_first_tab = this.position === 1; - previous_button_class = '.sequence-nav-button.button-previous'; - this.updateButtonState(previous_button_class, this.selectPrevious, 'Previous', is_first_tab, this.prevUrl); - /* - next button - */ - - /* - use inequality in case contents.length is 0 and position is 1. - */ - - is_last_tab = this.position >= this.contents.length; - next_button_class = '.sequence-nav-button.button-next'; - return this.updateButtonState(next_button_class, this.selectNext, 'Next', is_last_tab, this.nextUrl); - }; - - Sequence.prototype.render = function(new_position) { - var bookmarked, current_tab, modx_full_url, sequence_links, - _this = this; - if (this.position !== new_position) { - if (this.position !== void 0) { - this.mark_visited(this.position); - modx_full_url = "" + this.ajaxUrl + "/goto_position"; - $.postWithPrefix(modx_full_url, { - position: new_position - }); + this.updatedProblems = {}; + this.requestToken = $(element).data('request-token'); + this.el = $(element).find('.sequence'); + this.path = $('.path'); + this.contents = this.$('.seq_contents'); + this.content_container = this.$('#seq_content'); + this.sr_container = this.$('.sr-is-focusable'); + this.num_contents = this.contents.length; + this.id = this.el.data('id'); + this.ajaxUrl = this.el.data('ajax-url'); + this.nextUrl = this.el.data('next-url'); + this.prevUrl = this.el.data('prev-url'); + this.base_page_title = ' | ' + document.title; + this.bind(); + this.render(parseInt(this.el.data('position'), 10)); } - /* - On Sequence change, fire custom event "sequence:change" on element. - */ - /* - Added for aborting video bufferization, see ../video/10_main.js - */ - - this.el.trigger("sequence:change"); - this.mark_active(new_position); - current_tab = this.contents.eq(new_position - 1); - bookmarked = this.el.find('.active .bookmark-icon').hasClass('bookmarked') ? true : false; - this.content_container.html(current_tab.text()).attr("aria-labelledby", current_tab.attr("aria-labelledby")).data('bookmarked', bookmarked); - /* - update the data-attributes with latest contents only for updated problems. - */ - - if (this.anyUpdatedProblems(new_position)) { - $.each(this.updatedProblems[new_position], function(problem_id, latest_data) { - var latest_content, latest_response; - latest_content = latest_data[0]; - latest_response = latest_data[1]; - return _this.content_container.find("[data-problem-id='" + problem_id + "']").data('content', latest_content).data('problem-score', latest_response.current_score).data('problem-total-possible', latest_response.total_possible).data('attempts-used', latest_response.attempts_used); - }); - } - XBlock.initializeBlocks(this.content_container, this.requestToken); - /* - For embedded circuit simulator exercises in 6.002x - */ - - window.update_schematics(); - this.position = new_position; - this.toggleArrows(); - this.hookUpContentStateChangeEvent(); - this.hookUpProgressEvent(); - this.updatePageTitle(); - sequence_links = this.content_container.find('a.seqnav'); - sequence_links.click(this.goto); - this.path.text(this.el.find('.nav-item.active').data('path')); - return this.sr_container.focus(); - } - }; - - Sequence.prototype.goto = function(event) { - var alert_template, alert_text, is_bottom_nav, new_position, widget_placement; - event.preventDefault(); - /* - Links from courseware ..., was .target - */ - - if ($(event.currentTarget).hasClass('seqnav')) { - new_position = $(event.currentTarget).attr('href'); - /* - Tab links generated by backend template - */ - - } else { - new_position = $(event.currentTarget).data('element'); - } - if ((1 <= new_position) && (new_position <= this.num_contents)) { - is_bottom_nav = $(event.target).closest('nav[class="sequence-bottom"]').length > 0; - if (is_bottom_nav) { - widget_placement = 'bottom'; - } else { - widget_placement = 'top'; - } - /* - Formerly known as seq_goto - */ - - Logger.log("edx.ui.lms.sequence.tab_selected", { - current_tab: this.position, - target_tab: new_position, - tab_count: this.num_contents, - id: this.id, - widget_placement: widget_placement - }); - /* - On Sequence change, destroy any existing polling thread - */ - - /* - for queued submissions, see ../capa/display.js - */ - - if (window.queuePollerID) { - window.clearTimeout(window.queuePollerID); - delete window.queuePollerID; - } - return this.render(new_position); - } else { - alert_template = gettext("Sequence error! Cannot navigate to %(tab_name)s in the current SequenceModule. Please contact the course staff."); - alert_text = interpolate(alert_template, { - tab_name: new_position - }, true); - return alert(alert_text); - } - }; - - Sequence.prototype.selectNext = function(event) { - return this._change_sequential('next', event); - }; - - Sequence.prototype.selectPrevious = function(event) { - return this._change_sequential('previous', event); - }; - - /* - `direction` can be 'previous' or 'next' - */ - - - Sequence.prototype._change_sequential = function(direction, event) { - /* - silently abort if direction is invalid. - */ - - var analytics_event_name, is_bottom_nav, new_position, offset, widget_placement; - if (direction !== 'previous' && direction !== 'next') { - return; - } - event.preventDefault(); - analytics_event_name = "edx.ui.lms.sequence." + direction + "_selected"; - is_bottom_nav = $(event.target).closest('nav[class="sequence-bottom"]').length > 0; - if (is_bottom_nav) { - widget_placement = 'bottom'; - } else { - widget_placement = 'top'; - } - /* - Formerly known as seq_next and seq_prev - */ - - Logger.log(analytics_event_name, { - id: this.id, - current_tab: this.position, - tab_count: this.num_contents, - widget_placement: widget_placement - }); - if ((direction === 'next') && (this.position >= this.contents.length)) { - return window.location.href = this.nextUrl; - } else if ((direction === 'previous') && (this.position === 1)) { - return window.location.href = this.prevUrl; - } else { - /* - If the bottom nav is used, scroll to the top of the page on change. - */ - - if (is_bottom_nav) { - $.scrollTo(0, 150); - } - offset = { - next: 1, - previous: -1 + Sequence.prototype.$ = function(selector) { + return $(selector, this.el); }; - new_position = this.position + offset[direction]; - return this.render(new_position); - } - }; - Sequence.prototype.link_for = function(position) { - return this.$("#sequence-list .nav-item[data-element=" + position + "]"); - }; + Sequence.prototype.bind = function() { + this.$('#sequence-list .nav-item').click(this.goto); + this.el.on('bookmark:add', this.addBookmarkIconToActiveNavItem); + this.el.on('bookmark:remove', this.removeBookmarkIconFromActiveNavItem); + this.$('#sequence-list .nav-item').on('focus mouseenter', this.displayTabTooltip); + this.$('#sequence-list .nav-item').on('blur mouseleave', this.hideTabTooltip); + }; - Sequence.prototype.mark_visited = function(position) { - /* - Don't overwrite class attribute to avoid changing Progress class - */ + Sequence.prototype.displayTabTooltip = function(event) { + $(event.currentTarget).find('.sequence-tooltip').removeClass('sr'); + }; - var element; - element = this.link_for(position); - return element.removeClass("inactive").removeClass("active").addClass("visited"); - }; + Sequence.prototype.hideTabTooltip = function(event) { + $(event.currentTarget).find('.sequence-tooltip').addClass('sr'); + }; - Sequence.prototype.mark_active = function(position) { - /* - Don't overwrite class attribute to avoid changing Progress class - */ + Sequence.prototype.updatePageTitle = function() { + // update the page title to include the current section + var positionLink = this.link_for(this.position); - var element; - element = this.link_for(position); - return element.removeClass("inactive").removeClass("visited").addClass("active"); - }; + if (positionLink && positionLink.data('page-title')) { + document.title = positionLink.data('page-title') + this.base_page_title; + } + }; - Sequence.prototype.addBookmarkIconToActiveNavItem = function(event) { - event.preventDefault(); - this.el.find('.nav-item.active .bookmark-icon').removeClass('is-hidden').addClass('bookmarked'); - return this.el.find('.nav-item.active .bookmark-icon-sr').text(gettext('Bookmarked')); - }; + Sequence.prototype.hookUpContentStateChangeEvent = function() { + var self = this; - Sequence.prototype.removeBookmarkIconFromActiveNavItem = function(event) { - event.preventDefault(); - this.el.find('.nav-item.active .bookmark-icon').removeClass('bookmarked').addClass('is-hidden'); - return this.el.find('.nav-item.active .bookmark-icon-sr').text(''); - }; + return $('.problems-wrapper').bind('contentChanged', function(event, problemId, newContentState, newState) { + return self.addToUpdatedProblems(problemId, newContentState, newState); + }); + }; - return Sequence; + Sequence.prototype.addToUpdatedProblems = function(problemId, newContentState, newState) { + /** + * Used to keep updated problem's state temporarily. + * params: + * 'problem_id' is problem id. + * 'new_content_state' is the updated content of the problem. + * 'new_state' is the updated state of the problem. + * initialize for the current sequence if there isn't any updated problem + * for this position. + */ - })(); + if (!this.anyUpdatedProblems(this.position)) { + this.updatedProblems[this.position] = {}; + } + // Now, put problem content and score against problem id for current active sequence. + + this.updatedProblems[this.position][problemId] = [newContentState, newState]; + }; + + Sequence.prototype.anyUpdatedProblems = function(position) { + /** + * check for the updated problems for given sequence position. + * params: + * 'position' can be any sequence position. + */ + return typeof(this.updatedProblems[position]) !== 'undefined'; + }; + + Sequence.prototype.enableButton = function(buttonClass, buttonAction) { + this.$(buttonClass) + .removeClass('disabled') + .removeAttr('disabled') + .click(buttonAction); + }; + + Sequence.prototype.disableButton = function(buttonClass) { + this.$(buttonClass).addClass('disabled').attr('disabled', true); + }; + + Sequence.prototype.setButtonLabel = function(buttonClass, buttonLabel) { + this.$(buttonClass + ' .sr').html(buttonLabel); + }; + + Sequence.prototype.updateButtonState = function( + buttonClass, buttonAction, actionLabelPrefix, isAtBoundary, boundaryUrl + ) { + var buttonLabel; + if (isAtBoundary && boundaryUrl === 'None') { + this.disableButton(buttonClass); + } else { + buttonLabel = actionLabelPrefix + (isAtBoundary ? ' Subsection' : ' Unit'); + this.setButtonLabel(buttonClass, buttonLabel); + this.enableButton(buttonClass, buttonAction); + } + }; + + Sequence.prototype.toggleArrows = function() { + var isFirstTab, isLastTab, nextButtonClass, previousButtonClass; + + this.$('.sequence-nav-button').unbind('click'); + + // previous button + isFirstTab = this.position === 1; + previousButtonClass = '.sequence-nav-button.button-previous'; + this.updateButtonState(previousButtonClass, this.selectPrevious, 'Previous', isFirstTab, this.prevUrl); + + // next button + // use inequality in case contents.length is 0 and position is 1. + isLastTab = this.position >= this.contents.length; + nextButtonClass = '.sequence-nav-button.button-next'; + this.updateButtonState(nextButtonClass, this.selectNext, 'Next', isLastTab, this.nextUrl); + }; + + Sequence.prototype.render = function(newPosition) { + var bookmarked, currentTab, modxFullUrl, sequenceLinks, + self = this; + if (this.position !== newPosition) { + if (this.position) { + this.mark_visited(this.position); + modxFullUrl = '' + this.ajaxUrl + '/goto_position'; + $.postWithPrefix(modxFullUrl, { + position: newPosition + }); + } + + // On Sequence change, fire custom event 'sequence:change' on element. + // Added for aborting video bufferization, see ../video/10_main.js + this.el.trigger('sequence:change'); + this.mark_active(newPosition); + currentTab = this.contents.eq(newPosition - 1); + bookmarked = this.el.find('.active .bookmark-icon').hasClass('bookmarked'); + + // update the data-attributes with latest contents only for updated problems. + this.content_container + .html(currentTab.text()) + .attr('aria-labelledby', currentTab.attr('aria-labelledby')) + .data('bookmarked', bookmarked); + + + if (this.anyUpdatedProblems(newPosition)) { + $.each(this.updatedProblems[newPosition], function(problemId, latestData) { + var latestContent, latestResponse; + latestContent = latestData[0]; + latestResponse = latestData[1]; + self.content_container + .find("[data-problem-id='" + problemId + "']") + .data('content', latestContent) + .data('problem-score', latestResponse.current_score) + .data('problem-total-possible', latestResponse.total_possible) + .data('attempts-used', latestResponse.attempts_used); + }); + } + XBlock.initializeBlocks(this.content_container, this.requestToken); + + // For embedded circuit simulator exercises in 6.002x + window.update_schematics(); + this.position = newPosition; + this.toggleArrows(); + this.hookUpContentStateChangeEvent(); + this.updatePageTitle(); + sequenceLinks = this.content_container.find('a.seqnav'); + sequenceLinks.click(this.goto); + this.path.text(this.el.find('.nav-item.active').data('path')); + this.sr_container.focus(); + } + }; + + Sequence.prototype.goto = function(event) { + var alertTemplate, alertText, isBottomNav, newPosition, widgetPlacement; + event.preventDefault(); + + // Links from courseware ..., was .target_tab + if ($(event.currentTarget).hasClass('seqnav')) { + newPosition = $(event.currentTarget).attr('href'); + // Tab links generated by backend template + } else { + newPosition = $(event.currentTarget).data('element'); + } + + if ((newPosition >= 1) && (newPosition <= this.num_contents)) { + isBottomNav = $(event.target).closest('nav[class="sequence-bottom"]').length > 0; + + if (isBottomNav) { + widgetPlacement = 'bottom'; + } else { + widgetPlacement = 'top'; + } + + // Formerly known as seq_goto + Logger.log('edx.ui.lms.sequence.tab_selected', { + current_tab: this.position, + target_tab: newPosition, + tab_count: this.num_contents, + id: this.id, + widget_placement: widgetPlacement + }); + + // On Sequence change, destroy any existing polling thread + // for queued submissions, see ../capa/display.js + if (window.queuePollerID) { + window.clearTimeout(window.queuePollerID); + delete window.queuePollerID; + } + this.render(newPosition); + } else { + alertTemplate = gettext('Sequence error! Cannot navigate to %(tab_name)s in the current SequenceModule. Please contact the course staff.'); // eslint-disable-line max-len + alertText = interpolate(alertTemplate, { + tab_name: newPosition + }, true); + alert(alertText); // eslint-disable-line no-alert + } + }; + + Sequence.prototype.selectNext = function(event) { + this._change_sequential('next', event); + }; + + Sequence.prototype.selectPrevious = function(event) { + this._change_sequential('previous', event); + }; + + // `direction` can be 'previous' or 'next' + Sequence.prototype._change_sequential = function(direction, event) { + var analyticsEventName, isBottomNav, newPosition, offset, widgetPlacement; + + // silently abort if direction is invalid. + if (direction !== 'previous' && direction !== 'next') { + return; + } + event.preventDefault(); + analyticsEventName = 'edx.ui.lms.sequence.' + direction + '_selected'; + isBottomNav = $(event.target).closest('nav[class="sequence-bottom"]').length > 0; + + if (isBottomNav) { + widgetPlacement = 'bottom'; + } else { + widgetPlacement = 'top'; + } + + // Formerly known as seq_next and seq_prev + Logger.log(analyticsEventName, { + id: this.id, + current_tab: this.position, + tab_count: this.num_contents, + widget_placement: widgetPlacement + }); + + if ((direction === 'next') && (this.position >= this.contents.length)) { + window.location.href = this.nextUrl; + } else if ((direction === 'previous') && (this.position === 1)) { + window.location.href = this.prevUrl; + } else { + // If the bottom nav is used, scroll to the top of the page on change. + if (isBottomNav) { + $.scrollTo(0, 150); + } + + offset = { + next: 1, + previous: -1 + }; + + newPosition = this.position + offset[direction]; + this.render(newPosition); + } + }; + + Sequence.prototype.link_for = function(position) { + return this.$('#sequence-list .nav-item[data-element=' + position + ']'); + }; + + Sequence.prototype.mark_visited = function(position) { + // Don't overwrite class attribute to avoid changing Progress class + var element = this.link_for(position); + element.removeClass('inactive').removeClass('active').addClass('visited'); + }; + + Sequence.prototype.mark_active = function(position) { + // Don't overwrite class attribute to avoid changing Progress class + var element = this.link_for(position); + element.removeClass('inactive').removeClass('visited').addClass('active'); + }; + + Sequence.prototype.addBookmarkIconToActiveNavItem = function(event) { + event.preventDefault(); + this.el.find('.nav-item.active .bookmark-icon').removeClass('is-hidden').addClass('bookmarked'); + this.el.find('.nav-item.active .bookmark-icon-sr').text(gettext('Bookmarked')); + }; + + Sequence.prototype.removeBookmarkIconFromActiveNavItem = function(event) { + event.preventDefault(); + this.el.find('.nav-item.active .bookmark-icon').removeClass('bookmarked').addClass('is-hidden'); + this.el.find('.nav-item.active .bookmark-icon-sr').text(''); + }; + + return Sequence; + }()); }).call(this); From 749909b8ff1f1555e1af2f992abe6b44b2702d75 Mon Sep 17 00:00:00 2001 From: Brian Jacobel Date: Thu, 5 Jan 2017 11:55:15 -0500 Subject: [PATCH 5/5] Remove unused a11y functionality for prev/next buttons --- .../xmodule/js/src/sequence/display.js | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/common/lib/xmodule/xmodule/js/src/sequence/display.js b/common/lib/xmodule/xmodule/js/src/sequence/display.js index e914595b3b..802b96dcac 100644 --- a/common/lib/xmodule/xmodule/js/src/sequence/display.js +++ b/common/lib/xmodule/xmodule/js/src/sequence/display.js @@ -100,16 +100,14 @@ * 'problem_id' is problem id. * 'new_content_state' is the updated content of the problem. * 'new_state' is the updated state of the problem. - * initialize for the current sequence if there isn't any updated problem - * for this position. */ + // initialize for the current sequence if there isn't any updated problem for this position. if (!this.anyUpdatedProblems(this.position)) { this.updatedProblems[this.position] = {}; } // Now, put problem content and score against problem id for current active sequence. - this.updatedProblems[this.position][problemId] = [newContentState, newState]; }; @@ -133,19 +131,10 @@ this.$(buttonClass).addClass('disabled').attr('disabled', true); }; - Sequence.prototype.setButtonLabel = function(buttonClass, buttonLabel) { - this.$(buttonClass + ' .sr').html(buttonLabel); - }; - - Sequence.prototype.updateButtonState = function( - buttonClass, buttonAction, actionLabelPrefix, isAtBoundary, boundaryUrl - ) { - var buttonLabel; + Sequence.prototype.updateButtonState = function(buttonClass, buttonAction, isAtBoundary, boundaryUrl) { if (isAtBoundary && boundaryUrl === 'None') { this.disableButton(buttonClass); } else { - buttonLabel = actionLabelPrefix + (isAtBoundary ? ' Subsection' : ' Unit'); - this.setButtonLabel(buttonClass, buttonLabel); this.enableButton(buttonClass, buttonAction); } }; @@ -158,13 +147,13 @@ // previous button isFirstTab = this.position === 1; previousButtonClass = '.sequence-nav-button.button-previous'; - this.updateButtonState(previousButtonClass, this.selectPrevious, 'Previous', isFirstTab, this.prevUrl); + this.updateButtonState(previousButtonClass, this.selectPrevious, isFirstTab, this.prevUrl); // next button // use inequality in case contents.length is 0 and position is 1. isLastTab = this.position >= this.contents.length; nextButtonClass = '.sequence-nav-button.button-next'; - this.updateButtonState(nextButtonClass, this.selectNext, 'Next', isLastTab, this.nextUrl); + this.updateButtonState(nextButtonClass, this.selectNext, isLastTab, this.nextUrl); }; Sequence.prototype.render = function(newPosition) {