XSS linting, fixes CNVS-17663

no significant issues/vulnerabilities fixed in this commit, rather this is
a catch-all so we can enforce linting in the build.

to better understand what's going on here, see the following:

* public/javascripts/.xssignore
* script/xsslint.js
* https://github.com/jenseng/xsslint

high level summary of what's going on in this commit:

1. .html/.append/etc. now know what to do with a SafeString ... in many
   cases we now put a $.raw around an .html argument to tell the linter
   it's ok
2. although translation is an unlikely attack vector, we now htmlEscape
   I18n.t calls used in html snippets, etc. this is a good thing, as it
   ensures we don't create a vulnerability later (e.g. by interpolating
   user content into a translation)
3. many vars were renamed (Html suffix, $ prefix) to let the linter know
   it's something that was manually vetted
4. in some cases, rather than renaming or creating a superfluous var,
   we add special xsslint comment overrides

test plan:
specs should all pass

Change-Id: Ide1df825b798d1b0c468a5308802543bf716c0d7
Reviewed-on: https://gerrit.instructure.com/46097
Tested-by: Jenkins <jenkins@instructure.com>
QA-Review: Derek Hansen <dhansen@instructure.com>
Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
Product-Review: Jeremy Stanley <jeremy@instructure.com>
This commit is contained in:
Jon Jensen 2014-12-19 02:35:02 -07:00
parent 426fc9b1e8
commit 70cdc92bdb
135 changed files with 761 additions and 559 deletions

View File

@ -31,7 +31,7 @@
# PaginatedList expects an empty <ul> wrapped in a <div>. The element
# passed to the constructor should be the <div>.
define ['jquery', 'i18n!paginated_list', 'vendor/spin'], ($, I18n, Spinner) ->
define ['jquery', 'i18n!paginated_list', 'vendor/spin', 'str/htmlEscape'], ($, I18n, Spinner, htmlEscape) ->
class PaginatedList
##
# I18n keys used by class
@ -132,11 +132,11 @@ define ['jquery', 'i18n!paginated_list', 'vendor/spin'], ($, I18n, Spinner) ->
##
# animate loaded results
# @api private
animateInResults: (results) ->
animateInResults: ($results) ->
empty = @el.list.children().length == 0
results.css('display', 'none')
@el.list.append results
results.slideDown()
$results.css('display', 'none')
@el.list.append $results
$results.slideDown()
##
# keep track of what page we're on. when the last page
@ -146,7 +146,7 @@ define ['jquery', 'i18n!paginated_list', 'vendor/spin'], ($, I18n, Spinner) ->
if @hasNextPage()
@options.requestParams.page++
unless @pageLinkPresent
@el.wrapper.append @viewMoreLink()
@el.wrapper.append @viewMoreLinkHtml()
@pageLinkPresent = true
else
@el.wrapper.find('.view-more-link').remove()
@ -162,12 +162,12 @@ define ['jquery', 'i18n!paginated_list', 'vendor/spin'], ($, I18n, Spinner) ->
# template for view more link
# @return String
# @api private
viewMoreLink: ->
'<a class="view-more-link" href="#">' + @keys.viewMore + '</a>'
viewMoreLinkHtml: ->
'<a class="view-more-link" href="#">' + htmlEscape(@keys.viewMore) + '</a>'
##
# template for no results notification
# @return String
# @api private
noResults: ->
@el.list.append "<li>#{@keys.noResults}</li>"
@el.list.append "<li>#{htmlEscape @keys.noResults}</li>"

View File

@ -5,7 +5,12 @@ define [
'underscore'
'jquery'
'compiled/behaviors/authenticity_token'
], (Backbone, _, $, authenticity_token) ->
'str/htmlEscape'
], (Backbone, _, $, authenticity_token, htmlEscape) ->
###
xsslint safeString.identifier iframeId httpMethod
xsslint jqueryObject.identifier el
###
Backbone.syncWithoutMultipart = Backbone.sync
Backbone.syncWithMultipart = (method, model, options) ->
@ -35,7 +40,7 @@ define [
$el[0]
_.flatten(inputs)
$form = $("""
<form enctype='multipart/form-data' target='#{iframeId}' action='#{options.url ? model.url()}' method='POST'>
<form enctype='multipart/form-data' target='#{iframeId}' action='#{htmlEscape options.url ? model.url()}' method='POST'>
</form>
""").hide()
@ -43,7 +48,7 @@ define [
if options.proxyAttachment
$form.prepend """
<input type='hidden' name='_method' value='#{httpMethod}' />
<input type='hidden' name='authenticity_token' value='#{authenticity_token()}' />
<input type='hidden' name='authenticity_token' value='#{htmlEscape authenticity_token()}' />
"""
_.each toForm(model.toJSON()), (el) ->

View File

@ -250,6 +250,10 @@ define [
#
# @api private
###
xsslint safeString.method format
###
createBindings: (index, el) =>
@$('[data-bind]').each (index, el) =>
$el = $ el

View File

@ -4,5 +4,5 @@ define ['jquery'], ($) ->
for type, unread of ENV.badge_counts
if unread > 0
type = "grades" if type is "submissions"
$badge = $("<b/>").append(unread).addClass("nav-badge")
$badge = $("<b/>").text(unread).addClass("nav-badge")
$("#section-tabs .#{type}").append($badge)

View File

@ -231,6 +231,9 @@ define [
$course_syllabus.loadingImage()
success: (data) ->
###
xsslint safeString.property syllabus_body
###
$course_syllabus.loadingImage('remove').html data.course.syllabus_body
$course_syllabus.data('syllabus_body', data.course.syllabus_body)
$course_syllabus_details.hide()

View File

@ -1,14 +1,14 @@
define ['jquery', 'INST'], ($, INST) ->
link = null
$link = null
set = (env) ->
link.remove() if link
$link.remove() if $link
favicon = if env == 'development'
'/favicon-green.ico'
else if env == 'test'
'/favicon-yellow.ico'
else
'/favicon.ico'
link = $('<link />').attr(rel: 'icon', type: 'image/x-icon', href: favicon)
$(document.head).append(link)
$link = $('<link />').attr(rel: 'icon', type: 'image/x-icon', href: favicon)
$(document.head).append($link)
set(INST?.environment)
set

View File

@ -1,7 +1,7 @@
# copied from
# https://github.com/rails/jquery-ujs
define ['jquery', 'compiled/behaviors/authenticity_token'], ($, authenticity_token) ->
define ['jquery', 'compiled/behaviors/authenticity_token', 'str/htmlEscape'], ($, authenticity_token, htmlEscape) ->
##
# Handles "data-method" on links such as:
@ -11,12 +11,12 @@ define ['jquery', 'compiled/behaviors/authenticity_token'], ($, authenticity_tok
href = link.data('url') || link.attr('href')
method = link.data('method')
target = link.attr('target')
form = $("<form method='post' action='#{href}'></form>")
metadataInput = "<input name='_method' value='#{method }' type='hidden' />"
metadataInput += "<input name='authenticity_token' type='hidden' />"
form = $("<form method='post' action='#{htmlEscape href}'></form>")
metadataInputHtml = "<input name='_method' value='#{htmlEscape method}' type='hidden' />"
metadataInputHtml += "<input name='authenticity_token' type='hidden' />"
form.attr('target', target) if target
form.hide().append(metadataInput).appendTo('body').submit()
form.hide().append(metadataInputHtml).appendTo('body').submit()
# For 'data-confirm' attribute:

View File

@ -11,8 +11,8 @@ require [
if ENV.DASHBOARD_SIDEBAR_URL
rightSide = $('#right-side')
rightSide.disableWhileLoading(
$.get ENV.DASHBOARD_SIDEBAR_URL , (data) ->
rightSide.html data
$.get ENV.DASHBOARD_SIDEBAR_URL , (html) ->
rightSide.html html
newCourseForm()
)

View File

@ -39,9 +39,9 @@ require [
$link.hide()
$.ajaxJSON $(this).attr("href"), "GET", {}, (data) ->
$("#home_page").loadingImage "remove"
body = htmlEscape($.trim(data.wiki_page.body))
body = htmlEscape(I18n.t("empty_body", "No Content")) if body.length is 0
$("#home_page_content").html body
bodyHtml = htmlEscape($.trim(data.wiki_page.body))
bodyHtml = htmlEscape(I18n.t("empty_body", "No Content")) if bodyHtml.length is 0
$("#home_page_content").html bodyHtml
$("html,body").scrollTo $("#home_page")
$(".dashboard_view_link").click (event) ->

View File

@ -7,7 +7,7 @@ require [
showAllArtifacts.click (event) ->
event.preventDefault()
$("tr.artifact_details").toggle()
if showAllArtifacts.html() is I18n.t("#buttons.show_all_artifacts", "Show All Artifacts")
showAllArtifacts.html I18n("#buttons.hide_all_artifacts", "Hide All Artifacts")
if showAllArtifacts.text() is I18n.t("#buttons.show_all_artifacts", "Show All Artifacts")
showAllArtifacts.text I18n("#buttons.hide_all_artifacts", "Hide All Artifacts")
else
showAllArtifacts.html I18n.t("#buttons.show_all_artifacts", "Show All Artifacts")
showAllArtifacts.text I18n.t("#buttons.show_all_artifacts", "Show All Artifacts")

View File

@ -80,10 +80,10 @@ require [
@$linkFields ?= @$ '#profile_link_fields'
$row = $ """
<tr>
<td><input aria-label="#{I18n.t("Link title")}" type="text" maxlength="255" name="link_titles[]" value="#{htmlEscape title}"></td>
<td><input aria-label="#{htmlEscape I18n.t("Link title")}" type="text" maxlength="255" name="link_titles[]" value="#{htmlEscape title}"></td>
<td></td>
<td><input aria-label="#{I18n.t("Link Url")}" type="text" name="link_urls[]" value="#{htmlEscape url}"></td>
<td><a href="#" data-event="removeLinkRow"><span class="screenreader-only">#{I18n.t("Remove")}</span><i class="icon-end"></i></a></td>
<td><input aria-label="#{htmlEscape I18n.t("Link Url")}" type="text" name="link_urls[]" value="#{htmlEscape url}"></td>
<td><a href="#" data-event="removeLinkRow"><span class="screenreader-only">#{htmlEscape I18n.t("Remove")}</span><i class="icon-end"></i></a></td>
</tr>
"""
@$linkFields.append $row

View File

@ -20,8 +20,8 @@ require [
if ENV.SUBMISSION_VERSIONS_URL && !ENV.IS_SURVEY
versions = $("#quiz-submission-version-table")
versions.css(height: "100px")
dfd = $.get ENV.SUBMISSION_VERSIONS_URL, (data) ->
versions.html(data)
dfd = $.get ENV.SUBMISSION_VERSIONS_URL, (html) ->
versions.html(html)
versions.css(height: "auto")
versions.disableWhileLoading(dfd)

View File

@ -1,12 +1,13 @@
require [
'i18n!zip_file_imports' # I18n
'jquery' # $
'str/htmlEscape'
'jquery.ajaxJSON' # getJSON
'jquery.instructure_forms' # ajaxJSONFiles
'jqueryui/dialog'
'compiled/jquery.rails_flash_notifications'
'jqueryui/progressbar' # /\.progressbar/
], (I18n, $) ->
], (I18n, $, htmlEscape) ->
$(document).ready ->
$zipFile = $("#zip_file_import_form #zip_file")
@ -73,7 +74,7 @@ require [
importFailed []
else if zfi.workflow_state == 'imported'
$("#uploading_progressbar").progressbar 'value', 100
$("#uploading_please_wait_dialog").prepend I18n.t('notices.uploading_complete', "Uploading complete!")
$("#uploading_please_wait_dialog").prepend htmlEscape I18n.t('notices.uploading_complete', "Uploading complete!")
location.href = $("#return_to").val()
else
pollImport.errorCount = 0

View File

@ -156,7 +156,7 @@ define [
dayClick: @dayClick
addEventClick: @addEventClick
titleFormat:
week: "MMM d[ yyyy]{ '&ndash;'[ MMM] d, yyyy}"
week: "MMM d[ yyyy]{ ''[ MMM] d, yyyy}"
viewDisplay: @viewDisplay
windowResize: @windowResize
drop: @drop
@ -275,8 +275,8 @@ define [
I18n.t('event_event_title', 'Event Title:')
$element.attr('title', $.trim("#{timeString}\n#{$element.find('.fc-event-title').text()}\n\n#{I18n.t('calendar_title', 'Calendar:')} #{htmlEscape(event.contextInfo.name)}"))
$element.find('.fc-event-inner').prepend($("<span class='screenreader-only'>#{I18n.t('calendar_title', 'Calendar:')} #{htmlEscape(event.contextInfo.name)}</span>"))
$element.find('.fc-event-title').prepend($("<span class='screenreader-only'>#{screenReaderTitleHint} </span>"))
$element.find('.fc-event-inner').prepend($("<span class='screenreader-only'>#{htmlEscape I18n.t('calendar_title', 'Calendar:')} #{htmlEscape(event.contextInfo.name)}</span>"))
$element.find('.fc-event-title').prepend($("<span class='screenreader-only'>#{htmlEscape screenReaderTitleHint} </span>"))
$element.find('.fc-event-title').toggleClass('calendar__event--completed', event.isCompleted())
element.find('.fc-event-inner').prepend($('<i />', {'class': "icon-#{event.iconType()}"}))
true
@ -408,20 +408,20 @@ define [
drawNowLine: =>
return unless @currentView == 'week'
if !@nowLine
@nowLine = $('<div />', {'class': 'calendar-nowline'})
$('.fc-agenda-slots').parent().append(@nowLine)
if !@$nowLine
@$nowLine = $('<div />', {'class': 'calendar-nowline'})
$('.fc-agenda-slots').parent().append(@$nowLine)
now = $.fudgeDateForProfileTimezone(new Date)
midnight = new Date(now.getTime())
midnight.setHours(0, 0, 0)
seconds = (now.getTime() - midnight.getTime())/1000
@nowLine.toggle(@isSameWeek(@getCurrentDate(), now))
@$nowLine.toggle(@isSameWeek(@getCurrentDate(), now))
@nowLine.css('width', $('.fc-agenda-slots .fc-widget-content:first').css('width'))
@$nowLine.css('width', $('.fc-agenda-slots .fc-widget-content:first').css('width'))
secondHeight = $('.fc-agenda-slots').css('height').replace('px', '')/24/3600
@nowLine.css('top', seconds*secondHeight + 'px')
@$nowLine.css('top', seconds*secondHeight + 'px')
setDateTitle: (title) =>
@header.setHeaderText(title)
@ -642,7 +642,7 @@ define [
@agenda.fetch(@visibleContextList, start)
renderDateRange: (start, end) =>
@setDateTitle(I18n.l('#date.formats.medium', start)+' &ndash; '+I18n.l('#date.formats.medium', end))
@setDateTitle(I18n.l('#date.formats.medium', start)+' '+I18n.l('#date.formats.medium', end))
# for "load more" with voiceover, we want the alert to happen later so
# the focus change doesn't interrupt it.
window.setTimeout =>
@ -671,15 +671,16 @@ define [
colorizeContexts: =>
colors = colorSlicer.getColors(@contextCodes.length, 275, {unsafe: !ENV.SETTINGS.use_high_contrast})
html = for contextCode, index in @contextCodes
html = (for contextCode, index in @contextCodes
color = colors[index]
".group_#{contextCode}{
color: #{color};
border-color: #{color};
background-color: #{color};
}"
).join('')
$styleContainer.html "<style>#{html.join('')}</style>"
$styleContainer.html "<style>#{html}</style>"
dataFromDocumentHash: () =>
data = {}

View File

@ -13,24 +13,24 @@ define [
constructor: (selector, @event, @contextChangeCB, @closeCB) ->
@currentContextInfo = null
tpl = if @event.override then editAssignmentOverrideTemplate else editAssignmentTemplate
@form = $(tpl({
@$form = $(tpl({
title: @event.title
contexts: @event.possibleContexts()
}))
$(selector).append @form
$(selector).append @$form
@setupTimeAndDatePickers()
@form.submit @formSubmit
@form.find(".more_options_link").click @moreOptionsClick
@form.find("select.context_id").change @contextChange
@form.find("select.context_id").triggerHandler('change', false)
@$form.submit @formSubmit
@$form.find(".more_options_link").click @moreOptionsClick
@$form.find("select.context_id").change @contextChange
@$form.find("select.context_id").triggerHandler('change', false)
# Hide the context selector completely if this is an existing event, since it can't be changed.
if !@event.isNewEvent()
@form.find(".context_select").hide()
@form.attr('method', 'PUT')
@form.attr('action', $.replaceTags(@event.contextInfo.assignment_url, 'id', @event.object.id))
@$form.find(".context_select").hide()
@$form.attr('method', 'PUT')
@$form.attr('action', $.replaceTags(@event.contextInfo.assignment_url, 'id', @event.object.id))
contextInfoForCode: (code) ->
for context in @event.possibleContexts()
@ -39,14 +39,14 @@ define [
return null
activate: () =>
@form.find("select.context_id").change()
@$form.find("select.context_id").change()
if @event.assignment?.assignment_group_id
@form.find(".assignment_group_select .assignment_group").val(@event.assignment.assignment_group_id)
@$form.find(".assignment_group_select .assignment_group").val(@event.assignment.assignment_group_id)
moreOptionsClick: (jsEvent) =>
jsEvent.preventDefault()
pieces = $(jsEvent.target).attr('href').split("#")
data = @form.getFormData( object_name: 'assignment' )
data = @$form.getFormData( object_name: 'assignment' )
params = {}
if data.title then params['title'] = data.title
if data.due_at then params['due_at'] = data.due_at
@ -56,7 +56,7 @@ define [
window.location.href = pieces.join("#")
setContext: (newContext) =>
@form.find("select.context_id").val(newContext).triggerHandler('change', false)
@$form.find("select.context_id").val(newContext).triggerHandler('change', false)
contextChange: (jsEvent, propagate) =>
return if @ignoreContextChange
@ -72,30 +72,30 @@ define [
# TODO: support adding a new assignment group from this select box
assignmentGroupsSelectOptionsInfo =
collection: @currentContextInfo.assignment_groups
@form.find(".assignment_group").html(genericSelectOptionsTemplate(assignmentGroupsSelectOptionsInfo))
@$form.find(".assignment_group").html(genericSelectOptionsTemplate(assignmentGroupsSelectOptionsInfo))
# Update the edit and more options links with the new context
@form.attr('action', @currentContextInfo.create_assignment_url)
@$form.attr('action', @currentContextInfo.create_assignment_url)
moreOptionsUrl = if @event.assignment
"#{@event.assignment.html_url}/edit"
else
@currentContextInfo.new_assignment_url
@form.find(".more_options_link").attr('href', moreOptionsUrl)
@$form.find(".more_options_link").attr('href', moreOptionsUrl)
setupTimeAndDatePickers: () =>
@form.find(".datetime_field").datetime_field()
@$form.find(".datetime_field").datetime_field()
startDate = @event.startDate()
endDate = @event.endDate()
if @event.allDay
@form.find(".datetime_field").val(startDate.toString('MMM d, yyyy')).change()
@$form.find(".datetime_field").val(startDate.toString('MMM d, yyyy')).change()
else if startDate
@form.find(".datetime_field").val(startDate.toString('MMM d, yyyy h:mmtt')).change()
@$form.find(".datetime_field").val(startDate.toString('MMM d, yyyy h:mmtt')).change()
formSubmit: (e) =>
e.preventDefault()
form = @form.getFormData()
form = @$form.getFormData()
if form['assignment[due_at]']? then @submitAssignment(form) else @submitOverride(form)
submitAssignment: (form) ->
@ -104,12 +104,12 @@ define [
if dueAtString == ''
dueAt = null
else
dueAt = @form.find("#assignment_due_at").data('date')
dueAt = @$form.find("#assignment_due_at").data('date')
params = {
'assignment[name]': @form.find("#assignment_title").val()
'assignment[published]': @form.find("#assignment_published").val() if @form.find("#assignment_published").is(':checked')
'assignment[name]': @$form.find("#assignment_title").val()
'assignment[published]': @$form.find("#assignment_published").val() if @$form.find("#assignment_published").is(':checked')
'assignment[due_at]': if dueAt then $.unfudgeDateForProfileTimezone(dueAt).toISOString() else ''
'assignment[assignment_group_id]': @form.find(".assignment_group").val()
'assignment[assignment_group_id]': @$form.find(".assignment_group").val()
}
if @event.isNewEvent()
@ -117,7 +117,7 @@ define [
assignment:
title: params['assignment[name]']
due_at: if dueAt then dueAt.toISOString() else null
context_code: @form.find(".context_id").val()
context_code: @$form.find(".context_id").val()
newEvent = commonEventFactory(objectData, @event.possibleContexts())
newEvent.save(params)
else
@ -129,7 +129,7 @@ define [
submitOverride: (form) ->
dueAt = form['assignment_override[due_at]']
dueAt = if dueAt is '' then null else @form.find('#assignment_override_due_at').data('date')
dueAt = if dueAt is '' then null else @$form.find('#assignment_override_due_at').data('date')
params = 'assignment_override[due_at]': if dueAt then $.unfudgeDateForProfileTimezone(dueAt).toISOString() else ''
@event.start = dueAt
@event.save(params)

View File

@ -14,24 +14,24 @@ define [
class EditCalendarEventDetails
constructor: (selector, @event, @contextChangeCB, @closeCB) ->
@currentContextInfo = null
@form = $(editCalendarEventTemplate({
@$form = $(editCalendarEventTemplate({
title: @event.title
contexts: @event.possibleContexts()
lockedTitle: @event.lockedTitle
location_name: @event.location_name
}))
$(selector).append @form
$(selector).append @$form
@setupTimeAndDatePickers()
@form.submit @formSubmit
@form.find(".more_options_link").click @moreOptionsClick
@form.find("select.context_id").change @contextChange
@form.find("select.context_id").triggerHandler('change', false)
@$form.submit @formSubmit
@$form.find(".more_options_link").click @moreOptionsClick
@$form.find("select.context_id").change @contextChange
@$form.find("select.context_id").triggerHandler('change', false)
# Hide the context selector completely if this is an existing event, since it can't be changed.
if !@event.isNewEvent()
@form.find(".context_select").hide()
@$form.find(".context_select").hide()
contextInfoForCode: (code) ->
for context in @event.possibleContexts()
@ -40,20 +40,20 @@ define [
return null
activate: () =>
@form.find("select.context_id").change()
@$form.find("select.context_id").change()
getFormData: =>
data = @form.getFormData(object_name: 'calendar_event')
data = @$form.getFormData(object_name: 'calendar_event')
data = _.omit(data, 'date', 'start_time', 'end_time')
date = @form.find('input[name=date]').data('date')
date = @$form.find('input[name=date]').data('date')
if date
start_time = @form.find('input[name=start_time]').data('date')
start_time = @$form.find('input[name=start_time]').data('date')
start_at = date.toString('yyyy-MM-dd')
start_at += start_time.toString(' HH:mm') if start_time
data.start_at = tz.parse(start_at)
end_time = @form.find('input[name=end_time]').data('date')
end_time = @$form.find('input[name=end_time]').data('date')
end_at = date.toString('yyyy-MM-dd')
end_at += end_time.toString(' HH:mm') if end_time
data.end_at = tz.parse(end_at)
@ -69,9 +69,9 @@ define [
data = @getFormData()
# override parsed input with user input (for 'More Options' only)
data.start_date = @form.find('input[name=date]').val()
data.start_time = @form.find('input[name=start_time]').val()
data.end_time = @form.find('input[name=end_time]').val()
data.start_date = @$form.find('input[name=date]').val()
data.start_time = @$form.find('input[name=start_time]').val()
data.end_time = @$form.find('input[name=end_time]').val()
if data.title then params['title'] = data.title
if data.location_name then params['location_name'] = data.location_name
@ -84,7 +84,7 @@ define [
window.location.href = pieces.join("#")
setContext: (newContext) =>
@form.find("select.context_id").val(newContext).triggerHandler('change', false)
@$form.find("select.context_id").val(newContext).triggerHandler('change', false)
contextChange: (jsEvent, propagate) =>
context = $(jsEvent.target).val()
@ -101,21 +101,21 @@ define [
moreOptionsHref = @currentContextInfo.new_calendar_event_url
else
moreOptionsHref = @event.fullDetailsURL() + '/edit'
@form.find(".more_options_link").attr 'href', moreOptionsHref
@$form.find(".more_options_link").attr 'href', moreOptionsHref
setupTimeAndDatePickers: () =>
@form.find(".date_field").date_field()
@$form.find(".date_field").date_field()
# TODO: Refactor this logic that forms a relationship between two time fields into a module
@form.find(".time_field").time_field().
@$form.find(".time_field").time_field().
blur (jsEvent) =>
start_time = @form.find(".time_field.start_time").next(".datetime_suggest").text()
if @form.find(".time_field.start_time").next(".datetime_suggest").hasClass('invalid_datetime')
start_time = @$form.find(".time_field.start_time").next(".datetime_suggest").text()
if @$form.find(".time_field.start_time").next(".datetime_suggest").hasClass('invalid_datetime')
start_time = null
start_time ?= @form.find(".time_field.start_time").val()
end_time = @form.find(".time_field.end_time").next(".datetime_suggest").text()
if @form.find(".time_field.end_time").next(".datetime_suggest").hasClass('invalid_datetime')
start_time ?= @$form.find(".time_field.start_time").val()
end_time = @$form.find(".time_field.end_time").next(".datetime_suggest").text()
if @$form.find(".time_field.end_time").next(".datetime_suggest").hasClass('invalid_datetime')
end_time = null
end_time ?= @form.find(".time_field.end_time").val()
end_time ?= @$form.find(".time_field.end_time").val()
startDate = Date.parse(start_time)
endDate = Date.parse(end_time)
@ -128,21 +128,21 @@ define [
else
if endDate < startDate then endDate = startDate
if startDate
@form.find(".time_field.start_time").val(startDate.toString('h:mmtt').toLowerCase())
@$form.find(".time_field.start_time").val(startDate.toString('h:mmtt').toLowerCase())
if endDate
@form.find(".time_field.end_time").val(endDate.toString('h:mmtt').toLowerCase())
@$form.find(".time_field.end_time").val(endDate.toString('h:mmtt').toLowerCase())
startDate = @event.startDate()
endDate = @event.endDate()
if !@event.allDay
if startDate
@form.find(".time_field.start_time").val(startDate.toString('h:mmtt')).change().blur()
@$form.find(".time_field.start_time").val(startDate.toString('h:mmtt')).change().blur()
if endDate
@form.find(".time_field.end_time").val(endDate.toString('h:mmtt')).change().blur()
@$form.find(".time_field.end_time").val(endDate.toString('h:mmtt')).change().blur()
if startDate
@form.find(".date_field").val(startDate.toString('MMM d, yyyy')).change()
@$form.find(".date_field").val(startDate.toString('MMM d, yyyy')).change()
formSubmit: (jsEvent) =>
jsEvent.preventDefault()
@ -165,7 +165,7 @@ define [
start_at: if data.start_at then data.start_at.toISOString() else null
end_at: if data.end_at then data.end_at.toISOString() else null
location_name: location_name
context_code: @form.find(".context_id").val()
context_code: @$form.find(".context_id").val()
newEvent = commonEventFactory(objectData, @event.possibleContexts())
newEvent.save(params)
else

View File

@ -7,7 +7,7 @@ define [
'jquery.ajaxJSON'
'jquery.instructure_date_and_time'
'jqueryui/dialog'
], (I18n, $, developer_key, developer_key_form, preventDefault) ->
], (I18n, $, developer_key, developerKeyFormTemplate, preventDefault) ->
page = 0
buildKey = (key) ->
key.icon_image_url = key.icon_url || "/images/blank.png"
@ -20,7 +20,7 @@ define [
$key.data('key', key)
buildForm = (key, $orig) ->
$form = $(developer_key_form(key || {}))
$form = $(developerKeyFormTemplate(key || {}))
$form.formSubmit({
beforeSubmit: ->
$("#edit_dialog button.submit").text(I18n.t('button.saving', "Saving Key..."))
@ -72,7 +72,7 @@ define [
$form = buildForm()
$("#edit_dialog").empty().append($form).dialog('open')
)
$("#edit_dialog").html(developer_key_form({})).dialog({
$("#edit_dialog").html(developerKeyFormTemplate({})).dialog({
autoOpen: false,
width: 350
}).on('click', '.cancel', () ->

View File

@ -48,7 +48,7 @@ define [
createCancelButton: ->
$('<a/>')
.html(I18n.t('cancel', 'Cancel'))
.text(I18n.t('cancel', 'Cancel'))
.css(marginLeft: '5px')
.attr('href', 'javascript:')
.addClass('cancel_button')

View File

@ -8,8 +8,9 @@ define [
'jst/discussions/_reply_attachment'
'compiled/fn/preventDefault'
'compiled/views/editor/KeyboardShortcuts'
'str/stripTags'
'tinymce.editor_box'
], (Backbone, _, I18n, $, Entry, htmlEscape, replyAttachmentTemplate, preventDefault, KeyboardShortcuts) ->
], (Backbone, _, I18n, $, Entry, htmlEscape, replyAttachmentTemplate, preventDefault, KeyboardShortcuts, stripTags) ->
class Reply
@ -87,7 +88,7 @@ define [
submit: =>
@hide()
@textArea._setContentCode ''
@view.model.set 'notification', "<div class='alert alert-info'>#{I18n.t 'saving_reply', 'Saving reply...'}</div>"
@view.model.set 'notification', "<div class='alert alert-info'>#{htmlEscape I18n.t 'saving_reply', 'Saving reply...'}</div>"
entry = new Entry @getModelAttributes()
entry.save null,
success: @onPostReplySuccess
@ -112,7 +113,7 @@ define [
now = new Date().getTime()
# TODO: remove this summary, server should send it in create response and no further
# work is required
summary: $('<div/>').html(@content).text()
summary: stripTags(@content)
message: @content
parent_id: if @options.topLevel then null else @view.model.get 'id'
user_id: ENV.current_user_id

View File

@ -7,6 +7,10 @@ define [
'tinymce.editor_box'
], (_, I18n, $, Backbone, preventDefault) ->
###
xsslint safeString.property content
###
##
# Toggles an element between a rich text editor and itself
class EditorToggle
@ -103,7 +107,7 @@ define [
# @api private
createDone: ->
$('<a/>')
.html(@options.doneText)
.text(@options.doneText)
.attr('href', '#')
.addClass('btn edit-html-done edit_html_done')
.attr('title', I18n.t('done.title', 'Click to finish editing the rich text area'))

View File

@ -1,7 +1,8 @@
define [
'i18n!editor_accessibility'
'jquery'
], (I18n, $) ->
'str/htmlEscape'
], (I18n, $, htmlEscape) ->
##
# Used to insert accessibility titles into core TinyMCE components
class EditorAccessiblity
@ -32,7 +33,7 @@ define [
@$el.find("##{@id_prepend}_instructure_record").attr('aria-disabled', 'true')
@$el.find("##{@id_prepend}_instructure_record").removeAttr('role')
@$el.find("##{@id_prepend}_instructure_record_voice").append('<br/>').append(I18n.t('accessibles.record', 'This feature is inaccessible for screen readers.'))
@$el.find("##{@id_prepend}_instructure_record_voice").append('<br/>').append(htmlEscape(I18n.t('accessibles.record', 'This feature is inaccessible for screen readers.')))
@$el.find("##{@id_prepend}_instructure_record img").attr('alt',
@$el.find("##{@id_prepend}_instructure_record img").attr('alt') + ", " + I18n.t('accessibles.record', 'This feature is inaccessible for screen readers.'));

View File

@ -2,13 +2,14 @@ define [
'i18n!gradebook2'
'jquery'
'jst/CurveGradesDialog'
'str/htmlEscape'
'jquery.disableWhileLoading'
'jquery.instructure_forms'
'jqueryui/dialog'
'jquery.instructure_misc_plugins'
'compiled/jquery/fixDialogButtons'
'vendor/jquery.ba-tinypubsub'
], (I18n, $, curveGradesDialogTemplate) ->
], (I18n, $, curveGradesDialogTemplate, htmlEscape) ->
class CurveGradesDialog
constructor: ({@assignment, @students, context_url}) ->
@ -133,12 +134,12 @@ define [
pct = (users.length / maxCount)
cnt = users.length
color = (if idx == 0 then "#a03536" else "#007ab8")
$("#results_list").prepend "<td style='padding: 1px;'><div title='" + cnt + " student" + (if cnt == 1 then "" else "s") + " will get " + idx + " points' style='border: 1px solid #888; background-color: " + color + "; width: " + width + "px; height: " + (100 * pct) + "px; margin-top: " + (100 * (1 - pct)) + "px;'>&nbsp;</div></td>"
$("#results_values").prepend "<td style='text-align: center;'>" + idx + "</td>"
$("#results_list").prepend "<td style='padding: 1px;'><div title='" + htmlEscape(I18n.t({one: "1 student will get %{num} points", other: "%{count} students will get %{num} points"}, {count: cnt, num: idx})) + "' style='border: 1px solid #888; background-color: " + htmlEscape(color) + "; width: " + htmlEscape(width) + "px; height: " + htmlEscape(100 * pct) + "px; margin-top: " + htmlEscape(100 * (1 - pct)) + "px;'>&nbsp;</div></td>"
$("#results_values").prepend "<td style='text-align: center;'>" + htmlEscape(idx) + "</td>"
skipCount = 0
else
skipCount++
idx--
$("#results_list").prepend "<td><div style='height: 100px; position: relative; width: 30px; font-size: 0.8em;'><img src='/images/number_of_students.png' alt='# of students'/><div style='position: absolute; top: 0; right: 3px;'>" + maxCount + "</div><div style='position: absolute; bottom: 0; right: 3px;'>0</div></div></td>"
$("#results_list").prepend "<td><div style='height: 100px; position: relative; width: 30px; font-size: 0.8em;'><img src='/images/number_of_students.png' alt='" + htmlEscape(I18n.t("# of students")) + "'/><div style='position: absolute; top: 0; right: 3px;'>" + htmlEscape(maxCount) + "</div><div style='position: absolute; bottom: 0; right: 3px;'>0</div></div></td>"
$("#results_values").prepend "<td>&nbsp;</td>"
finalScores

View File

@ -410,16 +410,15 @@ define [
window.location.reload()
assignmentGroupHtml: (group_name, group_weight) =>
escaped_group_name = htmlEscape(group_name)
if @weightedGroups()
percentage = I18n.toPercentage(group_weight, precision: 2)
"""
#{escaped_group_name}<div class='assignment-points-possible'>
#{I18n.t 'percent_of_grade', "%{percentage} of grade", percentage: percentage}
#{htmlEscape(group_name)}<div class='assignment-points-possible'>
#{htmlEscape I18n.t 'percent_of_grade', "%{percentage} of grade", percentage: percentage}
</div>
"""
else
"#{escaped_group_name}"
htmlEscape(group_name)
# filter, sort, and build the dataset for slickgrid to read from, then force
# a full redraw
@ -536,7 +535,7 @@ define [
(SubmissionCell[assignment.grading_type] || SubmissionCell).formatter(row, col, submission, assignment)
staticCellFormatter: (row, col, val) =>
"<div class='cell-content gradebook-cell'>#{val}</div>"
"<div class='cell-content gradebook-cell'>#{htmlEscape(val)}</div>"
uneditableCellFormatter: (row, col) =>
"<div class='cell-content gradebook-cell grayed-out'></div>"
@ -654,7 +653,7 @@ define [
columnDef.name = columnDef.unminimizedName
columnDef.minimized = false
@$grid.find(".l#{colIndex}").add($columnHeader).removeClass('minimized')
$columnHeader.find('.slick-column-name').html(columnDef.name)
$columnHeader.find('.slick-column-name').html($.raw(columnDef.name))
@assignmentsToHide = $.grep @assignmentsToHide, (el) -> el != columnDef.id
userSettings.contextSet('hidden_columns', _.uniq(@assignmentsToHide))
@ -678,7 +677,7 @@ define [
# add lines for dropped, late, resubmitted
Array::push.apply htmlLines, $.map(SubmissionCell.classesBasedOnSubmission(submission, assignment), (c)=> GRADEBOOK_TRANSLATIONS["#submission_tooltip_#{c}"])
else if assignment.points_possible?
htmlLines.push I18n.t('points_out_of', "out of %{points_possible}", points_possible: assignment.points_possible)
htmlLines.push htmlEscape(I18n.t('points_out_of', "out of %{points_possible}", points_possible: assignment.points_possible))
$hoveredCell.data('tooltip', $("<span />",
class: 'gradebook-tooltip'
@ -687,7 +686,7 @@ define [
top: offset.top
zIndex: 10000
display: 'block'
html: htmlLines.join('<br />')
html: $.raw(htmlLines.join('<br />'))
).appendTo('body')
.css('top', (i, top) -> parseInt(top) - $(this).outerHeight()))
@ -972,7 +971,7 @@ define [
@parentColumns = [
id: 'student'
name: I18n.t 'student_name', 'Student Name'
name: htmlEscape I18n.t 'student_name', 'Student Name'
field: 'display_name'
width: 150
cssClass: "meta-cell"
@ -981,7 +980,7 @@ define [
formatter: @htmlContentFormatter
,
id: 'secondary_identifier'
name: I18n.t 'secondary_id', 'Secondary ID'
name: htmlEscape I18n.t 'secondary_id', 'Secondary ID'
field: 'secondary_identifier'
width: 100
cssClass: "meta-cell secondary_identifier_cell"
@ -1046,7 +1045,7 @@ define [
field: "total_grade"
formatter: @groupTotalFormatter
name: """
#{total}
#{htmlEscape total}
<div id=total_column_header></div>
"""
toolTip: total
@ -1208,6 +1207,10 @@ define [
else
@totalGradeWarning = null
###
xsslint jqueryObject.identifier createLink
xsslint jqueryObject.function showLink hideLink
###
showCustomColumnDropdownOption: ->
linkContainer = $("<li>").appendTo(".gradebook_drop_down")

View File

@ -9,6 +9,10 @@ define [
'jst/gradebook2/outcome_gradebook_student_cell'
], (I18n, $, _, HeaderFilterView, OutcomeColumnView, numberCompare, cellTemplate, studentCellTemplate) ->
###
xsslint safeString.method cellHtml
###
Grid =
filter: ['mastery', 'near-mastery', 'remedial']

View File

@ -15,7 +15,7 @@ define [
init: () ->
submission = @opts.item[@opts.column.field]
@$wrapper = $(@cellWrapper("<input #{@ariaLabel(submission.submission_type)} class='grade'/>")).appendTo(@opts.container)
@$wrapper = $(@cellWrapper("<input #{htmlEscape @ariaLabel(submission.submission_type)} class='grade'/>")).appendTo(@opts.container)
@$input = @$wrapper.find('input').focus().select()
destroy: () ->
@ -82,15 +82,15 @@ define [
if turnitin = extractData(opts.submission)
specialClasses.push('turnitin')
innerContents += "<span class='gradebook-cell-turnitin #{turnitin.state}-score' />"
innerContents += "<span class='gradebook-cell-turnitin #{htmlEscape turnitin.state}-score' />"
tooltipText = $.map(specialClasses, (c)-> GRADEBOOK_TRANSLATIONS["submission_tooltip_#{c}"]).join ', '
"""
#{ if tooltipText then '<div class="gradebook-tooltip">'+ tooltipText + '</div>' else ''}
<div class="gradebook-cell #{ if opts.editable then 'gradebook-cell-editable focus' else ''} #{opts.classes} #{specialClasses.join(' ')}">
#{$.raw if tooltipText then '<div class="gradebook-tooltip">'+ htmlEscape(tooltipText) + '</div>' else ''}
<div class="gradebook-cell #{htmlEscape if opts.editable then 'gradebook-cell-editable focus' else ''} #{htmlEscape opts.classes} #{htmlEscape specialClasses.join(' ')}">
<a href="#" data-user-id=#{opts.submission.user_id} data-assignment-id=#{opts.assignment.id} class="gradebook-cell-comment"><span class="gradebook-cell-comment-label">submission comments</span></a>
#{innerContents}
#{$.raw innerContents}
</div>
"""
@ -112,7 +112,7 @@ define [
@submissionIcon: (submission_type) ->
klass = SubmissionCell.iconFromSubmissionType(submission_type)
"<i class='icon-#{klass}' ></i>"
"<i class='icon-#{htmlEscape klass}' ></i>"
@iconFromSubmissionType: (submission_type) ->
switch submission_type
@ -135,7 +135,7 @@ define [
@$wrapper = $(@cellWrapper("""
<div class="overflow-wrapper">
<div class="grade-and-outof-wrapper">
<input type="text" #{@ariaLabel(submission.submission_type)} class="grade"/><span class="outof"><span class="divider">/</span>#{@opts.column.object.points_possible}</span>
<input type="text" #{htmlEscape @ariaLabel(submission.submission_type)} class="grade"/><span class="outof"><span class="divider">/</span>#{htmlEscape @opts.column.object.points_possible}</span>
</div>
</div>
""", { classes: 'gradebook-cell-out-of-formatter' })).appendTo(@opts.container)
@ -144,7 +144,7 @@ define [
class SubmissionCell.letter_grade extends SubmissionCell
@formatter: (row, col, submission, assignment) ->
innerContents = if submission.score
"#{submission.grade}<span class='letter-grade-points'>#{submission.score}</span>"
"#{htmlEscape submission.grade}<span class='letter-grade-points'>#{htmlEscape submission.score}</span>"
else
submission.grade
@ -165,7 +165,7 @@ define [
htmlFromSubmission: (options={}) ->
cssClass = classFromSubmission(options.submission)
SubmissionCell::cellWrapper("""
<a data-value="#{cssClass}" class="gradebook-checkbox gradebook-checkbox-#{cssClass} #{'editable' if options.editable}" href="#">#{cssClass}</a>
<a data-value="#{htmlEscape cssClass}" class="gradebook-checkbox gradebook-checkbox-#{htmlEscape cssClass} #{htmlEscape('editable' if options.editable)}" href="#">#{htmlEscape cssClass}</a>
""", options)
# htmlFromSubmission = (submission, editable = false) ->

View File

@ -53,7 +53,7 @@ define [
courseText = I18n.t('#helpers.course', 'Course')
courseDatetime = $.datetimeString(datetime, timezone: ENV.CONTEXT_TIMEZONE)
if localDatetime != courseDatetime
titleText = "#{localText}: #{localDatetime}<br>#{courseText}: #{courseDatetime}"
titleText = "#{htmlEscape localText}: #{htmlEscape localDatetime}<br>#{htmlEscape courseText}: #{htmlEscape courseDatetime}"
if justText
new Handlebars.SafeString titleText
@ -71,7 +71,7 @@ define [
else
timeTitle = $.datetimeString(datetime)
new Handlebars.SafeString "<time data-tooltip title='#{timeTitle}' datetime='#{datetime.toISOString()}' #{'pubdate' if pubdate}>#{$.friendlyDatetime(fudged)}</time>"
new Handlebars.SafeString "<time data-tooltip title='#{htmlEscape timeTitle}' datetime='#{datetime.toISOString()}' #{$.raw('pubdate' if pubdate)}>#{$.friendlyDatetime(fudged)}</time>"
@ -79,7 +79,7 @@ define [
formattedDate : (datetime, format, {hash: {pubdate}}) ->
return unless datetime?
datetime = tz.parse(datetime) unless _.isDate datetime
new Handlebars.SafeString "<time data-tooltip title='#{$.datetimeString(datetime)}' datetime='#{datetime.toISOString()}' #{'pubdate' if pubdate}>#{datetime.toString(format)}</time>"
new Handlebars.SafeString "<time data-tooltip title='#{$.datetimeString(datetime)}' datetime='#{datetime.toISOString()}' #{$.raw('pubdate' if pubdate)}>#{htmlEscape datetime.toString(format)}</time>"
# IMPORTANT: these next two handlebars helpers emit profile-timezone
# human-formatted strings. don't send them as is to the server (you can
@ -332,11 +332,11 @@ define [
attributes = for key, val of inputProps when val?
"#{htmlEscape key}=\"#{htmlEscape val}\""
hiddenDisabled = if inputProps.disabled then "disabled" else ""
hiddenDisabledHtml = if inputProps.disabled then "disabled" else ""
new Handlebars.SafeString """
<input name="#{htmlEscape inputProps.name}" type="hidden" value="0" #{hiddenDisabled}>
<input #{attributes.join ' '} />
<input name="#{htmlEscape inputProps.name}" type="hidden" value="0" #{hiddenDisabledHtml}>
<input #{$.raw attributes.join ' '} />
"""
toPercentage: (number) ->

View File

@ -85,9 +85,9 @@ define [
if newTitle = @$dialog.find("a[href='#{panelId}'] .text").text()
newTitle = $("
<a class='ui-dialog-header-backlink' href='#help-dialog-options'>
#{I18n.t('Back', 'Back')}
#{htmlEscape(I18n.t('Back', 'Back'))}
</a>
<span>#{newTitle}</span>
<span>#{htmlEscape(newTitle)}</span>
")
else
newTitle = @defaultTitle
@ -114,11 +114,12 @@ define [
@$dialog.dialog('close')
$.when(coursesDfd, @helpLinksDfd).done ([courses]) ->
options = $.map courses, (c) ->
"<option value='course_#{c.id}_admins' #{if ENV.context_id is c.id then 'selected' else ''}>
optionsHtml = $.map(courses, (c) ->
"<option value='course_#{c.id}_admins' #{$.raw if ENV.context_id is c.id then 'selected' else ''}>
#{htmlEscape(c.name)}
</option>"
$form.find('[name="recipients[]"]').html(options.join '')
).join('')
$form.find('[name="recipients[]"]').html(optionsHtml)
initTriggers: ->
$('.help_dialog_trigger').click preventDefault @open

View File

@ -5,6 +5,9 @@ define [
'jquery.ajaxJSON'
'jqueryui/dialog'
], (I18n, $, Slick) ->
###
xsslint safeString.identifier klass d out_of runtime_string
###
fillin_job_data = (job) ->
$('#show-job .show-field').each (idx, field) =>

View File

@ -23,6 +23,9 @@ define [
$this.stop(true, true).remove()
initFlashContainer() # look for the container on script load
###
xsslint safeString.function escapeContent
###
escapeContent = (content) ->
if content.hasOwnProperty('html') then content.html else htmlEscape(content)
@ -37,10 +40,10 @@ define [
flashBox = (type, content, timeout, cssOptions = {}) ->
$node = $("""
<li class="ic-flash-#{type}">
<li class="ic-flash-#{htmlEscape(type)}">
<i></i>
#{escapeContent(content)}
<a href="#" class="close_link icon-end">#{I18n.t("close", "Close")}</a>
<a href="#" class="close_link icon-end">#{htmlEscape I18n.t("close", "Close")}</a>
</li>
""")

View File

@ -26,8 +26,8 @@ define [
"#{dimensions.width}/height/#{dimensions.height}/bgcolor/000000/type/2/vid_sec/5"
$thumbnail = $ "<span
style='background-image: url(#{backgroundUrl});'
class='media_comment_thumbnail media_comment_thumbnail-#{size}'
style='background-image: url(#{htmlEscape(backgroundUrl)});'
class='media_comment_thumbnail media_comment_thumbnail-#{htmlEscape(size)}'
>
<span class='media_comment_thumbnail_play_button'>
#{htmlEscape I18n.t 'click_to_view', 'Click to view'}

View File

@ -16,8 +16,8 @@ define [
if @$fileInput
@$fileInput.off 'change', callback
@$fileInput.remove()
fileInput = "<input id='#{id}' type='file' style='display: none;'>"
$(parentId).append(fileInput)
fileInputHtml = "<input id='#{id}' type='file' style='display: none;'>"
$(parentId).append(fileInputHtml)
@$fileInput = $("##{id}")
@$fileInput.on 'change', callback

View File

@ -68,7 +68,7 @@ define [
showErrorMessage: (message) ->
@hideProgBar()
$('#media_upload_feedback').css('visibility', 'visible')
$("#media_upload_feedback_text").html(message)
$("#media_upload_feedback_text").text(message)
showProgBar: ->
$('#media_upload_progress').css('visibility', 'visible').progressbar()

View File

@ -7,7 +7,8 @@ define [
'compiled/collections/DiscussionEntriesCollection'
'compiled/models/Assignment'
'compiled/models/DateGroup'
], (I18n, Backbone, $, _, ParticipantCollection, DiscussionEntriesCollection, Assignment, DateGroup) ->
'str/stripTags'
], (I18n, Backbone, $, _, ParticipantCollection, DiscussionEntriesCollection, Assignment, DateGroup, stripTags) ->
class DiscussionTopic extends Backbone.Model
resourceName: 'discussion_topics'
@ -116,7 +117,7 @@ define [
@entries.reset(entries)
summary: ->
$('<div/>').html(@get('message')).text() || ''
stripTags @get('message')
# TODO: this would belong in Backbone.model, but I dont know of others are going to need it much
# or want to commit to this api so I am just putting it here for now

View File

@ -7,8 +7,9 @@ define [
'jquery'
'underscore'
'Backbone'
'str/stripTags'
'jquery.ajaxJSON'
], (I18n, $, _, Backbone) ->
], (I18n, $, _, Backbone, stripTags) ->
##
# Model representing an entry in discussion topic
@ -172,8 +173,7 @@ define [
##
# Computed attribute
summary: ->
@escapeDiv ||= $('<div/>')
@escapeDiv.html(@get('message')).text()
stripTags @get('message')
##
# Not familiar enough with Backbone.sync to do this, using ajaxJSON

View File

@ -8,10 +8,11 @@ define [
'jst/registration/studentDialog'
'jst/registration/parentDialog'
'compiled/util/addPrivacyLinkToDialog'
'str/htmlEscape'
'compiled/jquery/validate'
'jquery.instructure_forms'
'jquery.instructure_date_and_time'
], ($, _, I18n, preventDefault, registrationErrors, teacherDialog, studentDialog, parentDialog, addPrivacyLinkToDialog) ->
], ($, _, I18n, preventDefault, registrationErrors, teacherDialog, studentDialog, parentDialog, addPrivacyLinkToDialog, htmlEscape) ->
$nodes = {}
templates = {teacherDialog, studentDialog, parentDialog}
@ -24,19 +25,20 @@ define [
"teacher_dialog.agree_to_terms_and_pp"
"You agree to the *terms of use* and acknowledge the **privacy policy**."
wrappers: [
"<a href=\"#{terms_of_use_url}\" target=\"_blank\">$1</a>"
"<a href=\"#{privacy_policy_url}\" target=\"_blank\">$1</a>"
"<a href=\"#{htmlEscape terms_of_use_url}\" target=\"_blank\">$1</a>"
"<a href=\"#{htmlEscape privacy_policy_url}\" target=\"_blank\">$1</a>"
]
)
signupDialog = (id, title) ->
return unless templates[id]
$node = $nodes[id] ?= $('<div />')
$node.html templates[id](
html = templates[id](
account: ENV.ACCOUNT.registration_settings
terms_required: ENV.ACCOUNT.terms_required
terms_html: termsHtml(ENV.ACCOUNT)
)
$node.html html
$node.find('.date-field').datetime_field()
$node.find('.signup_link').click preventDefault ->

View File

@ -52,9 +52,9 @@ define [
th =
quoteClump: (lines) ->
"<div class='quoted_text_holder'>
<a href='#' class='show_quoted_text_link'>#{I18n.t("quoted_text_toggle", "show quoted text")}</a>
<a href='#' class='show_quoted_text_link'>#{htmlEscape I18n.t("quoted_text_toggle", "show quoted text")}</a>
<div class='quoted_text' style='display: none;'>
#{lines.join "\n"}
#{$.raw lines.join "\n"}
</div>
</div>"

View File

@ -1,7 +1,8 @@
define [
'jquery'
'underscore'
], ($, _) ->
'str/htmlEscape'
], ($, _, htmlEscape) ->
# use this method to process any user content fields returned in api responses
# this is important to handle object/embed tags safely, and to properly display audio/video tags
@ -9,9 +10,9 @@ define [
$dummy = $('<div />').html(html)
# finds any <video/audio class="instructure_inline_media_comment"> and turns them into media comment thumbnails
$dummy.find('video.instructure_inline_media_comment,audio.instructure_inline_media_comment').replaceWith ->
$node = $("<a id='media_comment_#{$(this).data('media_comment_id')}'
data-media_comment_type='#{$(this).data('media_comment_type')}'
class='instructure_inline_media_comment #{this.nodeName.toLowerCase()}_comment' />")
$node = $("<a id='media_comment_#{htmlEscape($(this).data('media_comment_id'))}'
data-media_comment_type='#{htmlEscape($(this).data('media_comment_type'))}'
class='instructure_inline_media_comment #{htmlEscape(this.nodeName.toLowerCase())}_comment' />")
$node.html $(this).html()
$node
@ -34,11 +35,11 @@ define [
uuid = _.uniqueId("uc_")
action = "/object_snippet"
action = "//#{ENV.files_domain}#{action}" if ENV.files_domain
$form = $("<form action='#{action}' method='post' class='user_content_post_form' target='#{uuid}' id='form-#{uuid}' />")
$form = $("<form action='#{htmlEscape(action)}' method='post' class='user_content_post_form' target='#{htmlEscape(uuid)}' id='form-#{htmlEscape(uuid)}' />")
$form.append($("<input type='hidden'/>").attr({name: 'object_data', value: $this.data('uc_snippet')}))
$form.append($("<input type='hidden'/>").attr({name: 's', value: $this.data('uc_sig')}))
$('body').append($form)
setTimeout((-> $form.submit()), 0)
$("<iframe class='user_content_iframe' name='#{uuid}' style='width: #{$this.data('uc_width')}; height: #{$this.data('uc_height')};' frameborder='0' />")
$("<iframe class='user_content_iframe' name='#{htmlEscape(uuid)}' style='width: #{htmlEscape($this.data('uc_width'))}; height: #{htmlEscape($this.data('uc_height'))};' frameborder='0' />")
$dummy.html()

View File

@ -18,6 +18,10 @@
define ['underscore'], (_) ->
###
xsslint xssable.receiver.whitelist builder
###
BlobFactory =
fromCanvas: (canvas, type = 'image/jpeg') ->
url = canvas.toDataURL(type)

View File

@ -31,5 +31,5 @@ define [
}
"""
$('<div/>').html("<style>#{css.join('')}</style>").appendTo(document.body)
$('<div/>').html("<style>#{$.raw(css.join(''))}</style>").appendTo(document.body)

View File

@ -13,12 +13,12 @@ define [
context
format = (context, linkToContexts) ->
str = h(context.name)
html = h(context.name)
if context.activeFilter
str = "<span class='active-filter'>#{str}</span>"
html = "<span class='active-filter'>#{html}</span>"
if linkToContexts and context.type is "course"
str = "<a href='#{h(context.url)}'>#{str}</a>"
$.raw str
html = "<a href='#{h(context.url)}'>#{html}</a>"
$.raw html
# given a map of ids by type (e.g. {courses: [1, 2], groups: ...})
# and a map of possible contexts by type,

View File

@ -5,6 +5,10 @@ define [
'str/htmlEscape'
], (I18n, $, _, h) ->
###
xsslint safeString.identifier i
###
builders =
year: (options, htmlOptions) ->
step = if options.startYear < options.endYear then 1 else -1
@ -56,12 +60,11 @@ define [
for i in [0...options.order.length]
type = options.order[i]
tName = name.replace(/(\]?)$/, "(" + position[type] + "i)$1")
$result.append(
builders[type](
options,
_.extend({name: tName}, htmlOptions),
dateSettings
)
html = builders[type](
options,
_.extend({name: tName}, htmlOptions),
dateSettings
)
$result.append(html)
delete htmlOptions.id
$result

View File

@ -61,7 +61,7 @@ define [
for i in [0..count-1]
iframe = document.createElement('iframe')
$(iframe).addClass('hidden kaltura-analytics')
$(document.body).append(iframe)
$(document.body).append($(iframe))
# there is no reliable way to know when a remote url has loaded in an
# iframe, so just send them every 4 seconds
@ -160,4 +160,4 @@ define [
if pluginSettings && pluginSettings.do_analytics
ka = new KalturaAnalytics(mediaId, mediaElement, pluginSettings)
ka.addListeners()
ka
ka

View File

@ -17,7 +17,7 @@ define [
#{h(I18n.t('other', 'other', count: strOrArray.length))}
<span>
<ul>
#{(('<li>' + h(str) + '</li>') for str in strOrArray).join('')}
#{$.raw (('<li>' + h(str) + '</li>') for str in strOrArray).join('')}
</ul>
</span>
</span>

View File

@ -1,10 +1,10 @@
# requires $.sameDate, $.dateString, $.timeString, $.datetimeString
define ['i18n!dates', 'jquery', 'timezone', 'jquery.instructure_date_and_time'], (I18n, $, tz) ->
define ['i18n!dates', 'jquery', 'timezone', 'str/htmlEscape', 'jquery.instructure_date_and_time'], (I18n, $, tz, htmlEscape) ->
semanticDateRange = (startISO, endISO) ->
unless startISO
return """
<span class="date-range date-range-no-date">
#{I18n.t 'no_date', 'No Date'}
#{htmlEscape I18n.t 'no_date', 'No Date'}
</span>
"""

View File

@ -6,7 +6,7 @@ define [
'jst/discussions/EntryCollectionView'
'jst/discussions/entryStats'
'compiled/views/DiscussionTopic/EntryView'
], (I18n, $, walk, {View}, template, entryStats, EntryView) ->
], (I18n, $, walk, {View}, template, entryStatsTemplate, EntryView) ->
class EntryCollectionView extends View
@ -110,7 +110,7 @@ define [
one: "Show one reply"
other: "Show all %{count} replies"
{count: stats.total + @collection.options.perPage}
@nextLink.html entryStats({stats, moreText, showMore: yes})
@nextLink.html entryStatsTemplate({stats, moreText, showMore: yes})
@nextLink.addClass 'showMore loadNext'
if @options.threaded
@nextLink.insertAfter @list

View File

@ -17,7 +17,7 @@ define [
'compiled/str/convertApiUserContent'
'jst/_avatar'
'jst/discussions/_reply_form'
], ($, _, I18n, MarkAsReadWatcher, walk, Backbone, EntryCollection, entryContentPartial, deletedEntriesTemplate, entryWithRepliesTemplate, entryStats, Reply, EntryEditor, htmlEscape, {publish}, convertApiUserContent) ->
], ($, _, I18n, MarkAsReadWatcher, walk, Backbone, EntryCollection, entryContentPartial, deletedEntriesTemplate, entryWithRepliesTemplate, entryStatsTemplate, Reply, EntryEditor, htmlEscape, {publish}, convertApiUserContent) ->
class EntryView extends Backbone.View
@ -124,11 +124,11 @@ define [
stats = @countPosterity()
html = """
<div class='new-and-total-badge'>
<span class="new-items">#{stats.unread}</span>
<span class="total-items">#{stats.total}</span>
<span class="new-items">#{htmlEscape stats.unread}</span>
<span class="total-items">#{htmlEscape stats.total}</span>
</div>
"""
@$headerBadges.append entryStats({stats})
@$headerBadges.append entryStatsTemplate({stats})
@addedCountsToHeader = true
toggleDeleted: (model, deleted) =>
@ -179,10 +179,10 @@ define [
renderDescendantsLink: ->
stats = @countPosterity()
@descendantsLink = $ '<div/>'
@descendantsLink.html entryStats({stats, showMore: yes})
@descendantsLink.addClass 'showMore loadDescendants'
@$replies.append @descendantsLink
@$descendantsLink = $ '<div/>'
@$descendantsLink.html entryStatsTemplate({stats, showMore: yes})
@$descendantsLink.addClass 'showMore loadDescendants'
@$replies.append @$descendantsLink
countPosterity: ->
stats = unread: 0, total: 0

View File

@ -5,7 +5,8 @@ define [
'underscore'
'jst/ExternalTools/AddAppView'
'jquery.disableWhileLoading'
], (Backbone, I18n, $, _, template, disableWhileLoading) ->
'str/htmlEscape'
], (Backbone, I18n, $, _, template, disableWhileLoading, htmlEscape) ->
class AddAppView extends Backbone.View
template: template
@ -104,14 +105,14 @@ define [
addError: (input, message) ->
input = @$(input)
input.parents('.control-group').addClass('error')
input.after("<span class='help-inline'>#{message}</span>")
input.after("<span class='help-inline'>#{htmlEscape message}</span>")
input.one 'keypress', ->
$(this).parents('.control-group').removeClass('error')
$(this).parents('.control-group').find('.help-inline').remove()
onSaveFail: (model) =>
message = I18n.t 'generic_error', 'There was an error in processing your request'
@$el.prepend("<div class='alert alert-error'>#{message}</span>")
@$el.prepend("<div class='alert alert-error'>#{htmlEscape message}</span>")
onSaveSuccess: (model) =>
@app.set('is_installed', true)

View File

@ -3,8 +3,9 @@ define [
'jquery'
'jst/ExternalTools/EditView'
'compiled/views/ValidatedFormView'
'str/htmlEscape'
'compiled/jquery/fixDialogButtons'
], (I18n, $, template, ValidatedFormView) ->
], (I18n, $, template, ValidatedFormView, htmlEscape) ->
class EditView extends ValidatedFormView
template: template
@ -64,7 +65,7 @@ define [
addError: (input, message) ->
input = $(input)
input.parents('.control-group').addClass('error')
input.after("<span id='#{input.attr("name")}_error_message' class='help-inline'>#{message}</span>")
input.after("<span id='#{htmlEscape input.attr("name")}_error_message' class='help-inline'>#{htmlEscape message}</span>")
input.attr('aria-describedby', "#{input.attr('name')}_error_message")
input.attr('aria-invalid', 'true')
input.one 'keypress', ->
@ -76,7 +77,7 @@ define [
onSaveFail: (xhr) =>
super
message = I18n.t 'generic_error', 'There was an error in processing your request'
@$el.prepend("<div class='alert alert-error'>#{message}</span>")
@$el.prepend("<div class='alert alert-error'>#{htmlEscape message}</span>")
delay = (ms, func) -> setTimeout func, ms
delay 1, -> @$("[aria-invalid='true']").first().focus()

View File

@ -123,7 +123,7 @@ define [
view = @$(event.currentTarget).closest('.external_tool_item').data('view')
tool = view.model
msg = I18n.t 'remove_tool', "Are you sure you want to remove this tool?"
dialog = $("<div>#{msg}</div>").dialog
dialog = $("<div>#{htmlEscape msg}</div>").dialog
modal: true,
resizable: false
title: I18n.t('delete', 'Delete') + ' ' + tool.get('name') + '?'
@ -149,4 +149,4 @@ define [
@$('[data-installed-state]').removeClass('active')
@$('[data-installed-state="' + @appCenterView.targetInstalledState + '"]').addClass('active')
@$('[data-installed-state="' + @appCenterView.targetInstalledState + '"] > a').attr('aria-selected', 'true')
@appCenterView.render()
@appCenterView.render()

View File

@ -70,4 +70,4 @@ define [
showErrorMessage: ->
message = I18n.t 'missing_stars', 'You must select a star rating'
@$el.prepend("<div class='alert alert-error'>#{message}</span>")
@$el.prepend("<div class='alert alert-error'>#{htmlEscape(message)}</span>")

View File

@ -44,5 +44,6 @@ define [
fullsize: "https://farm#{photo.farm}.static.flickr.com/#{photo.server}/#{photo.id}_#{photo.secret}.jpg"
source: "https://secure.flickr.com/photos/#{photo.owner}/#{photo.id}"
title: photo.title
html = html.join('')
@$('.flickrResults').showIf(!!photos.length).html html.join('')
@$('.flickrResults').showIf(!!photos.length).html html

View File

@ -16,8 +16,8 @@ define [
# you're responsible for rendering the content via HB
# and passing it in
render: (content) ->
@$el.html(content)
render: (html) ->
@$el.html(html)
@
bindOpenKeys: ->

View File

@ -8,7 +8,7 @@ define [
'jst/_messageStudentsWhoRecipientList'
'underscore'
'compiled/jquery/serializeForm'
], (I18n, $, DialogFormView, template, wrapperTemplate, Conversation, recipientList, _) ->
], (I18n, $, DialogFormView, template, wrapperTemplate, Conversation, recipientListTemplate, _) ->
class MessageStudentsDialog extends DialogFormView
@ -76,7 +76,7 @@ define [
updateListOfRecipients: =>
groupName = @$recipientGroupName.val()
{recipients} = @_findRecipientGroupByName groupName
@$messageRecipients.html recipientList recipients: recipients
@$messageRecipients.html recipientListTemplate recipients: recipients
onSaveSuccess: ->
@close()

View File

@ -32,9 +32,9 @@ define [
# I'm sorry, Voiceover + jQueryUI made me do it
# VO won't acknowledge the existance of the re-rendered view
# but if we render just the options, it's OK
fragment = $(@template @toJSON())
opts = fragment.filter('select').find('option')
@$('select').empty().append(opts)
$fragment = $(@template @toJSON())
$opts = $fragment.filter('select').find('option')
@$('select').empty().append($opts)
value: ->
@$('select').val()

View File

@ -98,11 +98,11 @@ define [
# attaches child views to @$childContainer
attachChildViews: ->
container = @$childContainer.detach()
$container = @$childContainer.detach()
if @parentListView
container.append(@parentListView.render().el)
container.append(@listView.render().el)
@$content.append(container)
$container.append(@parentListView.render().el)
$container.append(@listView.render().el)
@$content.append($container)
cleanup: =>
@parentListView?.remove()

View File

@ -3,8 +3,9 @@ define [
'jquery'
'compiled/fn/preventDefault'
'Backbone'
'str/htmlEscape'
'jquery.instructure_forms'
], (I18n, $, preventDefault, Backbone) ->
], (I18n, $, preventDefault, Backbone, htmlEscape) ->
class PublishButton extends Backbone.View
disabledClass: 'disabled'
@ -167,7 +168,7 @@ define [
@$el.attr 'aria-pressed', options.buttonClass is @publishedClass
@$icon.addClass options.iconClass
@$text.html "&nbsp;#{options.text}"
@$text.html "&nbsp;#{htmlEscape(options.text)}"
# unpublishable
if !@model.get('unpublishable')? or @model.get('unpublishable')

View File

@ -85,9 +85,10 @@ define [
messages = errors[integerField]
control_group.toggleClass('error', messages?)
if messages
helpInline = $('<span class="help-inline"></span>')
helpInline.html((htmlEscape(message) for {message} in messages).join('<br/>'))
control_group.find('.controls').append(helpInline)
$helpInline = $('<span class="help-inline"></span>')
html = (htmlEscape(message) for {message} in messages).join('<br/>')
$helpInline.html(html)
control_group.find('.controls').append($helpInline)
findItem: ->
@hideErrors()

View File

@ -74,6 +74,7 @@ define [
messages = errors[integerField]
control_group.toggleClass('error', messages?)
if messages
helpInline = $('<span class="help-inline"></span>')
helpInline.html((htmlEscape(message) for {message} in messages).join('<br/>'))
control_group.find('.controls').append(helpInline)
$helpInline = $('<span class="help-inline"></span>')
html = (htmlEscape(message) for {message} in messages).join('<br/>')
$helpInline.html(html)
control_group.find('.controls').append($helpInline)

View File

@ -76,7 +76,7 @@ define [
total_weight += v.findWeight() || 0
total_weight = round(total_weight,2)
@$el.find('#percent_total').html(total_weight + "%")
@$el.find('#percent_total').text(total_weight + "%")
clearWeights: ->
@weights = []
@ -87,7 +87,7 @@ define [
for v in @weights
total_weight += v.findWeight() || 0
total_weight = round(total_weight,2)
@$el.find('#percent_total').html(total_weight + "%")
@$el.find('#percent_total').text(total_weight + "%")
toJSON: ->
data = super

View File

@ -72,7 +72,7 @@ define [
@dueDateViews.push dueDateView
@hideOrShowRemoveButtons()
if render
row = dueDateView.render().$el
@$el.append row
$row = dueDateView.render().$el
@$el.append $row
@reRenderSections()
row.find(".section-list").focus()
$row.find(".section-list").focus()

View File

@ -3,8 +3,9 @@ define [
'Backbone'
'jquery'
'underscore'
'str/htmlEscape'
'compiled/jquery/fixDialogButtons'
], (turnitinSettingsDialog, { View }, $, _) ->
], (turnitinSettingsDialog, { View }, $, _, htmlEscape) ->
class TurnitinSettingsDialog extends View
@ -37,14 +38,15 @@ define [
json = super
_.extend json,
wordsInput: """
<input class="span1" id="exclude_small_matches_words_value" name="words" value="#{json.words}" type="text"/>
<input class="span1" id="exclude_small_matches_words_value" name="words" value="#{htmlEscape json.words}" type="text"/>
"""
percentInput: """
<input class="span1" id="exclude_small_matches_percent_value" name="percent" value="#{json.percent}" type="text"/>
<input class="span1" id="exclude_small_matches_percent_value" name="percent" value="#{htmlEscape json.percent}" type="text"/>
"""
renderEl: =>
@$el.html(turnitinSettingsDialog(@toJSON()))
html = turnitinSettingsDialog(@toJSON())
@$el.html(html)
@$el.dialog({width: 'auto', modal: true}).fixDialogButtons()
getFormValues: =>

View File

@ -56,8 +56,7 @@ define [
hide: => @show(false)
setTitle: (new_text) =>
# need to use .html instead of .text so &ndash; will render correctly
@$title.html(new_text)
@$title.text(new_text)
showPicker: (visible = true) ->
@_pickerShowing = visible

View File

@ -4,15 +4,16 @@ define [
'Backbone'
'i18n!calendar.edit'
'jst/calendar/missingDueDateDialog'
'str/htmlEscape'
'jqueryui/dialog'
'compiled/jquery/fixDialogButtons'
], ($, _, {View}, I18n, template) ->
], ($, _, {View}, I18n, template, htmlEscape) ->
class MissingDateDialogView extends View
dialogTitle: """
<span>
<i class="icon-warning"></i>
#{I18n.t('titles.warning', 'Warning')}
#{htmlEscape I18n.t('titles.warning', 'Warning')}
</span>
"""

View File

@ -58,8 +58,8 @@ define [
render: =>
@updateFilter([])
collaborators = @collection.map(@renderCollaborator)
@$el.html(collaborators.join(''))
collaboratorsHtml = @collection.map(@renderCollaborator).join('')
@$el.html(collaboratorsHtml)
@updateFocus() if @currentIndex? && @hasFocus
@hasFocus = false
super

View File

@ -62,13 +62,14 @@ define [
render: =>
@removeCurrentUser() if @options.currentUser
@updateElementVisibility()
collaborators = @collection.map (c) =>
collaboratorsHtml = @collection.map (c) =>
collaboratorTemplate(extend(c.toJSON(),
type: c.modelType or c.get('type'),
id: c.get('collaborator_id') or c.get('id')
name: c.get('sortable_name') or c.get('name')
selected: true))
@$list.html(collaborators.join(''))
collaboratorsHtml = collaboratorsHtml.join('')
@$list.html(collaboratorsHtml)
@updateFocus() if @currentIndex? && @hasFocus
@hasFocus = false

View File

@ -4,6 +4,10 @@ define [
'i18n!context_modules'
'jquery.loadingImg'
], (Backbone, $, I18n) ->
###
xsslint jqueryObject.identifier dragItem dragModule
###
class ContextModules extends Backbone.View
@optionProperty 'modules'

View File

@ -36,8 +36,8 @@ define [
#
# Returns nothing.
render: ->
html = _.map(@collection.models, @renderPageView)
@$el.append(html.join(''))
html = _.map(@collection.models, @renderPageView).join('')
@$el.append(html)
super
# Public: Return HTML for a given record.

View File

@ -1,7 +1,11 @@
define [
'jquery'
'Backbone'
], ($, Backbone) ->
'str/htmlEscape'
], ($, Backbone, htmlEscape) ->
###
xsslint jqueryObject.identifier dragObject current_item
###
class NavigationView extends Backbone.View
@ -47,11 +51,11 @@ define [
which_list.children('.navitem').each (key, item) ->
if $(item).attr('aria-label') is which_item.attr('aria-label')
return
options.push('<option value="' + $(item).attr('id') + '">' + $(item).attr('aria-label') + '</option>')
options.push('<option value="' + htmlEscape($(item).attr('id')) + '">' + htmlEscape($(item).attr('aria-label')) + '</option>')
$select = @$move_dialog.children().find('#move_nav_item_select')
# Clear the options first
$select.empty()
$select.append(options.join(''))
$select.append($.raw(options.join('')))
# Set the name in the dialog
@$move_name.text which_item.attr('aria-label')
@$move_dialog.data 'current_item', which_item
@ -171,4 +175,4 @@ define [
# Internal: returns whether the element is inside the enabled list
disabled: (el) ->
el.parent().attr("id") == @$disabled_list.attr("id")
el.parent().attr("id") == @$disabled_list.attr("id")

View File

@ -62,8 +62,8 @@ define [
$link = $token.find('a')
$link.attr('href', '#')
$link.attr('title', I18n.t("remove_user_from_course_section", "Remove user from %{course_section}", course_section: $token.find('div').attr('title')))
$screenreader_span = $('<span class="screenreader-only"></span>').append(I18n.t("remove_user_from_course_section",
"Remove user from %{course_section}", course_section: h($token.find('div').attr('title'))))
$screenreader_span = $('<span class="screenreader-only"></span>').append(h(I18n.t("remove_user_from_course_section",
"Remove user from %{course_section}", course_section: h($token.find('div').attr('title')))))
$link.append($screenreader_span)
update: (e) =>

View File

@ -58,10 +58,11 @@ define [
render: ->
@attachEvents()
@$el.html if @innerView
html = if @innerView
@innerView.render().el
else if @renderInstructions
@instructionsTemplate()
@$el.html html
this
attachEvents: ->

View File

@ -119,7 +119,7 @@ define [
# Returns nothing.
onDrop: (dragObject, $destination) ->
# TODO: reduce duplication b/w this and OutcomesDirectoryView.initDroppable
$target = dragObject.li
$target = dragObject.$li
model = dragObject.model
destinationView = $destination.data('view')
originalView = dragObject.parent
@ -142,13 +142,13 @@ define [
$sidebar = $target.parents('.wrapper:first')
if dragObject = $sidebar.data('drag')
# drop
$target.after(dragObject.li)
$target.after(dragObject.$li)
@onDrop(dragObject, $target.parent())
else
# drag
$target.attr('aria-grabbed', true)
dragObject =
li: $target,
$li: $target,
model: $target.data('view').model
parent: $target.parent().data('view')
$sidebar.data('drag', dragObject)
@ -161,7 +161,7 @@ define [
onEscapeKey: (e, $target) ->
$sidebar = $target.parents('.wrapper:first')
return unless dataObject = $sidebar.data('drag')
dataObject.li.data('parent', null).attr('aria-grabbed', false)
dataObject.$li.data('parent', null).attr('aria-grabbed', false)
$sidebar.data('drag', null);
# Internal: Update tabindex on $el and its siblings.

View File

@ -89,10 +89,10 @@ define [
insertRating: (e) =>
e.preventDefault()
rating = $ criterionTemplate description: '', points: '', _index: 99
$(e.currentTarget).closest('.rating').after rating
rating.find('.show').hide().next().show(200)
rating.find('.edit input:first').focus()
$rating = $ criterionTemplate description: '', points: '', _index: 99
$(e.currentTarget).closest('.rating').after $rating
$rating.find('.show').hide().next().show(200)
$rating.find('.edit input:first').focus()
@updateRatings()
# Update rating form field elements and the total.
@ -106,7 +106,7 @@ define [
# reset indices
$(i).attr 'name', i.name.replace /\[[0-9]+\]/, "[#{index}]"
points = @$('.points_possible')
points.html points.html().replace /[0-9/.]+/, total
points.html $.raw points.html().replace(/[0-9/.]+/, total)
showRatingDialog: (e) =>
e.preventDefault()

View File

@ -26,10 +26,11 @@ define [
'compiled/collections/OutcomeGroupCollection'
'compiled/views/outcomes/OutcomeGroupIconView'
'compiled/views/outcomes/OutcomeIconView'
'str/htmlEscape'
'jquery.disableWhileLoading'
'jqueryui/droppable'
'compiled/jquery.rails_flash_notifications'
], (I18n, $, _, PaginatedView, OutcomeGroup, OutcomeCollection, OutcomeGroupCollection, OutcomeGroupIconView, OutcomeIconView) ->
], (I18n, $, _, PaginatedView, OutcomeGroup, OutcomeCollection, OutcomeGroupCollection, OutcomeGroupIconView, OutcomeIconView, htmlEscape) ->
# The outcome group "directory" browser.
class OutcomesDirectoryView extends PaginatedView
@ -157,11 +158,12 @@ define [
# Overriding
paginationLoaderTemplate: ->
"<li><a href='#' class='loading-more'>
#{I18n.t("loading_more_results", "Loading more results")}</a></li>"
#{htmlEscape I18n.t("loading_more_results", "Loading more results")}</a></li>"
# Overriding to insert into the ul.
showPaginationLoader: ->
@$el.append(@$paginationLoader ?= $(@paginationLoaderTemplate()))
@$paginationLoader ?= $(@paginationLoaderTemplate())
@$el.append(@$paginationLoader)
# Fetch outcomes after all the groups have been fetched.
fetchOutcomes: ->

View File

@ -130,6 +130,9 @@ define [
delete @image
###
xsslint xssable.receiver.whitelist req
###
req.append(k, v) for k, v of preflightResponse.upload_params
req.append(preflightResponse.file_param, image, 'profile.jpg')
dataType = if preflightResponse.success_url then 'xml' else 'json'

View File

@ -2,9 +2,10 @@ define [
"underscore"
"Backbone"
"jquery"
"jst/quizzes/LDBLoginPopup",
"jst/quizzes/LDBLoginPopup"
"str/htmlEscape"
"jquery.toJSON"
], (_, Backbone, $, Markup) ->
], (_, Backbone, $, Markup, htmlEscape) ->
# Consumes an event and stops it from propagating.
consume = (e) ->
@ -179,7 +180,7 @@ define [
# Inject the stylesheets.
_(styleSheets).each (href) ->
$head.append "<link rel=\"stylesheet\" href=\"" + href + "\" />"
$head.append "<link rel=\"stylesheet\" href=\"" + htmlEscape(href) + "\" />"
return
# Show the form.
@ -286,4 +287,4 @@ define [
global: false
headers:
'Content-Type': 'application/json'
'Accept': 'application/json'
'Accept': 'application/json'

View File

@ -6,7 +6,8 @@ define [
'jst/roles/manageRoles'
'compiled/views/roles/PermissionButtonView'
'compiled/views/roles/RoleHeaderView'
], (I18n, $, _, Backbone, template, PermissionButtonView, RoleHeaderView) ->
'str/htmlEscape'
], (I18n, $, _, Backbone, template, PermissionButtonView, RoleHeaderView, htmlEscape) ->
class ManageRolesView extends Backbone.View
template: template
className: 'manage-roles-table'
@ -40,7 +41,7 @@ define [
# called, which should get called when role is added or removed.
# @api private
renderHeader: ->
@$el.find('thead tr').html "<th>#{I18n.t('permissions', 'Permissions')}</th>"
@$el.find('thead tr').html "<th>#{htmlEscape(I18n.t('permissions', 'Permissions'))}</th>"
@collection.each (role) =>
roleHeaderView = new RoleHeaderView
@ -94,20 +95,20 @@ define [
_.each @permission_groups, (permission_group) =>
# Add the headers to the group
permission_group_header = """
permission_group_header_html = """
<tr class="toolbar">
<th colspan="#{@collection.length + 1}">#{permission_group.group_name.toUpperCase()}</th>
<th colspan="#{htmlEscape(@collection.length + 1)}">#{htmlEscape(permission_group.group_name.toUpperCase())}</th>
</tr>
"""
@$el.find('tbody').append permission_group_header
@$el.find('tbody').append permission_group_header_html
# Add each permission item.
_.each permission_group.group_permissions, (permission_row) =>
permission_row_html = """
<tr>
<th role="rowheader">#{permission_row.label}</th>
<th role="rowheader">#{htmlEscape(permission_row.label)}</th>
</tr>
"""

View File

@ -335,8 +335,8 @@ define [
# 3 = "disabledLocked"
# @api private
setButtonPreview: (selected_radio) ->
icons = @$el.find("label[for=button-#{@cid}-#{selected_radio}] i").clone()
@$el.find('a.dropdown-toggle').html icons
$icons = @$el.find("label[for=button-#{@cid}-#{selected_radio}] i").clone()
@$el.find('a.dropdown-toggle').html $icons
# Method Summary
# Sets the button preview for an dropdown button to the enabled icons

View File

@ -5,10 +5,11 @@ define [
'Backbone'
'compiled/views/tinymce/EquationToolbarView'
'jst/tinymce/EquationEditorView'
'str/htmlEscape'
'jqueryui/dialog'
'mathquill'
], (I18n, $, _, Backbone, EquationToolbarView, template) ->
], (I18n, $, _, Backbone, EquationToolbarView, template, htmlEscape) ->
# like $.text() / Sizzle.getText(elems), except it also gets alt attributes from images
getEquationText = (elems) ->
@ -46,7 +47,7 @@ define [
@prevSelection = @editor.selection.getBookmark()
unless @toolbar = @$el.data('toolbar')
nodes = $('<span>').html @editor.selection.getContent()
nodes = $('<span>').text @editor.selection.getContent()
equation = getEquationText(nodes).replace(/^\s+|\s+$/g, '')
@addToolbar(equation)
@ -73,7 +74,7 @@ define [
@restoreCaret()
initialRender: =>
nodes = $('<span>').html @editor.selection.getContent()
nodes = $('<span>').text @editor.selection.getContent()
equation = getEquationText(nodes).replace(/^\s+|\s+$/g, '')
@$mathjaxMessage.empty()
@ -83,7 +84,7 @@ define [
addToolbar: (equation) ->
@$el.append(@template)
$('#mathjax-preview').html("<script type='math/tex; mode=display'>#{equation}</script>")
$('#mathjax-preview').html("<script type='math/tex; mode=display'>#{htmlEscape equation}</script>")
@toolbar = new EquationToolbarView
el: @$el
@toolbar.render()
@ -138,7 +139,7 @@ define [
.mathquill('editor')
.mathquill('write', equation)
if @$mathquillContainer.mathquill('latex').replace(/\s+/, '') != equation.replace(/\s+/, '')
@$mathjaxMessage.html(I18n.t('cannot_render_equation', "This equation cannot be rendered in Basic View."))
@$mathjaxMessage.text(I18n.t('cannot_render_equation', "This equation cannot be rendered in Basic View."))
return false
else if view == 'mathjax'
@$mathjaxEditor.val(equation)

View File

@ -131,10 +131,10 @@ define [
@setSelectedImage src: $(event.currentTarget).val()
generateImageHtml: ->
img_tag = @editor.dom.createHTML("img", @getAttributes())
imgHtml = @editor.dom.createHTML("img", @getAttributes())
if @flickr_link
img_tag = "<a href='#{@flickr_link}'>#{img_tag}</a>"
img_tag
imgHtml = "<a href='#{h @flickr_link}'>#{imgHtml}</a>"
imgHtml
update: =>
@editor.selection.moveToBookmark(@prevSelection)

View File

@ -7,7 +7,7 @@ define [
class WikiPageReloadView extends Backbone.View
setViewProperties: false
template: -> "<div class='alert alert-#{if @options.warning then 'warning' else 'info'} reload-changed-page'>#{@reloadMessage}</div>"
template: -> "<div class='alert alert-#{$.raw if @options.warning then 'warning' else 'info'} reload-changed-page'>#{$.raw @reloadMessage}</div>"
defaults:
modelAttributes: ['title', 'url', 'body']

View File

@ -116,7 +116,7 @@ define [
# @api private
# Build an <option> tag for an item.
_buildOption: (item) =>
"<option value='#{@_value item}'>#{htmlEscape @_label item}</option>"
"<option value='#{htmlEscape @_value item}'>#{htmlEscape @_label item}</option>"
##
# @api private

View File

@ -68,7 +68,8 @@ define [
$name.append($b, $contextInfo)
$span = $('<span />', class: 'details')
if data.common_courses?
$span.html(@contextList(courses: data.common_courses, groups: data.common_groups))
contextListHtml = (@contextList(courses: data.common_courses, groups: data.common_groups))
$span.html contextListHtml
else if data.user_count?
$span.text(I18n.t('people_count', 'person', {count: data.user_count}))
else if data.item_count?

View File

@ -97,10 +97,10 @@ define [
animateGhost: (fromElement, toElement) ->
from = fromElement.offset()
to = toElement.offset()
clone = fromElement.clone()
$clone = fromElement.clone()
from.position = 'absolute'
@ghost.append(clone)
@ghost.append($clone)
@ghost.appendTo(@doc).css(from).animate to, @options.animationDuration, =>
@ghost.detach().empty()

View File

@ -1,9 +1,10 @@
define [
'i18n!rubrics'
'jquery'
'str/htmlEscape'
'jqueryui/dialog'
'vendor/jquery.ba-tinypubsub'
], (I18n, $) ->
], (I18n, $, htmlEscape) ->
assignmentRubricDialog =
@ -23,7 +24,7 @@ define [
initDialog: ->
@dialogInited = true
@$dialog = $("<div><h4>#{I18n.t 'loading', 'Loading...'}</h4></div>").dialog
@$dialog = $("<div><h4>#{htmlEscape I18n.t 'loading', 'Loading...'}</h4></div>").dialog
title: I18n.t("titles.assignment_rubric_details", "Assignment Rubric Details")
width: 600
modal: false

View File

@ -10,85 +10,85 @@ define [
constructor: (title, slideshow) ->
@title = title
@slideshow = slideshow
@body = $('<li/>').addClass('slide')
@slideshow.slides.append(@body)
@$body = $('<li/>').addClass('slide')
@slideshow.slides.append(@$body)
@prevSlide = @slideshow.slideObjects[@slideshow.slideObjects.length - 1]
@prevSlide?.nextSlide = this
@nextSlide = null
@indicator = $("<a/>").addClass('slide').attr('href', '#').attr('title', htmlEscape(@title)).html('&nbsp;')
@indicator.data('slide', this)
@slideshow.navigation.append(@indicator)
@$indicator = $("<a/>").addClass('slide').attr('href', '#').attr('title', htmlEscape(@title)).html('&nbsp;')
@$indicator.data('slide', this)
@slideshow.navigation.append(@$indicator)
slide = this
@indicator.click ->
@$indicator.click ->
slideshow.showSlide(slide)
return false
@hide()
addParagraph: (text, klass) ->
paragraph = $("<p/>")
paragraph.addClass(klass) if klass?
paragraph.html(htmlEscape(text))
@body.append(paragraph)
$paragraph = $("<p/>")
$paragraph.addClass(klass) if klass?
$paragraph.html(htmlEscape(text))
@$body.append($paragraph)
addImage: (src, klass, url) ->
image = $("<img/>").attr('src', src)
image.addClass(klass) if klass?
$image = $("<img/>").attr('src', src)
$image.addClass(klass) if klass?
if url
link = $("<a/>").attr('href', url).attr('target', '_blank')
link.append(image)
@body.append(link)
$link = $("<a/>").attr('href', url).attr('target', '_blank')
$link.append($image)
@$body.append($link)
else
@body.append(image)
@$body.append($image)
show: ->
@body.show()
@indicator.addClass('current_slide')
@$body.show()
@$indicator.addClass('current_slide')
hide: ->
@indicator.removeClass('current_slide')
@body.hide()
@$indicator.removeClass('current_slide')
@$body.hide()
class Slideshow
constructor: (id) ->
slideshow = this
@dom = $('<div/>').attr('id', id)
@$dom = $('<div/>').attr('id', id)
@slides = $('<ul/>').addClass('slides')
@dom.append(@slides)
@$slides = $('<ul/>').addClass('slides')
@$dom.append(@$slides)
@separator = $("<div/>").addClass('separator')
@dom.append(@separator)
@$separator = $("<div/>").addClass('separator')
@$dom.append(@$separator)
@navigation = $("<div/>").addClass('navigation')
@dom.append(@navigation)
@$navigation = $("<div/>").addClass('navigation')
@$dom.append(@$navigation)
@backButton = $("<a/>").addClass('back').
@$backButton = $("<a/>").addClass('back').
attr('href', '#').
attr('title', htmlEscape(I18n.t('titles.back', 'Back'))).
attr('title', I18n.t('titles.back', 'Back')).
html('&nbsp;')
@navigation.append(@backButton)
@backButton.click ->
@$navigation.append(@$backButton)
@$backButton.click ->
slideshow.showPrevSlide()
return false
@forwardButton = $("<a/>").addClass('forward').
@$forwardButton = $("<a/>").addClass('forward').
attr('href', '#').
attr('title', htmlEscape(I18n.t('titles.forward', 'Forward'))).
attr('title', I18n.t('titles.forward', 'Forward')).
html('&nbsp;')
@navigation.append(@forwardButton)
@forwardButton.click ->
@$navigation.append(@$forwardButton)
@$forwardButton.click ->
slideshow.showNextSlide()
return false
@closeButton = $("<a/>").addClass('close').
@$closeButton = $("<a/>").addClass('close').
attr('href', '#').
attr('title', htmlEscape(I18n.t('titles.close', 'Close'))).
attr('title', I18n.t('titles.close', 'Close')).
html('&nbsp;')
@navigation.append(@closeButton)
@closeButton.click ->
@$navigation.append(@$closeButton)
@$closeButton.click ->
slideshow.close()
return false
@ -102,7 +102,7 @@ define [
start: ->
@showSlide(@slideObjects[0])
@dialog = @dom.dialog
@$dialog = @$dom.dialog
dialogClass: 'slideshow_dialog'
height: 529
width: 700
@ -117,13 +117,13 @@ define [
@slideShown = slide
@slideShown.show()
if @slideShown.prevSlide
@backButton.removeClass('inactive')
@$backButton.removeClass('inactive')
else
@backButton.addClass('inactive')
@$backButton.addClass('inactive')
if @slideShown.nextSlide
@forwardButton.removeClass('inactive')
@$forwardButton.removeClass('inactive')
else
@forwardButton.addClass('inactive')
@$forwardButton.addClass('inactive')
showPrevSlide: ->
@showSlide(@slideShown?.prevSlide)
@ -132,8 +132,8 @@ define [
@showSlide(@slideShown?.nextSlide)
close: ->
@dom.dialog('close')
@dom.hide()
@$dom.dialog('close')
@$dom.hide()
@slideShown?.hide()
@slideShown = null

View File

@ -20,6 +20,10 @@
# Populates a <select> element with data from an ajax call. Takes two arguments.
# The first is a <select> element, the second is an options hash
###
xsslint jqueryObject.property placeholder spinner
###
define [
'jquery'
'underscore'

View File

@ -15,18 +15,20 @@
"react-tools": "0.11.2"
},
"devDependencies": {
"loom": "~2.0.0",
"fleck": "~0.5.1",
"testem": "~0.3.30",
"karma-qunit": "~0.1.1",
"gglobby": "0.0.2",
"karma": "~0.10.9",
"karma-safari-launcher": "~0.1.1",
"karma-opera-launcher": "~0.1.0",
"karma-coffee-preprocessor": "0.1.3",
"karma-coverage": "~0.1.4",
"karma-ie-launcher": "~0.1.1",
"karma-osx-reporter": "0.0.4",
"karma-firework-reporter": "~0.2.3",
"karma-coffee-preprocessor": "0.1.3"
"karma-ie-launcher": "~0.1.1",
"karma-opera-launcher": "~0.1.0",
"karma-osx-reporter": "0.0.4",
"karma-qunit": "~0.1.1",
"karma-safari-launcher": "~0.1.1",
"loom": "~2.0.0",
"testem": "~0.3.30",
"xsslint": "0.0.2"
},
"repository": {
"type": "git",

View File

@ -0,0 +1,16 @@
# top level, and plugin ones too
jst
/bower
/jsx/.module-cache
/jsx/sample.js
/mediaelement
/tinymce/jscripts/tiny_mce/langs
/tinymce/jscripts/tiny_mce/plugins/*
!/tinymce/jscripts/tiny_mce/plugins/instructure*
/tinymce/jscripts/tiny_mce/themes
/tinymce/jscripts/tiny_mce/tiny_mce*.js
/tinymce/jscripts/tiny_mce/utils
/vendor
# need better hinting, skipping this atm due to false positives in vendor code
/client_apps

View File

@ -61,7 +61,7 @@ define([
$('#discovery_url_config .delete_url').click(function(){
$.ajaxJSON( $(this).data('url'), "DELETE", {}, function(){
$('#discovery_url_input').val("");
$('#discovery_url_display').html(I18n.t('no_discovery_url', "None set"));
$('#discovery_url_display').text(I18n.t('no_discovery_url', "None set"));
});
});
@ -78,4 +78,4 @@ define([
$("#secondary_ldap_config_disabled").val("1");
$(".add_secondary_ldap_link").show();
});
});
});

View File

@ -108,8 +108,8 @@ define([
} catch(e) {}
$.ajaxJSON(location.protocol + '//' + location.host + "/simple_response.json?rnd=" + Math.round(Math.random() * 9999999), 'GET', {}, function() {
if ($.ajaxJSON.isUnauthenticated(request)) {
var message = I18n.t('errors.logged_out', "You are not currently logged in, possibly due to a long period of inactivity.")
message += "<br\/><a href='/login' target='_new'>" + I18n.t('links.login', 'Login') + "<\/a>";
var message = htmlEscape(I18n.t('errors.logged_out', "You are not currently logged in, possibly due to a long period of inactivity."))
message += "<br\/><a href='/login' target='_new'>" + htmlEscape(I18n.t('links.login', 'Login')) + "<\/a>";
$.flashError({ html: message }, 30000);
} else {
ajaxErrorFlash(I18n.t('errors.unhandled', "Oops! The last request didn't work out."), request);
@ -126,12 +126,12 @@ define([
window.frames[$obj.attr('id')].document;
var $body = $(d).find("body");
$body.html($("<h1 />").text(I18n.t('error_heading', 'Ajax Error: %{status_code}', {status_code: status})));
$body.append(text);
$body.append(htmlEscape(text));
$("#instructure_ajax_error_box").hide();
var pre = "";
message = htmlEscape(message);
if(debugOnly) {
message = message + "<br\/><span style='font-size: 0.7em;'>(Development Only)<\/span>";
message += "<br\/><span style='font-size: 0.7em;'>(Development Only)<\/span>";
}
if(debugOnly || INST.environment != "production") {
message += "<br\/><a href='#' class='last_error_details_link'>" + htmlEscape(I18n.t('links.details', 'details...')) + "<\/a>";
@ -161,7 +161,7 @@ define([
"&Platform=" + escape(navigator.platform) +
"&UserAgent=" + escape(navigator.userAgent) +
"&Params=" + escape(data.params || "unknown");
$("body").append("<img style='position: absolute; left: -1000px; top: 0;' src='" + INST.ajaxErrorURL + txt.substring(0, 2000) + "' />");
$("body").append("<img style='position: absolute; left: -1000px; top: 0;' src='" + htmlEscape(INST.ajaxErrorURL + txt.substring(0, 2000)) + "' />");
}
});
$(".last_error_details_link").live('click', function(event) {

View File

@ -152,7 +152,7 @@ define([
if ( data.multiple_due_dates === "true" && id !== 'assignment_new' ) {
var $dateInput = $form.find('.input-append');
$dateInput.before($("<span class=vdd_no_edit>" +
I18n.t('multiple_due_dates','Multiple Due Dates')+
htmlEscape(I18n.t('multiple_due_dates','Multiple Due Dates'))+
"</span>"));
$dateInput.hide();
$form.find('.ui-datepicker-trigger').hide();
@ -594,7 +594,7 @@ define([
$(this).hide();
$(this).parents(".assignment_group").find(".hide_info_link").show().end()
.find(".more_info").show();
var rules = "";
var rulesHtml = "";
var ruleData = $(this).parents(".assignment_group").find(".rules").text().split("\n");
$.each(ruleData, function(idx, rule) {
var parts = rule.split(":");
@ -602,16 +602,16 @@ define([
var rule_type = parts[0];
var value = parts[1];
if(rule_type == "drop_lowest") {
rules += htmlEscape(I18n.t('drop_lowest_scores', "Drop the Lowest %{number} Scores", {number: value})) + "<br/>";
rulesHtml += htmlEscape(I18n.t('drop_lowest_scores', "Drop the Lowest %{number} Scores", {number: value})) + "<br/>";
} else if(rule_type == "drop_highest") {
rules += htmlEscape(I18n.t('drop_highest_scores', "Drop the Highest %{number} Scores", {number: value})) + "<br/>";
rulesHtml += htmlEscape(I18n.t('drop_highest_scores', "Drop the Highest %{number} Scores", {number: value})) + "<br/>";
} else if(rule_type == "never_drop") {
var title = $("#assignment_" + value).find(".title").text();
rules += htmlEscape(I18n.t('never_drop_scores', "Never Drop %{assignment_name}", {assignment_name: title})) + "<br/>";
rulesHtml += htmlEscape(I18n.t('never_drop_scores', "Never Drop %{assignment_name}", {assignment_name: title})) + "<br/>";
}
}
});
$(this).parents(".assignment_group").find(".rule_details").html(rules);
$(this).parents(".assignment_group").find(".rule_details").html(rulesHtml);
});
$(".hide_info_link").click(function(event) {
event.preventDefault();

View File

@ -730,7 +730,7 @@ define([
data.description = $event.data('description');
$box.fillTemplateData({data: data, htmlValues: ['description']});
if (data.lock_info) {
$box.find(".lock_explanation").html(INST.lockExplanation(data.lock_info, 'assignment'));
$box.find(".lock_explanation").html(htmlEscape(INST.lockExplanation(data.lock_info, 'assignment')));
}
if ($editEvent.data('dialog')) $editEvent.dialog('close');
$box.find(".description").css("max-height", Math.max($(window).height() - 200, 150));

View File

@ -18,10 +18,11 @@
define([
'i18n!content_exports',
'jquery' /* $ */,
'str/htmlEscape',
'jquery.ajaxJSON' /* ajaxJSON */,
'jquery.instructure_forms' /* formSubmit */,
'jqueryui/progressbar' /* /\.progressbar/ */
], function(I18n, $) {
], function(I18n, $, htmlEscape) {
$(document).ready(function(event) {
var state = 'nothing';
@ -30,7 +31,7 @@ $(document).ready(function(event) {
$exporter_form = $('#exporter_form');
function startPoll() {
$exporter_form.html(I18n.t('messages.processing', "Processing") + "<div style='font-size: 0.8em;'>" + I18n.t('messages.this_may_take_a_bit', "this may take a bit...") + "</div>")
$exporter_form.html(htmlEscape(I18n.t('messages.processing', "Processing")) + "<div style='font-size: 0.8em;'>" + htmlEscape(I18n.t('messages.this_may_take_a_bit', "this may take a bit...")) + "</div>")
.attr('disabled', true);
$(".instruction").hide();
$(".progress_bar_holder").slideDown();
@ -65,14 +66,14 @@ $(document).ready(function(event) {
if(content_export.workflow_state == 'exported') {
$exporter_form.hide();
$(".export_progress").progressbar('option', 'value', 100);
$(".progress_message").html("Your content has been exported.");
$("#export_files").append('<p>' + I18n.t('labels.new_export', "New Export:") + ' <a href="' + content_export.download_url + '">' + I18n.t('links.download_plain', "Click here to download") + '</a> </p>')
$(".progress_message").text(I18n.t("Your content has been exported."));
$("#export_files").append('<p>' + htmlEscape(I18n.t('labels.new_export', "New Export:")) + ' <a href="' + htmlEscape(content_export.download_url) + '">' + htmlEscape(I18n.t('links.download_plain', "Click here to download")) + '</a> </p>')
} else if(content_export.workflow_state == 'failed') {
code = "content_export_" + content_export.id;
$(".progress_bar_holder").hide();
$exporter_form.hide();
var message = I18n.t('errors.error', "There was an error exporting your content. Please notify your system administrator and give them the following export identifier: \"%{code}\"", {code: code});
$(".export_messages .error_message").html(message);
$(".export_messages .error_message").text(message);
$(".export_messages").show();
} else {
if(progress == lastProgress) {

View File

@ -193,7 +193,8 @@ define([
}
folderNames = folderNames.sort();
for (var idx in folderNames) {
$("#copy_files_list").append(folders[folderNames[idx]]);
var $folder = folders[folderNames[idx]];
$("#copy_files_list").append($folder);
}
}
populateItem(null, null, null, null);

View File

@ -82,10 +82,10 @@ define([
}
if ($("#context_modules_url").length > 0) {
html += "<br/>";
html += "<a href='" + $("#context_modules_url").attr('href') + "'>";
html += I18n.t('messages.visit_modules_page_for_details', "Visit the modules page for information on how to unlock this content.");
html += "<a href='" + htmlEscape($("#context_modules_url").attr('href')) + "'>";
html += htmlEscape(I18n.t('messages.visit_modules_page_for_details', "Visit the modules page for information on how to unlock this content."));
html += "</a>";
return html;
return $.raw(html);
}
}
else {
@ -113,7 +113,7 @@ define([
var data = $(this).data('lock_reason');
var type = data.type;
var $reason = $("<div/>");
$reason.html(INST.lockExplanation(data, type));
$reason.html(htmlEscape(INST.lockExplanation(data, type)));
var $dialog = $("#lock_reason_dialog");
if($dialog.length === 0) {
$dialog = $("<div/>").attr('id', 'lock_reason_dialog');

View File

@ -266,7 +266,7 @@ define([
moduleSelectOptions.push('<option value="' + id + '">' + htmlEscape(name) + '</option>');
});
$('#move_module_item_module_select').empty();
$('#move_module_item_module_select').append(moduleSelectOptions.join(''));
$('#move_module_item_module_select').append($.raw(moduleSelectOptions.join('')));
// Trigger the change to make sure the list is initally populated.
$('#move_module_item_module_select').trigger('change');
@ -307,7 +307,7 @@ define([
var data = $module.getTemplateData({textValues: ['name', 'unlock_at', 'require_sequential_progress', 'publish_final_grade']});
$('#move_context_module_select').empty();
$('#move_context_module_select').append(selectOptions.join(''));
$('#move_context_module_select').append($.raw(selectOptions.join('')));
//$form.fillFormData(data, {object_name: 'context_module'});
$form.dialog({
autoOpen: false,
@ -960,7 +960,7 @@ define([
var name = $(item).children().find('span.title').text();
selectItemOptions.push('<option value="' + id + '">' + htmlEscape(name) + '</option>');
});
$('#move_module_item_select').append(selectItemOptions.join(''));
$('#move_module_item_select').append($.raw(selectItemOptions.join('')));
// The case where the module has no items.
if ($('#move_module_item_select').children().length === 0) {
@ -1550,7 +1550,7 @@ define([
});
$row.find(".still_need_completing")
.append("<b>"+htmlEscape(I18n.t('still_needs_completing', 'Still Needs to Complete'))+"</b><br/>")
.append(unfulfilled.join("<br/>"));
.append($.raw(unfulfilled.join("<br/>")));
}
$row.removeClass('locked').removeClass('in_progress').removeClass('completed')
.addClass(type);

View File

@ -52,22 +52,22 @@ define([
$publish_grades_error = $("#publish_grades_error");
if (GradePublishing.status == 'published') {
$publish_grades_error.hide();
$publish_grades_link.html(I18n.t('links.republish', "Republish grades to SIS"));
$publish_grades_link.text(I18n.t('links.republish', "Republish grades to SIS"));
$publish_grades_link.removeClass("disabled");
} else if (GradePublishing.status == 'publishing' || GradePublishing.status == 'pending') {
$publish_grades_error.hide();
$publish_grades_link.html(I18n.t('links.publishing', "Publishing grades to SIS..."));
$publish_grades_link.text(I18n.t('links.publishing', "Publishing grades to SIS..."));
if (!requestInProgress) {
setTimeout(GradePublishing.checkup, 5000);
}
$publish_grades_link.addClass("disabled");
} else if (GradePublishing.status == 'unpublished') {
$publish_grades_error.hide();
$publish_grades_link.html(I18n.t('links.publish', "Publish grades to SIS"));
$publish_grades_link.text(I18n.t('links.publish', "Publish grades to SIS"));
$publish_grades_link.removeClass("disabled");
} else {
$publish_grades_error.show();
$publish_grades_link.html(I18n.t('links.republish', "Republish grades to SIS"));
$publish_grades_link.text(I18n.t('links.republish', "Republish grades to SIS"));
$publish_grades_link.removeClass("disabled");
}
$messages = $("#publish_grades_messages");

View File

@ -16,6 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// xsslint jqueryObject.method _createCell _templateCellHTML
define([
'INST' /* INST */,
'jquery' /* $ */,

View File

@ -199,7 +199,7 @@ define([
if(section_type == "rich_text") {
code = $(this).find(".edit_section").editorBox('get_code');
}
$(this).find(".section_content").html(code);
$(this).find(".section_content").html($.raw(code));
} else if(!$(this).hasClass('read_only')) {
$(this).remove();
}
@ -416,7 +416,7 @@ define([
},
error: function(data) {
var $section = $(this).data("section");
$section.find(".uploading_file").html(I18n.t('errors.upload_failed', "Upload Failed."));
$section.find(".uploading_file").text(I18n.t('errors.upload_failed', "Upload Failed."));
$section.addClass('failed');
$section.formErrors(data.errors || data);
}

View File

@ -192,7 +192,7 @@ define([
importFailed(zfi.data.errors);
} else if(zfi && zfi.workflow_state == 'imported') {
$progress.progressbar('value', 100);
$dialog.append(I18n.t('messages.extraction_complete', "Extraction complete! Updating..."));
$dialog.append(htmlEscape(I18n.t('messages.extraction_complete', "Extraction complete! Updating...")));
files.refreshContext(folder.context_string, function() {
$dialog.dialog('close');
});
@ -587,6 +587,7 @@ define([
$(ui.helper).find(".header .sub_header").html("&nbsp;");
}
},
// xsslint jqueryObject.method breadcrumb
breadcrumb: function() {
var folders = location.hash.substring(1).replace(/\/\//g, "\\").split("/");
var $crumbs = $("<div/>");
@ -1413,11 +1414,11 @@ define([
if(node.hasClass('node')) {
var folder_url = $.replaceTags($("." + data.context_string + "_folder_url").attr('href'), 'id', data.id);
var cancelled = false;
var $no_content = $("<li class='message'>" + I18n.t('messages.folder_empty', "Nothing in this Folder") + "</li>");
var $no_content = $("<li class='message'>" + htmlEscape(I18n.t('messages.folder_empty', "Nothing in this Folder")) + "</li>");
if(node.hasClass('folder')) {
if(!data || !data.permissions || !data.permissions.read_contents) {
$files_content.find(".content_panel:last")
.after("<li class='message'>" + I18n.t('messages.access_denied', "You cannot read the contents of this folder.") + "</li>");
.after("<li class='message'>" + htmlEscape(I18n.t('messages.access_denied', "You cannot read the contents of this folder.")) + "</li>");
cancelled = true;
} else {
// add a control panel to the top for adding files, folders to this

Some files were not shown because too many files have changed in this diff Show More