clean up $.serializeForm and make it Just Work™, fixes CNVS-7407

the initial implementation had an order-of-operations bug, causing most
disabled or un-named inputs to be serialized, as well as unchecked
checkboxes and radio buttons. radio buttons were also serialized as bools,
which makes absolutely no sense. these weird issues inspired little
patches (and regressions), but the the major underlying problems were
never really fixed, so here we are.

this commit resets $.serializeForm to a straight coffeescript port of
$.serializeArray (no underscore conversion), with just the things we need:

1. a canvas-friendly value mapper (for date pickers, files and tinymce)
2. support for non-forms

additionally, change (almost) every checkbox field in handlebars templates
to use the {{checkbox}} helper (so you get the hidden input right before
it, a la rails)

a notable side effect is that checkboxes are no longer serialized as
booleans, rather as "1" / "0" (or whatever the value is, per the html
spec). so now you can use checkboxes for non-bool scenarios (e.g. multiple
selection), which is nice. the big takeaway is you should *not* do truthy
checks on checkbox values in coffeescript moving forward if you use the
{{checkbox}} helper, since "0" is truthy.

test plan:
1. run specs
2. regression test of checkbox functionality in the following places:
   * gradebook2 dialogs w/ checkboxes, specifically:
     * group weights dialog
     * curve grades dialog
     * submission details
   * create/edit assignment (lots of checkboxes)
     * don't forget turnitin settings
   * create/edit appointment group (scheduler)
   * edit timeslot (scheduler)
   * edit calendar event (specifically section-level dates)
   * create/edit conference
   * conversations (group convo / faculty journal options)
   * add course users dialog (limit-to-section option)
   * discussion settings dialog (index page)
   * create/edit discussion topic (various options)
   * notification preferences (just a couple checkboxes)
3. regression of radio button functionality in the following places:
   * content migrations
   * delete assignment group dialog

Change-Id: Ifa5ef3cea954ca260a899da9fa426f8f833472f8
Reviewed-on: https://gerrit.instructure.com/23096
Tested-by: Jenkins <jenkins@instructure.com>
Product-Review: Marc LeGendre <marc@instructure.com>
QA-Review: Marc LeGendre <marc@instructure.com>
Reviewed-by: Landon Wilkins <lwilkins@instructure.com>
Reviewed-by: Ryan Florence <ryanf@instructure.com>
This commit is contained in:
Jon Jensen 2013-08-06 16:42:42 -06:00
parent 9f93efbdd8
commit c25df1cc50
41 changed files with 305 additions and 327 deletions

View File

@ -20,7 +20,10 @@ define [
$(selector).html editAppointmentGroupTemplate({
title: @apptGroup.title
contexts: @contexts
appointment_group: @apptGroup
appointment_group: _.extend(
{use_group_signup: @apptGroup.participant_type is 'Group'}
@apptGroup
)
})
@contextsHash = {}
@ -68,7 +71,7 @@ define [
@form.find(".group-signup").toggle(checked)
@form.find(".group-signup-checkbox").change()
$perSlotCheckbox = @form.find('[name="per_slot_option"]')
$perSlotCheckbox = @form.find('#appointment-blocks-per-slot-option-button')
$perSlotInput = @form.find('[name="participants_per_appointment"]')
slotChangeHandler = (e) => @perSlotChange($perSlotCheckbox, $perSlotInput)
$.merge($perSlotCheckbox, $perSlotInput).on 'change', slotChangeHandler
@ -149,7 +152,7 @@ define [
'appointment_group[location_name]': data.location
}
if data.max_appointments_per_participant_option
if data.max_appointments_per_participant_option is '1'
if data.max_appointments_per_participant < 1
$('[name="max_appointments_per_participant"]').errorBox(
I18n.t('bad_max_appts', 'You must allow at least one appointment per participant'))
@ -167,7 +170,7 @@ define [
$.dateToISO8601UTC($.unfudgeDateForProfileTimezone(range[1]))
])
if data.per_slot_option
if data.per_slot_option is '1'
if data.participants_per_appointment < 1
$('[name="participants_per_appointment"]').errorBox(
I18n.t('bad_per_slot', 'You must allow at least one appointment per time slot'))

View File

@ -9,7 +9,7 @@ define [
.html(editApptCalendarEventTemplate(@event))
.appendTo('body')
$maxParticipantsOption = @form.find('[name=max_participants_option]')
$maxParticipantsOption = @form.find('[type=checkbox][name=max_participants_option]')
@$maxParticipants = @form.find('[name=max_participants]')
$maxParticipantsOption.change =>
@ -36,7 +36,7 @@ define [
save: =>
formData = @dialog.getFormData()
limit_participants = formData.max_participants_option == "on"
limit_participants = formData.max_participants_option == "1"
max_participants = formData.max_participants
if limit_participants and max_participants <= 0

View File

@ -21,7 +21,7 @@ define [
events:
'submit form': 'submit'
'change [name="use_section_dates"]': 'toggleUseSectionDates'
'change #use_section_dates': 'toggleUseSectionDates'
'click .delete_link': 'destroyModel'
'click .switch_event_description_view': 'toggleHtmlView'
@ -75,12 +75,11 @@ define [
submit: (event) ->
event?.preventDefault()
eventData = unflatten @$el.getFormData()
# force use_section_dates to boolean, so it doesnt cause 'change' if it is '1'
eventData.use_section_dates = !!eventData.use_section_dates
eventData.use_section_dates = eventData.use_section_dates is '1'
_.each [eventData].concat(eventData.child_event_data), @setStartEnd
delete eventData.child_event_data if eventData.remove_child_events == '1'
if $('[name=use_section_dates]').prop('checked')
if $('#use_section_dates').prop('checked')
dialog = new MissingDateDialogView
validationFn: ->
$fields = $('[name*=start_date]:visible').filter -> $(this).val() is ''

View File

@ -128,7 +128,7 @@ define [
for key, enabled of options
$node = @$form.find(".#{key}_info")
$node.showIf(enabled)
$node.find("input[name=#{key}]").prop('checked', false) unless enabled
$node.find("input[type=checkbox][name=#{key}]").prop('checked', false) unless enabled
toggle: (state) ->
@$form[if state then 'addClass' else 'removeClass']('disabled')

View File

@ -248,6 +248,11 @@ define [
checkbox: (propertyName, {hash}) ->
splitPropertyName = propertyName.split(/\./)
snakeCase = splitPropertyName.join('_')
if hash.prefix
splitPropertyName.unshift hash.prefix
delete hash.prefix
bracketNotation = splitPropertyName[0] + _.chain(splitPropertyName)
.rest()
.map((prop) -> "[#{prop}]")
@ -260,11 +265,19 @@ define [
name: bracketNotation
, hash
unless inputProps.checked
value = _.reduce splitPropertyName, ((memo, key) -> memo[key]), this
unless inputProps.checked?
value = _.reduce(splitPropertyName, ((memo, key) -> memo[key] if memo?), this)
inputProps.checked = true if value
attributes = _.map inputProps, (val, key) -> "#{htmlEscape key}=\"#{htmlEscape val}\""
for prop in ['checked', 'disabled']
if inputProps[prop]
inputProps[prop] = prop
else
delete inputProps[prop]
attributes = for key, val of inputProps when val?
"#{htmlEscape key}=\"#{htmlEscape val}\""
new Handlebars.SafeString """
<input name="#{htmlEscape inputProps.name}" type="hidden" value="0" />
<input #{attributes.join ' '} />

View File

@ -1,52 +1,58 @@
define [
'jquery'
'underscore'
], ($, _) ->
$.fn.serializeForm = ->
define ['jquery'], ($) ->
rselectTextarea = /^(?:select|textarea)/i
rcheckboxOrRadio = /checkbox|radio/i
rCRLF = /\r?\n/g
rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week|checkbox|radio|file)$/i
rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week|file)$/i
# radio / checkbox are not included, since they are handled by the @checked check
isInput = (el) ->
el.name && !el.disabled && rselectTextarea.test(el.nodeName) or rinput.test(el.type)
elements = ->
if @elements
$.makeArray @elements
else
elements = $(this).find(':input')
if elements.length
elements
else
this
if this.is('[serialize-radio-value]')
rcheckboxOrRadio = /checkbox/i # return val for radio boxes
rRadio = /radio/i
isSerializable = ->
@name and not @disabled and (
@checked or
rselectTextarea.test(@nodeName) or
rinput.test(@type)
)
_isInput = isInput
isInput = (el) -> # only include the checked radio input
_isInput(el) && (!rRadio.test(el.type) || el.checked)
resultFor = (name, value) ->
value = value.replace(rCRLF, "\r\n") if typeof value is 'string'
{name, value}
getValue = (el) ->
resultFor = (val) ->
name: el.name
el: el
value: if _.isString(val) then val.replace( rCRLF, "\r\n" ) else val
$input = $(el)
val = if rcheckboxOrRadio.test(el.type)
el.checked
else if el.type == 'file'
el if $input.val()
getValue = ->
$input = $(this)
value = if @type == 'file'
this if $input.val()
else if $input.hasClass 'datetime_field_enabled'
# datepicker doesn't clear the data date attribute when a date is deleted
if $input.val() == ""
if $input.val() is ""
null
else
$input.data('date') || null
$input.data('date') or null
else if $input.data('rich_text')
$input.editorBox('get_code', false)
else
$input.val()
if _.isArray val
_.map val, resultFor
if $.isArray(value)
resultFor(@name, val) for val in value
else
resultFor val
resultFor(@name, value)
_.chain(this[0].elements || this.find(':input'))
.filter(isInput)
##
# identical to $.fn.serializeArray, except:
# 1. it works on non-forms (see elements)
# 2. it handles file, date picker and tinymce inputs (see getValue)
$.fn.serializeForm = ->
@map(elements)
.filter(isSerializable)
.map(getValue)
.value()
.get()

View File

@ -55,7 +55,8 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
isAnnouncement: => @model.constructor is Announcement
toJSON: ->
json = _.extend super, @options,
data = super
json = _.extend data, @options,
showAssignment: !!@assignmentGroupCollection
useForGrading: @model.get('assignment')?
isTopic: @isTopic()
@ -64,6 +65,7 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
canAttach: @permissions.CAN_ATTACH
canModerate: @permissions.CAN_MODERATE
isLargeRoster: ENV?.IS_LARGE_ROSTER || false
threaded: data.discussion_type is "threaded"
json.assignment = json.assignment.toView()
json
@ -130,14 +132,14 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
getFormData: ->
data = super
data.title ||= I18n.t 'default_discussion_title', 'No Title'
data.discussion_type = if data.threaded then 'threaded' else 'side_comment'
data.podcast_has_student_posts = false unless data.podcast_enabled
data.discussion_type = if data.threaded is '1' then 'threaded' else 'side_comment'
data.podcast_has_student_posts = false unless data.podcast_enabled is '1'
assign_data = data.assignment
delete data.assignment
if assign_data?.set_assignment
data.set_assignment = true
if assign_data?.set_assignment is '1'
data.set_assignment = '1'
data.assignment = @updateAssignment(assign_data)
data.delayed_post_at = ''
data.lock_at = ''
@ -146,12 +148,9 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
# DiscussionTopics get a model created for them in their
# constructor. Delete it so the API doesn't automatically
# create assignments unless the user checked "Use for Grading".
# We're doing this here because syncWithMultipart doesn't call
# the model's toJSON method unfortunately, so assignment params
# would be sent in the response, creating an assignment.
# The controller checks for set_assignment on the assignment model,
# so we can't make it undefined here for the case of discussion topics.
data.assignment = {set_assignment: false}
data.assignment = {set_assignment: '0'}
# these options get passed to Backbone.sync in ValidatedFormView
@saveOpts = multipart: !!data.attachment, proxyAttachment: true
@ -203,7 +202,7 @@ htmlEscape, DiscussionTopic, Announcement, Assignment, $, preventDefault, Missin
)
validateBeforeSave: (data, errors) =>
if @isTopic() && data.set_assignment
if @isTopic() && data.set_assignment is '1'
if @assignmentGroupSelector?
errors = @assignmentGroupSelector.validateBeforeSave(data, errors)
unless ENV?.IS_LARGE_ROSTER

View File

@ -35,11 +35,12 @@ AssignmentGroupSelector, GroupCategorySelector, toggleAccessibly) ->
SUBMISSION_TYPE = '[name="submission_type"]'
ONLINE_SUBMISSION_TYPES = '#assignment_online_submission_types'
NAME = '[name="name"]'
ALLOW_FILE_UPLOADS = '[name="online_submission_types[online_upload]"]'
RESTRICT_FILE_UPLOADS = '#restrict_file_extensions_container'
ALLOW_FILE_UPLOADS = '#assignment_online_upload'
RESTRICT_FILE_UPLOADS = '#assignment_restrict_file_extensions'
RESTRICT_FILE_UPLOADS_OPTIONS = '#restrict_file_extensions_container'
ALLOWED_EXTENSIONS = '#allowed_extensions_container'
ADVANCED_ASSIGNMENT_OPTIONS = '#advanced_assignment_options'
TURNITIN_ENABLED = '[name="turnitin_enabled"]'
TURNITIN_ENABLED = '#assignment_turnitin_enabled'
ADVANCED_TURNITIN_SETTINGS = '#advanced_turnitin_settings_link'
ASSIGNMENT_TOGGLE_ADVANCED_OPTIONS = '#assignment_toggle_advanced_options'
GRADING_TYPE_SELECTOR = '#grading_type_selector'
@ -61,6 +62,7 @@ AssignmentGroupSelector, GroupCategorySelector, toggleAccessibly) ->
els["#{NAME}"] = '$name'
els["#{ALLOW_FILE_UPLOADS}"] = '$allowFileUploads'
els["#{RESTRICT_FILE_UPLOADS}"] = '$restrictFileUploads'
els["#{RESTRICT_FILE_UPLOADS_OPTIONS}"] = '$restrictFileUploadsOptions'
els["#{ALLOWED_EXTENSIONS}"] = '$allowedExtensions'
els["#{ADVANCED_ASSIGNMENT_OPTIONS}"] = '$advancedAssignmentOptions'
els["#{TURNITIN_ENABLED}"] = '$turnitinEnabled'
@ -142,14 +144,14 @@ AssignmentGroupSelector, GroupCategorySelector, toggleAccessibly) ->
@$externalToolsNewTab.prop('checked', data['item[new_tab]'] == '1')
toggleRestrictFileUploads: =>
@$restrictFileUploads.toggleAccessibly @$allowFileUploads.prop('checked')
@$restrictFileUploadsOptions.toggleAccessibly @$allowFileUploads.prop('checked')
toggleAdvancedTurnitinSettings: (ev) =>
ev.preventDefault()
@$advancedTurnitinSettings.toggleAccessibly @$turnitinEnabled.prop('checked')
handleRestrictFileUploadsChange: =>
@$allowedExtensions.toggleAccessibly @$restrictFileUploads.find('input').prop('checked')
@$allowedExtensions.toggleAccessibly @$restrictFileUploads.prop('checked')
handleGradingTypeChange: (gradingType) =>
@$gradedAssignmentFields.toggleAccessibly gradingType != 'not_graded'
@ -169,9 +171,11 @@ AssignmentGroupSelector, GroupCategorySelector, toggleAccessibly) ->
this
toJSON: =>
_.extend @assignment.toView(),
data = @assignment.toView()
_.extend data,
kalturaEnabled: ENV?.KALTURA_ENABLED || false
isLargeRoster: ENV?.IS_LARGE_ROSTER || false
submissionTypesFrozen: _.include(data.frozenAttributes, 'submission_types')
_attachEditorToDescription: =>
@$description.editorBox()
@ -229,7 +233,7 @@ AssignmentGroupSelector, GroupCategorySelector, toggleAccessibly) ->
assignmentData.submission_types = ['not_graded']
else if assignmentData.submission_type == 'online'
types = _.select _.keys(assignmentData.online_submission_types), (k) ->
assignmentData.online_submission_types[k]
assignmentData.online_submission_types[k] is '1'
assignmentData.submission_types = types
else
assignmentData.submission_types = [assignmentData.submission_type]
@ -240,7 +244,7 @@ AssignmentGroupSelector, GroupCategorySelector, toggleAccessibly) ->
_filterAllowedExtensions: (data) =>
restrictFileExtensions = data.restrict_file_extensions
delete data.restrict_file_extensions
if restrictFileExtensions
if restrictFileExtensions is '1'
data.allowed_extensions = _.select data.allowed_extensions.split(","), (ext) ->
$.trim(ext.toString()).length > 0
else

View File

@ -51,16 +51,20 @@ define [
@showGroupCategoryCreateDialog()
toJSON: =>
frozenAttributes = @parentModel.frozenAttributes()
groupCategoryId: @parentModel.groupCategoryId()
groupCategories: @groupCategories
gradeGroupStudentsIndividually: @parentModel.gradeGroupStudentsIndividually()
frozenAttributes: @parentModel.frozenAttributes()
frozenAttributes: frozenAttributes
groupCategoryIdFrozen: _.include(frozenAttributes, 'group_category_id')
nested: @nested
prefix: 'assignment' if @nested
filterFormData: (data) =>
hasGroupCategory = data.has_group_category
delete data.has_group_category
unless hasGroupCategory
unless hasGroupCategory is '1'
data.group_category_id = null
data.grade_group_students_individually = false
data

View File

@ -46,9 +46,13 @@ define [
@$peerReviewsAssignAt.datetime_field()
toJSON: =>
frozenAttributes = @parentModel.frozenAttributes()
peerReviews: @parentModel.peerReviews()
automaticPeerReviews: @parentModel.automaticPeerReviews()
peerReviewCount: @parentModel.peerReviewCount()
peerReviewsAssignAt: @parentModel.peerReviewsAssignAt()
frozenAttributes: @parentModel.frozenAttributes()
frozenAttributes: frozenAttributes
peerReviewsFrozen: _.include(frozenAttributes, 'peer_reviews')
nested: @nested
prefix: 'assignment' if @nested

View File

@ -11,7 +11,7 @@ define [
tagName: 'div'
EXCLUDE_SMALL_MATCHES_OPTIONS = '.js-exclude-small-matches-options'
EXCLUDE_SMALL_MATCHES = '[name="exclude_small_matches"]'
EXCLUDE_SMALL_MATCHES = '#exclude_small_matches'
EXCLUDE_SMALL_MATCHES_TYPE = '[name="exclude_small_matches_type"]'
events: do ->

View File

@ -16,7 +16,7 @@ define [
events:
'change #courseSelect' : 'updateSearch'
'change [name=include_completed_courses]' : 'toggleConcludedCourses'
'change #include_completed_courses' : 'toggleConcludedCourses'
render: ->
super

View File

@ -366,5 +366,5 @@ define [
# for key, enabled of options
# $node = @$form.find(".#{key}_info")
# $node.showIf(enabled)
# $node.find("input[name=#{key}]").prop('checked', false) unless enabled
# $node.find("input[type=checkbox][name=#{key}]").prop('checked', false) unless enabled
#

View File

@ -801,7 +801,7 @@ class ConversationsController < ApplicationController
:forwarded_message_ids => params[:forwarded_message_ids],
:root_account_id => @domain_root_account.id,
:media_comment => infer_media_comment,
:generate_user_note => params[:user_note]
:generate_user_note => value_to_boolean(params[:user_note])
)
end

View File

@ -7,5 +7,5 @@
jammit_css :content_migrations
%>
<form id="migrationConverterContainer" class="form-horizontal" serialize-radio-value></form>
<form id="migrationConverterContainer" class="form-horizontal"></form>
<div id="progress" class="span10"></div>

View File

@ -1,6 +1,6 @@
<div title="{{#t "titles.assignment_group_weights"}}Manage assignment group weighting{{/t}}" style="display:none;" id="assignment_group_weights_dialog">
<p style="margin-top: 8px;">
<input type="checkbox" id="group_weighting_scheme" name="group_weighting_scheme"/>
{{checkbox "group_weighting_scheme"}}
<label for="group_weighting_scheme">{{#t "weight_final"}}Weight final grade based on groups{{/t}}</label>
</p>
<table>

View File

@ -22,7 +22,7 @@
{{/if}}
</p>
<p>
<input type="checkbox" name="assign_blanks" id="assign_blanks"/>
{{checkbox "assign_blanks"}}
<label for="assign_blanks">{{#t "labels.assign_blanks"}}Assign zeroes to unsubmitted students{{/t}}</label>
</p>
<div class="alert alert-danger">

View File

@ -49,22 +49,17 @@
<div class="controls">
{{#if isTopic}}
<label class="checkbox">
<input name="threaded" type="hidden" value="0" />
<input type="checkbox"
{{#ifEqual discussion_type "threaded"}}checked{{/ifEqual}}
name="threaded">
{{checkbox "threaded"}}
{{#t "allow_threaded_replies"}}Allow threaded replies{{/t}}
</label>
{{/if}}
{{#if contextIsCourse}}
{{#if isAnnouncement}}
<label class="checkbox">
<input name="delay_posting" type="hidden" value="0" />
<input type="checkbox"
{{#if delayed_post_at}}checked{{/if}}
name="delay_posting"
{{checkbox "delay_posting"
checked=delayed_post_at
class="element_toggler"
aria-controls="discussion_topic_delay_post_container">
aria-controls="discussion_topic_delay_post_container"}}
{{#t "delay_posting"}}Delay posting{{/t}}
</label>
<label id="discussion_topic_delay_post_container"
@ -85,12 +80,10 @@
{{/if}}
{{#if canModerate}}
<label class="checkbox">
<input name="podcast_enabled" type="hidden" value="0" />
<input type="checkbox"
{{#if podcast_url}}checked{{/if}}
name="podcast_enabled"
{{checkbox "podcast_enabled"
checked=podcast_url
class="element_toggler"
aria-controls="podcast_has_student_posts_container">
aria-controls="podcast_has_student_posts_container"}}
{{#t "enable_podcast_feed"}}Enable podcast feed{{/t}}
</label>
@ -103,14 +96,11 @@
{{/if}}
{{#if showAssignment}}
<label class="checkbox">
<input name="assignment[set_assignment]" type="hidden" value="0" />
<input type="checkbox"
{{checkbox "assignment.set_assignment"
id="use_for_grading"
class="element_toggler"
aria-controls="assignment_options"
name="assignment[set_assignment]"
{{checkedIf set_assignment}}
value="1">
checked=set_assignment}}
{{#t "use_for_grading"}}Graded{{/t}}
</label>
{{/if}}

View File

@ -65,7 +65,7 @@
<textarea id="add_a_comment" name="comment[text_comment]"></textarea>
{{#if assignment.group_category_id}}
<span id="group-comment-container">
<input type="checkbox" id="group_comment" name="comment[group_comment]" />
{{checkbox "comment.group_comment" id="group_comment"}}
<label for="group_comment">{{#t "labels.group_comment"}}Send Comment to the Whole Group{{/t}}</label>
</span>
{{/if}}

View File

@ -80,6 +80,6 @@
{{#if ENV.PERMISSIONS.manage}}
<form data-view="createAssignment" class="form-dialog"></form>
<form data-view="editAssignmentGroup" class="form-dialog"></form>
<form data-view="deleteAssignmentGroup" class="form-dialog" serialize-radio-value></form>
<form data-view="deleteAssignmentGroup" class="form-dialog"></form>
{{/if}}
</div>

View File

@ -1,7 +1,7 @@
<div class="form-dialog-content">
<div class="form-horizontal">
<label class="checkbox" id="weight-groups" >
<input type="checkbox" id="apply_assignment_group_weights" name="apply_assignment_group_weights" {{#if apply_assignment_group_weights}}checked="checked"{{/if}} />
{{checkbox "apply_assignment_group_weights"}}
{{#t "weight_groups"}}Weight final grade based on assignment groups{{/t}}
</label>

View File

@ -120,10 +120,9 @@
<fieldset>
<div class="control-group controls">
<label class="checkbox">
<input id="assignment_freeze_on_copy"
name="freeze_on_copy"
{{checkedIf freezeOnCopy}}
type="checkbox"/>
{{checkbox "freezeOnCopy"
id="assignment_freeze_on_copy"
name="freeze_on_copy"}}
{{#t "freeze_properties"}}Lock assignment properties when copied{{/t}}
</label>
</div>
@ -138,10 +137,9 @@
<div style="float: left; min-width: 300px;">
<label class="checkbox" style="text-align:left;">
<input id="assignment_notify_of_update"
name="notify_of_update"
{{checkedIf notifyOfUpdate}}
type="checkbox"/>
{{checkbox "notifyOfUpdate"
id="assignment_notify_of_update"
name="notify_of_update"}}
{{#t "notify_content_change"}}Notify users that this content has changed{{/t}}
</label>
</div>

View File

@ -1,10 +1,10 @@
<div class="controls">
<label class="checkbox">
<input id="assignment_has_group_category"
name="{{#if nested}}assignment[has_group_category]{{else}}has_group_category{{/if}}"
type="checkbox"
{{checkedIf groupCategoryId}}
{{disabledIfIncludes frozenAttributes "group_category_id"}}/>
{{checkbox "has_group_category"
id="assignment_has_group_category"
prefix=prefix
checked=groupCategoryId
disabled=groupCategoryIdFrozen}}
{{#t "is_group_assignment"}}This is a Group Assignment{{/t}}
</label>
</div>
@ -13,10 +13,11 @@
<div class="controls">
<label class="checkbox" >
<input name="{{#if nested}}assignment[grade_group_students_individually]{{else}}grade_group_students_individually{{/if}}"
type="checkbox"
{{checkedIf gradeGroupStudentsIndividually}}
{{disabledIfIncludes frozenAttributes "group_category_id"}}/>
{{checkbox "grade_group_students_individually"
id=null
prefix=prefix
checked=gradeGroupStudentsIndividually
disabled=groupCategoryIdFrozen}}
{{#t "grade_group_students_individually"}}
Assign Grades to Each Student Individually
{{/t}}

View File

@ -1,11 +1,11 @@
<div class="controls">
<label class="checkbox" for="assignment_peer_reviews">
<input id="assignment_peer_reviews"
name="{{#if nested}}assignment[peer_reviews]{{else}}peer_reviews{{/if}}"
type="checkbox"
{{checkbox "peer_reviews"
id="assignment_peer_reviews"
prefix=prefix
checked=peerReviews
aria-controls="peer_reviews_details"
{{checkedIf peerReviews}}
{{disabledIfIncludes frozenAttributes "peer_reviews"}}/>
disabled=peerReviewsFrozen}}
{{#t "labels.require_peer_reviews"}}Require Peer Reviews{{/t}}
</label>
</div>

View File

@ -46,10 +46,7 @@
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input id="s_paper_check"
name="s_paper_check"
{{checkedIf sPaperCheck}}
type="checkbox"/>
{{checkbox "s_paper_check" checked=sPaperCheck}}
{{#t "turnitin_settings.labels.student_paper_check"}}
Other Student Papers
{{/t}}
@ -58,10 +55,7 @@
<div class="controls">
<label class="checkbox">
<input id="internet_check"
name="internet_check"
{{checkedIf internetCheck}}
type="checkbox"/>
{{checkbox "internet_check" checked=internetCheck}}
{{#t "turnitin_settings.labels.internet_check"}}
Internet Database
{{/t}}
@ -70,10 +64,7 @@
<div class="controls">
<label class="checkbox">
<input id="journal_check"
name="journal_check"
type="checkbox"
{{checkedIf journalCheck}}/>
{{checkbox "journal_check" checked=journalCheck}}
{{#t "turnitin_settings.labels.journal_check"}}
Journals, Periodicals, and Publications
{{/t}}
@ -91,10 +82,7 @@
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input id="exclude_biblio"
name="exclude_biblio"
type="checkbox"
{{checkedIf excludeBiblio}}/>
{{checkbox "exclude_biblio" checked=excludeBiblio}}
{{#t "turnitin_settings.labels.exclude_biblio"}}
Bibliographic Material
{{/t}}
@ -102,10 +90,7 @@
</div>
<div class="controls">
<label class="checkbox">
<input id="exclude_quoted"
name="exclude_quoted"
type="checkbox"
{{checkedIf excludeQuoted}}/>
{{checkbox "exclude_quoted" checked=excludeQuoted}}
{{#t "turnitin_settings.labels.exclude_quoted"}}
Quoted Material
{{/t}}
@ -114,10 +99,7 @@
<div class="controls">
<label class="checkbox">
<input id="exclude_small_matches"
name="exclude_small_matches"
type="checkbox"
{{checkedIf excludesSmallMatches}}/>
{{checkbox "exclude_small_matches" checked=excludesSmallMatches}}
{{#t "assignments.turnitin_settings.labels.exclude_small_matches"}}
Small Matches
{{/t}}

View File

@ -32,22 +32,20 @@
<div class="controls">
<label class="checkbox">
<input id="assignment_text_entry"
{{checkbox "acceptsOnlineTextEntries"
id="assignment_text_entry"
name="online_submission_types[online_text_entry]"
type="checkbox"
{{checkedIf acceptsOnlineTextEntries}}
{{disabledIfIncludes frozenAttributes "submission_types"}}/>
disabled=submissionTypesFrozen}}
{{#t "labels.allow_text_entry"}}Allow Text Entry{{/t}}
</label>
</div>
<div class="controls">
<label class="checkbox">
<input id="assignment_online_url"
{{checkbox "acceptsOnlineURL"
id="assignment_online_url"
name="online_submission_types[online_url]"
type="checkbox"
{{checkedIf acceptsOnlineURL}}
{{disabledIfIncludes frozenAttributes "submission_types"}}/>
disabled=submissionTypesFrozen}}
{{#t "labels.allow_url"}}Allow Website URL{{/t}}
</label>
</div>
@ -55,11 +53,10 @@
<div class="controls">
{{#if kalturaEnabled}}
<label class="checkbox">
<input id="assignment_media_recording"
{{checkbox "acceptsMediaRecording"
id="assignment_media_recording"
name="online_submission_types[media_recording]"
type="checkbox"
{{checkedIf acceptsMediaRecording}}
{{disabledIfIncludes frozenAttributes "submission_types"}}/>
disabled=submissionTypesFrozen}}
{{#t "labels.allow_media_recordings"}}Allow Media Recordings{{/t}}
</label>
{{/if}}
@ -67,12 +64,11 @@
<div class="controls">
<label class="checkbox" for="assignment_online_upload">
<input id="assignment_online_upload"
{{checkbox "acceptsOnlineUpload"
id="assignment_online_upload"
name="online_submission_types[online_upload]"
type="checkbox"
aria-controls="restrict_file_extensions_container"
{{checkedIf acceptsOnlineUpload}}
{{disabledIfIncludes frozenAttributes "submission_types"}}/>
disabled=submissionTypesFrozen}}
{{#t "labels.allow_file_uploads"}}Allow File Uploads{{/t}}
</label>
</div>
@ -82,12 +78,11 @@
style="{{hiddenUnless acceptsOnlineUpload}}">
<div class="controls">
<label class="checkbox" for="assignment_restrict_file_extensions">
<input id="assignment_restrict_file_extensions"
{{checkbox "restrictFileExtensions"
id="assignment_restrict_file_extensions"
name="restrict_file_extensions"
type="checkbox"
aria-controls="allowed_extensions_container"
{{checkedIf restrictFileExtensions}}
{{disabledIfIncludes frozenAttributes "submission_types"}}/>
disabled=submissionTypesFrozen}}
{{#t "labels.restrict_file_extensions"}}
Restrict Upload File Types
{{/t}}
@ -118,12 +113,11 @@
<div class="controls" style="{{hiddenUnless turnitinAvailable}}">
<label for="assignment_turnitin_enabled" class="checkbox">
<input id="assignment_turnitin_enabled"
{{checkbox "turnitinEnabled"
id="assignment_turnitin_enabled"
name="turnitin_enabled"
type="checkbox"
aria-controls="advanced_turnitin_settings_link"
{{checkedIf turnitinEnabled}}
{{disabledIfIncludes frozenAttributes "submission_types"}}/>
disabled=submissionTypesFrozen}}
{{#t "label.turnitin_enabled"}}Enable Turnitin Submissions{{/t}}
</label>
@ -155,11 +149,10 @@
<div class="control-group">
<div class="controls">
<label for="assignment_external_tool_tag_attributes_new_tab" class="checkbox">
<input id="assignment_external_tool_tag_attributes_new_tab"
{{checkbox "externalToolNewTab"
id="assignment_external_tool_tag_attributes_new_tab"
name="external_tool_tag_attributes[new_tab]"
type="checkbox"
{{checkedIf externalToolNewTab}}
{{disabledIfIncludes frozenAttributes "submission_types"}}/>
disabled=submissionTypesFrozen}}
{{#t "label.external_tool_new_tab"}}Load This Tool In A New Tab{{/t}}
</label>
</div>

View File

@ -14,7 +14,7 @@
<div class="ag-menu-container"></div>
</div>
<p>
<input type="checkbox" class="group-signup-checkbox" id="group-signup-checkbox" name="use_group_signup" value="1" {{#ifEqual participant_type "Group"}}checked{{/ifEqual}}>
{{checkbox "use_group_signup" id="group-signup-checkbox" class="group-signup-checkbox"}}
<label for="group-signup-checkbox">{{#t "group_signup"}}Have students sign up in groups.{{/t}}</label>
</p>
<div class="group-signup" style="display: none">
@ -51,7 +51,7 @@
<b>Options</b>
<ul class="ag-options">
<li>
<input type="checkbox" id="appointment-blocks-per-slot-option-button" name="per_slot_option" value="1" />
{{checkbox "per_slot_option" id="appointment-blocks-per-slot-option-button"}}
<label for="appointment-blocks-per-slot-option-button">Limit each time slot to</label>
<input type="number" name="participants_per_appointment" value="{{appointment_group.participants_per_appointment}}" min="1" style="width: 40px;" />
<label for="appointment-blocks-per-slot-option-button">
@ -63,10 +63,10 @@
</a>
</li>
<li>
<label for="appointment-blocks-participant-visibility-button"><input type="checkbox" id="appointment-blocks-participant-visibility-button" name="participant_visibility" value="1" /> Allow students to see who has signed up for time slots.</label>
<label for="appointment-blocks-participant-visibility-button">{{checkbox "participant_visibility" id="appointment-blocks-participant-visibility-button"}} Allow students to see who has signed up for time slots.</label>
</li>
<li>
<input type="checkbox" id="max-per-student-option" name="max_appointments_per_participant_option" value="1">
{{checkbox "max_appointments_per_participant_option" id="max-per-student-option"}}
<label for="max-per-student-option">Limit participants to attend</label>
<input type="number" name="max_appointments_per_participant" value="{{appointment_group.max_appointments_per_participant}}" min="1" style="width: 40px">
<label for="max-per-student-option">appointment(s).</label>

View File

@ -4,7 +4,7 @@
<textarea name="description">{{description}}</textarea>
</p>
<p>
<input type="checkbox" name="max_participants_option">
{{checkbox "max_participants_option" id=null}}
{{#t "slot_limit"}}Limit this slot to{{/t}}
<input type="number" name="max_participants" class="max-participants" min="1" disabled>
{{#t "users"}}users.{{/t}}

View File

@ -14,10 +14,7 @@
{{#if course_sections}}
<label class="label_with_checkbox">
<input type="checkbox"
name="use_section_dates"
value="1"
{{#if use_section_dates}}checked{{/if}} />
{{checkbox "use_section_dates"}}
{{#t "different_date_for_each_section"}}Use a different date for each section{{/t}}
</label>

View File

@ -42,10 +42,9 @@
<div class="controls">
<div class="web_conference_user_settings"></div>
<label class="checkbox" for="web_conference_long_running">
<input name="web_conference[long_running]" type="hidden" value="0">
<input id="web_conference_long_running" name="web_conference[long_running]" type="checkbox"
value="1" {{#if conferenceData.long_running}}checked{{/if}}
{{#if settings.disable_duration_changes}}disabled='disabled'{{/if}}>
{{checkbox "web_conference.long_running"
checked=conferenceData.long_running
disabled=settings.disable_duration_changes}}
{{#t "long_running"}}No time limit (for long-running conferences){{/t}}
</label>
</div>
@ -58,8 +57,7 @@
</div>
<legend>{{#t "members"}}Members{{/t}}</legend>
<label class="checkbox" for="user_all">
<input name="user[all]" type="hidden" value="0">
<input class="all_users_checkbox" id="user_all" name="user[all]" type="checkbox" value="1" {{#if inviteAll}}checked{{/if}}>
{{checkbox "user.all" class="all_users_checkbox" checked=inviteAll}}
{{#t "invite_all"}}Invite All Course Members{{/t}}
</label>
<div>

View File

@ -15,7 +15,7 @@
<input id="courseSearchField" type="text" placeholder="Course name" name="courseSearchField" class='span3'/>
<br>
<label class="checkbox">
<input type="checkbox" name="include_completed_courses" {{#if include_concluded}}checked{{/if}}>
{{checkbox "include_completed_courses" checked=include_concluded}}
{{#t 'include_completed_courses'}}Include completed courses{{/t}}
</label>
</div>

View File

@ -1,6 +1,6 @@
<div class="controls">
<label class="checkbox">
<input type="checkbox" id="dateShiftCheckbox" name="date_shift_options[shift_dates]">
{{checkbox "date_shift_options.shift_dates" id="dateShiftCheckbox"}}
{{#t "checkbox_label"}}Adjust events and due dates{{/t}}
</label>
</div>

View File

@ -39,7 +39,7 @@
<th></th>
<td>
<label>
<input type="checkbox" name="group_conversation" value="1" class="group_conversation">
{{checkbox "group_conversation" class="group_conversation"}}
{{#t "labels.group_conversation"}}This is a group conversation. Participants will see everyone's replies{{/t}}
</label>
</td>
@ -49,13 +49,11 @@
<th></th>
<td>
<label>
<input type="checkbox"
name="user_note"
value="1"
{{checkbox "user_note"
data-track-category="Compose Message"
data-track-action="Edit"
data-track-label="Faculty Journal"
class="user_note" />
class="user_note"}}
{{#t "labels.add_to_faculty_journal"}}Add as a Faculty Journal entry{{/t}}
</label>
</td>

View File

@ -121,10 +121,9 @@
<div class='message-header-label'></div>
<div class='message-header-input'>
<label class="checkbox" for="compose-message-group_conversation">
<input type="checkbox" id="compose-message-group_conversation"
name="group_conversation"
value="1"
class="group_conversation">
{{checkbox "group_conversation"
id="compose-message-group_conversation"
class="group_conversation"}}
{{#t "labels.group_conversation"}}This is a group conversation. Participants will see everyone's replies{{/t}}
</label>
</div>
@ -135,14 +134,12 @@
<div class='message-header-label'></div>
<div class='message-header-input'>
<label class="checkbox" for="compose-message-user_note">
<input type="checkbox"
name="user_note"
{{checkbox "user_note"
id="compose-message-user_note"
value="1"
data-track-category="Compose Message"
data-track-action="Edit"
data-track-label="Faculty Journal"
class="user_note" />
class="user_note"}}
{{#t "labels.add_to_faculty_journal"}}Add as a Faculty Journal entry{{/t}}
</label>
</div>

View File

@ -34,7 +34,7 @@
<div class="control-group" id="privileges">
<div class="controls">
<label class="checkbox">
<input type="checkbox" id="limit_privileges_to_course_section" name="limit_privileges_to_course_section" value="1" {{#if limit_privileges_to_course_section}}checked{{/if}}>
{{checkbox "limit_privileges_to_course_section"}}
{{#t "can_only_grade_students_in_section"}}Can grade students in their section only{{/t}}
</label>
</div>

View File

@ -36,11 +36,10 @@
{{#if checkName}}
<div class="user-preference">
<label class="checkbox" for="{{checkID}}">
<input type="checkbox"
id="{{checkID}}"
{{checkbox checkName
id=checkID
class="user-pref-check"
name="{{checkName}}"
{{#if checkedState}}checked="checked"{{/if}} />
checked=checkedState}}
{{checkLabel}}
</label>
</div>

View File

@ -11,7 +11,7 @@
<div class="row-fluid expandee">
<div class="span2">
<label class="checkbox checkbox-inline"><input type="checkbox" name="graded"> Graded?</label>
<label class="checkbox checkbox-inline">{{checkbox "graded"}} Graded?</label>
</div>
<div class="span2">

View File

@ -40,7 +40,7 @@
{{#if CAN.EDIT_HIDE}}
<div class="controls">
<label for="hide_from_students" class="checkbox">
<input id="hide_from_students" type="checkbox" name="wiki_page[hide_from_students]" {{#if hide_from_students}}checked{{/if}}>
{{checkbox "hide_from_students" prefix="wiki_page"}}
{{#t "hide_from_students"}}Hide this page from students{{/t}}
</label>
</div>
@ -67,7 +67,7 @@
<div class="form-actions clearfix">
<div style="height:30px">
<label for="notify_of_update" class="checkbox clearfix pull-left" style="margin-top:5px">
<input id="notify_of_update" type="checkbox" name="wiki_page[notify_of_update]">
{{checkbox "notify_of_update" prefix="wiki_page"}}
{{#t "notify_users_text"}}Notify users that this content has changed{{/t}}
</label>
<a class="btn cancel" tabindex="0" role="button">{{#t "buttons.cancel"}}Cancel{{/t}}</a>

View File

@ -1,63 +1,56 @@
define [
'jquery'
'underscore'
'compiled/jquery/serializeForm'
], ($, _) ->
$sampleForm = $('<form></form>')
.append('<input type="radio" value="val1" name=radio[1] />')
.append('<input type="radio" value="val2" name=radio[2] />')
.append('<input type="radio" value="val3" name=radio[3] />')
.append('<input type="radio" value="group_val_1" name="radio_group" id="rg1" />')
.append('<input type="radio" value="group_val_2" name="radio_group" id="rg2" />')
.append('<input type="radio" value="group_val_3" name="radio_group" id="rg3" />')
.append('<input type="checkbox" value="checkbox1" name="checkbox[1]" />')
.append('<input type="checkbox" value="checkbox2" name="checkbox[2]" />')
.append('<input type="button" value="button" />')
], ($) ->
$sampleForm = $('''
<form>
Radio
<input type="radio" value="group_val_1" name="radio_group" checked />
<input type="radio" value="group_val_2" name="radio_group" />
module "SerializeForm: without serialize-radio-value",
setup: ->
$sampleForm.find('[name="radio[1]"]').prop('checked', true)
teardown: ->
$sampleForm.find('[name="radio[1]"]').prop('checked', false)
Checked checkbox
<input type="checkbox" value="checkbox1" name="checkbox[1]" checked />
test "Radio button values should be booleans", ->
Unchecked checkbox
<input type="checkbox" value="checkbox2" name="checkbox[2]" />
Unchecked checkbox with hidden field (a la rails and handlebars helper)
<input type="hidden" value="0" name="checkbox[3]" />
<input type="checkbox" value="1" name="checkbox[3]" />
Text field
<input type="text" value="asdf" name="text" />
Disabled field
<input type="text" value="qwerty" name="text2" disabled />
Textarea
<textarea name="textarea">hello\nworld</textarea>
Select
<select name="select"><option>1</option><option selected>2</option></select>
Multi-select
<select name="multiselect" multiple>
<option>1</option>
<option selected>2</option>
<option selected>3</option>
</select>
</form>
''')
module "SerializeForm"
test "Serializes valid input items correctly", ->
serialized = $sampleForm.serializeForm()
radio1 = _.find serialized, (input) -> input.name == "radio[1]"
ok radio1.value, "Selected radio value should be true"
test "Serializes all input items", ->
serialized = $sampleForm.serializeForm()
ok serialized.length == 8, "There are 8 input elements serialized"
module "SerializeForm: with serialize-radio-value",
setup: ->
$sampleForm.attr('serialize-radio-value', '')
teardown: ->
$sampleForm.removeAttr('serialize-radio-value')
test "Doesnt serialize radio buttons that arent selected", ->
serialized = $sampleForm.serializeForm()
radios = _.filter serialized, (input) ->
input.name == "radio[1]" ||
input.name == "radio[2]" ||
input.name == "radio[3]"
ok radios.length == 0, "No radio selected"
test "Sends the true value of radio buttons that are selected", ->
$sampleForm.find('[name="radio[1]"]').prop('checked', true)
serialized = $sampleForm.serializeForm()
radio1 = _.find serialized, (input) -> input.name == "radio[1]"
equal radio1.value, "val1", "Serializes the true value of radio buttons"
$sampleForm.find('[name="radio[1]"]').prop('checked', false)
test "Serializes true radio button values of a selected group", ->
$sampleForm.find('#rg2').prop('checked', true)
serialized = $sampleForm.serializeForm()
radio2 = _.find serialized, (input) -> input.name == "radio_group"
equal radio2.value, "group_val_2", "Serializes the true value of radio buttons"
$sampleForm.find('#rg2').prop('checked', false)
deepEqual serialized, [
{name: "radio_group", value: "group_val_1"}
{name: "checkbox[1]", value: "checkbox1"}
{name: "checkbox[3]", value: "0"}
{name: "text", value: "asdf"}
{name: "textarea", value: "hello\r\nworld"}
{name: "select", value: "2"}
{name: "multiselect", value: "2"}
{name: "multiselect", value: "3"}
]

View File

@ -15,13 +15,13 @@ require File.expand_path(File.dirname(__FILE__) + '/../common')
f('.ag_contexts_done').click
if opts[:checkable_options]
if opts[:checkable_options].has_key?(:per_slot_option)
set_value f('[name="per_slot_option"]'), true
set_value f('[type=checkbox][name="per_slot_option"]'), true
end
if opts[:checkable_options].has_key?(:participant_visibility)
set_value f('[name="participant_visibility"]'), true
set_value f('[type=checkbox][name="participant_visibility"]'), true
end
if opts[:checkable_options].has_key?(:max_appointments_per_participant_option)
set_value f('[name="max_appointments_per_participant_option"]'), true
set_value f('[type=checkbox][name="max_appointments_per_participant_option"]'), true
end
end
date_field = edit_form.find_element(:css, '.date_field')

View File

@ -133,21 +133,21 @@ describe "scheduler" do
}
# assert options are checked
open_edit_dialog
f('[name="per_slot_option"]').selected?.should be_true
f('[name="participant_visibility"]').selected?.should be_true
f('[name="max_appointments_per_participant_option"]').selected?.should be_true
f('[type=checkbox][name="per_slot_option"]').selected?.should be_true
f('[type=checkbox][name="participant_visibility"]').selected?.should be_true
f('[type=checkbox][name="max_appointments_per_participant_option"]').selected?.should be_true
# uncheck the options
f('[name="per_slot_option"]').click
f('[name="participant_visibility"]').click
f('[name="max_appointments_per_participant_option"]').click
f('[type=checkbox][name="per_slot_option"]').click
f('[type=checkbox][name="participant_visibility"]').click
f('[type=checkbox][name="max_appointments_per_participant_option"]').click
submit_dialog('.ui-dialog-buttonset', '.ui-button')
wait_for_ajaximations
# assert options are not checked
open_edit_dialog
f('[name="per_slot_option"]').selected?.should be_false
f('[name="participant_visibility"]').selected?.should be_false
f('[name="max_appointments_per_participant_option"]').selected?.should be_false
f('[type=checkbox][name="per_slot_option"]').selected?.should be_false
f('[type=checkbox][name="participant_visibility"]').selected?.should be_false
f('[type=checkbox][name="max_appointments_per_participant_option"]').selected?.should be_false
end
it "should delete an appointment group after clicking appointment group link" do
@ -375,7 +375,7 @@ describe "scheduler" do
ag.participants_per_appointment.should == 2
open_edit_event_dialog
f('[name=max_participants_option]').click
f('[type=checkbox][name=max_participants_option]').click
fj('.ui-button:contains(Update)').click
wait_for_ajaximations