Commit 6f34bb1d by Diana Huang

Merge pull request #1468 from edx/diana/navigation-skip-links

Add skip links to both CMS and LMS
parents 71ece15c f2f067a3
......@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected.
Common: Add skip links for accessibility to CMS and LMS (LMS-1311)
Studio: Change course overview page, checklists, assets, and course staff management
page URLs to a RESTful interface. Also removed "\listing", which duplicated
"\index".
......
......@@ -30,6 +30,7 @@ requirejs.config({
"xmodule": "xmodule_js/src/xmodule",
"xblock": "xmodule_js/common_static/coffee/src/xblock",
"utility": "xmodule_js/common_static/js/src/utility",
"accessibility": "xmodule_js/common_static/js/src/accessibility_tools",
"sinon": "xmodule_js/common_static/js/vendor/sinon-1.7.1",
"squire": "xmodule_js/common_static/js/vendor/Squire",
"jasmine-jquery": "xmodule_js/common_static/js/vendor/jasmine-jquery",
......
......@@ -826,10 +826,32 @@ hr.divide {
word-wrap: break-word;
}
// ui - semantic + visual divider
hr.divider {
@extend %cont-text-sr;
}
// ui - skipnav
.nav-skip {
@include font-size(13);
display: block;
position: absolute;
left: 0px;
top: -($baseline*30);
width: 1px;
height: 1px;
overflow: hidden;
background: $white;
border-bottom: 1px solid $gray-l4;
padding: ($baseline*0.75) ($baseline/2);
&:focus, &:active {
position: static;
width: auto;
height: auto;
}
}
// ====================
// js dependant
......
## -*- coding: utf-8 -*-
<%! from django.utils.translation import ugettext as _ %>
<%namespace name='static' file='static_content.html'/>
<!doctype html>
......@@ -29,211 +30,209 @@
</head>
<body class="<%block name='bodyclass'></%block> hide-wip">
<script type="text/javascript">
window.baseUrl = "${settings.STATIC_URL}";
var require = {
baseUrl: baseUrl,
waitSeconds: 60,
paths: {
"domReady": "js/vendor/domReady",
"gettext": "/i18n",
"mustache": "js/vendor/mustache",
"codemirror": "js/vendor/codemirror-compressed",
"codemirror/stex": "js/vendor/CodeMirror/stex",
"jquery": "js/vendor/jquery.min",
"jquery.ui": "js/vendor/jquery-ui.min",
"jquery.form": "js/vendor/jquery.form",
"jquery.markitup": "js/vendor/markitup/jquery.markitup",
"jquery.leanModal": "js/vendor/jquery.leanModal.min",
"jquery.ajaxQueue": "js/vendor/jquery.ajaxQueue",
"jquery.smoothScroll": "js/vendor/jquery.smooth-scroll.min",
"jquery.timepicker": "js/vendor/timepicker/jquery.timepicker",
"jquery.cookie": "js/vendor/jquery.cookie",
"jquery.qtip": "js/vendor/jquery.qtip.min",
"jquery.scrollTo": "js/vendor/jquery.scrollTo-1.4.2-min",
"jquery.flot": "js/vendor/flot/jquery.flot.min",
"jquery.maskedinput": "js/vendor/jquery.maskedinput.min",
"jquery.fileupload": "js/vendor/jQuery-File-Upload/js/jquery.fileupload",
"jquery.iframe-transport": "js/vendor/jQuery-File-Upload/js/jquery.iframe-transport",
"jquery.inputnumber": "js/vendor/html5-input-polyfills/number-polyfill",
"jquery.immediateDescendents": "coffee/src/jquery.immediateDescendents",
"datepair": "js/vendor/timepicker/datepair",
"date": "js/vendor/date",
"tzAbbr": "js/vendor/tzAbbr",
"underscore": "js/vendor/underscore-min",
"underscore.string": "js/vendor/underscore.string.min",
"backbone": "js/vendor/backbone-min",
"backbone.associations": "js/vendor/backbone-associations-min",
"tinymce": "js/vendor/tiny_mce/tiny_mce",
"jquery.tinymce": "js/vendor/tiny_mce/jquery.tinymce",
"xmodule": "/xmodule/xmodule",
"xblock": "coffee/src/xblock",
"utility": "js/src/utility",
"draggabilly": "js/vendor/draggabilly.pkgd",
<a class="nav-skip" href="#content">${_("Skip to this view's content")}</a>
// externally hosted files
"tender": "//edxedge.tenderapp.com/tender_widget",
"mathjax": "//edx-static.s3.amazonaws.com/mathjax-MathJax-727332c/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured",
// youtube URL does not end in ".js". We add "?noext" to the path so
// that require.js adds the ".js" to the query component of the URL,
// and leaves the path component intact.
"youtube": "//www.youtube.com/player_api?noext"
},
shim: {
"gettext": {
exports: "gettext"
},
"date": {
exports: "Date"
},
"jquery.ui": {
deps: ["jquery"],
exports: "jQuery.ui"
},
"jquery.form": {
deps: ["jquery"],
exports: "jQuery.fn.ajaxForm"
},
"jquery.markitup": {
deps: ["jquery"],
exports: "jQuery.fn.markitup"
},
"jquery.leanmodal": {
deps: ["jquery"],
exports: "jQuery.fn.leanModal"
},
"jquery.ajaxQueue": {
deps: ["jquery"],
exports: "jQuery.fn.ajaxQueue"
},
"jquery.smoothScroll": {
deps: ["jquery"],
exports: "jQuery.fn.smoothScroll"
},
"jquery.cookie": {
deps: ["jquery"],
exports: "jQuery.fn.cookie"
},
"jquery.qtip": {
deps: ["jquery"],
exports: "jQuery.fn.qtip"
},
"jquery.scrollTo": {
deps: ["jquery"],
exports: "jQuery.fn.scrollTo",
},
"jquery.flot": {
deps: ["jquery"],
exports: "jQuery.fn.plot"
},
"jquery.maskedinput": {
deps: ["jquery"],
exports: "jQuery.fn.mask"
},
"jquery.fileupload": {
deps: ["jquery.iframe-transport"],
exports: "jQuery.fn.fileupload"
},
"jquery.inputnumber": {
deps: ["jquery"],
exports: "jQuery.fn.inputNumber"
},
"jquery.tinymce": {
deps: ["jquery", "tinymce"],
exports: "jQuery.fn.tinymce"
},
"datepair": {
deps: ["jquery.ui", "jquery.timepicker"]
},
"underscore": {
exports: "_"
},
"backbone": {
deps: ["underscore", "jquery"],
exports: "Backbone"
},
"backbone.associations": {
deps: ["backbone"],
exports: "Backbone.Associations"
},
"youtube": {
exports: "YT"
},
"codemirror": {
exports: "CodeMirror"
},
"codemirror/stex": {
deps: ["codemirror"]
},
"tinymce": {
exports: "tinymce"
<script type="text/javascript">
window.baseUrl = "${settings.STATIC_URL}";
var require = {
baseUrl: baseUrl,
waitSeconds: 60,
paths: {
"domReady": "js/vendor/domReady",
"gettext": "/i18n",
"mustache": "js/vendor/mustache",
"codemirror": "js/vendor/codemirror-compressed",
"codemirror/stex": "js/vendor/CodeMirror/stex",
"jquery": "js/vendor/jquery.min",
"jquery.ui": "js/vendor/jquery-ui.min",
"jquery.form": "js/vendor/jquery.form",
"jquery.markitup": "js/vendor/markitup/jquery.markitup",
"jquery.leanModal": "js/vendor/jquery.leanModal.min",
"jquery.ajaxQueue": "js/vendor/jquery.ajaxQueue",
"jquery.smoothScroll": "js/vendor/jquery.smooth-scroll.min",
"jquery.timepicker": "js/vendor/timepicker/jquery.timepicker",
"jquery.cookie": "js/vendor/jquery.cookie",
"jquery.qtip": "js/vendor/jquery.qtip.min",
"jquery.scrollTo": "js/vendor/jquery.scrollTo-1.4.2-min",
"jquery.flot": "js/vendor/flot/jquery.flot.min",
"jquery.fileupload": "js/vendor/jQuery-File-Upload/js/jquery.fileupload",
"jquery.iframe-transport": "js/vendor/jQuery-File-Upload/js/jquery.iframe-transport",
"jquery.inputnumber": "js/vendor/html5-input-polyfills/number-polyfill",
"jquery.immediateDescendents": "coffee/src/jquery.immediateDescendents",
"datepair": "js/vendor/timepicker/datepair",
"date": "js/vendor/date",
"tzAbbr": "js/vendor/tzAbbr",
"underscore": "js/vendor/underscore-min",
"underscore.string": "js/vendor/underscore.string.min",
"backbone": "js/vendor/backbone-min",
"backbone.associations": "js/vendor/backbone-associations-min",
"tinymce": "js/vendor/tiny_mce/tiny_mce",
"jquery.tinymce": "js/vendor/tiny_mce/jquery.tinymce",
"xmodule": "/xmodule/xmodule",
"xblock": "coffee/src/xblock",
"utility": "js/src/utility",
"accessibility": "js/src/accessibility_tools",
"draggabilly": "js/vendor/draggabilly.pkgd",
// externally hosted files
"tender": "//edxedge.tenderapp.com/tender_widget",
"mathjax": "//edx-static.s3.amazonaws.com/mathjax-MathJax-727332c/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured",
// youtube URL does not end in ".js". We add "?noext" to the path so
// that require.js adds the ".js" to the query component of the URL,
// and leaves the path component intact.
"youtube": "//www.youtube.com/player_api?noext"
},
"mathjax": {
exports: "MathJax",
init: function() {
MathJax.Hub.Config({
tex2jax: {
inlineMath: [
["\\(","\\)"],
['[mathjaxinline]','[/mathjaxinline]']
],
displayMath: [
["\\[","\\]"],
['[mathjax]','[/mathjax]']
]
shim: {
"gettext": {
exports: "gettext"
},
"date": {
exports: "Date"
},
"jquery.ui": {
deps: ["jquery"],
exports: "jQuery.ui"
},
"jquery.form": {
deps: ["jquery"],
exports: "jQuery.fn.ajaxForm"
},
"jquery.markitup": {
deps: ["jquery"],
exports: "jQuery.fn.markitup"
},
"jquery.leanmodal": {
deps: ["jquery"],
exports: "jQuery.fn.leanModal"
},
"jquery.ajaxQueue": {
deps: ["jquery"],
exports: "jQuery.fn.ajaxQueue"
},
"jquery.smoothScroll": {
deps: ["jquery"],
exports: "jQuery.fn.smoothScroll"
},
"jquery.cookie": {
deps: ["jquery"],
exports: "jQuery.fn.cookie"
},
"jquery.qtip": {
deps: ["jquery"],
exports: "jQuery.fn.qtip"
},
"jquery.scrollTo": {
deps: ["jquery"],
exports: "jQuery.fn.scrollTo",
},
"jquery.flot": {
deps: ["jquery"],
exports: "jQuery.fn.plot"
},
"jquery.fileupload": {
deps: ["jquery.iframe-transport"],
exports: "jQuery.fn.fileupload"
},
"jquery.inputnumber": {
deps: ["jquery"],
exports: "jQuery.fn.inputNumber"
},
"jquery.tinymce": {
deps: ["jquery", "tinymce"],
exports: "jQuery.fn.tinymce"
},
"datepair": {
deps: ["jquery.ui", "jquery.timepicker"]
},
"underscore": {
exports: "_"
},
"backbone": {
deps: ["underscore", "jquery"],
exports: "Backbone"
},
"backbone.associations": {
deps: ["backbone"],
exports: "Backbone.Associations"
},
"youtube": {
exports: "YT"
},
"codemirror": {
exports: "CodeMirror"
},
"codemirror/stex": {
deps: ["codemirror"]
},
"tinymce": {
exports: "tinymce"
},
"mathjax": {
exports: "MathJax",
init: function() {
MathJax.Hub.Config({
tex2jax: {
inlineMath: [
["\\(","\\)"],
['[mathjaxinline]','[/mathjaxinline]']
],
displayMath: [
["\\[","\\]"],
['[mathjax]','[/mathjax]']
]
}
});
MathJax.Hub.Configured();
}
});
MathJax.Hub.Configured();
}
},
"xblock/core": {
exports: "XBlock",
deps: ["jquery", "jquery.immediateDescendents"]
},
"xblock/runtime.v1": {
exports: "XBlock",
deps: ["xblock/core"]
},
},
"xblock/core": {
exports: "XBlock",
deps: ["jquery", "jquery.immediateDescendents"]
},
"xblock/runtime.v1": {
exports: "XBlock",
deps: ["xblock/core"]
},
"coffee/src/main": {
deps: ["coffee/src/ajax_prefix"]
"coffee/src/main": {
deps: ["coffee/src/ajax_prefix"]
},
"coffee/src/logger": {
exports: "Logger",
deps: ["coffee/src/ajax_prefix"]
}
},
"coffee/src/logger": {
exports: "Logger",
deps: ["coffee/src/ajax_prefix"]
// load jquery and gettext automatically
deps: ["jquery", "gettext"],
callback: function() {
// load other scripts on every page, after jquery loads
require(["js/base", "coffee/src/main", "coffee/src/logger", "datepair", "accessibility"]);
// we need "datepair" because it dynamically modifies the page
// when it is loaded -- yuck!
}
},
// load jquery and gettext automatically
deps: ["jquery", "gettext"],
callback: function() {
// load other scripts on every page, after jquery loads
require(["js/base", "coffee/src/main", "coffee/src/logger", "datepair"]);
// we need "datepair" because it dynamically modifies the page
// when it is loaded -- yuck!
}
};
</script>
<script type="text/javascript" src="${static.url("js/vendor/require.js")}"></script>
};
</script>
<script type="text/javascript" src="${static.url("js/vendor/require.js")}"></script>
## js templates
<script id="system-feedback-tpl" type="text/template">
<%static:include path="js/system-feedback.underscore" />
</script>
## js templates
<script id="system-feedback-tpl" type="text/template">
<%static:include path="js/system-feedback.underscore" />
</script>
% if context_course:
<script type="text/javascript">
require(['js/models/course'], function(Course) {
window.course = new Course({
id: "${context_course.id}",
name: "${context_course.display_name_with_default | h}",
url_name: "${context_course.location.name | h}",
org: "${context_course.location.org | h}",
num: "${context_course.location.course | h}",
revision: "${context_course.location.revision | h}"
<script type="text/javascript">
require(['js/models/course'], function(Course) {
window.course = new Course({
id: "${context_course.id}",
name: "${context_course.display_name_with_default | h}",
url_name: "${context_course.location.name | h}",
org: "${context_course.location.org | h}",
num: "${context_course.location.course | h}",
revision: "${context_course.location.revision | h}"
});
});
});
</script>
</script>
% endif
<!-- view -->
......@@ -242,7 +241,9 @@ require(['js/models/course'], function(Course) {
<div id="page-alert"></div>
<div id="content">
<%block name="content"></%block>
</div>
% if user.is_authenticated():
<script type="text/javascript">
......
......@@ -109,3 +109,23 @@ var accessible_modal = function(trigger, closeButtonId, modalId, mainPageId) {
});
});
};
// NOTE: This is a gross hack to make the skip links work for Webkit browsers
// see http://stackoverflow.com/questions/6280399/skip-links-not-working-in-chrome/12720183#12720183
// handle things properly for clicks
$('.nav-skip').click(function() {
var href = $(this).attr('href');
if(href) {
$(href).attr('tabIndex', -1).focus();
}
});
// and for the enter key
$('.nav-skip').keypress(function(e) {
if(e.which == 13) {
var href = $(this).attr('href');
if(href) {
$(href).attr('tabIndex', -1).focus();
}
}
});
......@@ -69,6 +69,6 @@ spec_paths:
# You can access these within JavaScript code
# at the URL: document.location.href + "/include/"
# plus the path to the file (relative to this YAML file)
#fixture_paths:
# - path/to/fixture
fixture_paths:
- js/fixtures
......@@ -285,3 +285,23 @@ mark {
#feedback_form textarea[name="details"] {
height: 150px;
}
// ui - skipnav
.nav-skip {
display: block;
position: absolute;
left: 0px;
top:- ($baseline*30);
width: 1px;
height: 1px;
overflow: hidden;
background: $white;
border-bottom: 1px solid $border-color-4;
padding: ($baseline*0.75) ($baseline/2);
&:focus, &:active {
position: static;
width: auto;
height: auto;
}
}
......@@ -78,6 +78,8 @@
</head>
<body class="<%block name='bodyclass'/>">
<a class="nav-skip" href="#content">${_("Skip to this view's content")}</a>
<%include file="mathjax_accessible.html" />
% if theme_enabled():
......@@ -86,7 +88,7 @@
<%include file="navigation.html" />
% endif
<section class="content-wrapper">
<section class="content-wrapper" id="content">
${self.body()}
<%block name="bodyextra"/>
</section>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment