Commit 17334d9b by Matjaz Gregoric Committed by Tim Krones

Avoid using ID attributes in preference to classes.

ID attributes have to be unique on the document level. To avoid
accidentaly having multiple elements with the same ID, we prefer to use
class attributes.

There are two situations where we still need an ID attribute:

- Connecting <label> elements to the associated input element using the
  'for' attribute
- Connecting description of an element with the 'aria-describedby'
  attribute.

In the first case, we can often wrap the associated input inside the
<label> tag, so that we don't need IDs, although in some cases that is
not possible because it breaks the layout.

In cases when we still need to use ID attributes, we append a random
string to ensure uniqueness.
parent 142b49ce
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
""" Drag and Drop v2 XBlock """ """ Drag and Drop v2 XBlock """
# Imports ########################################################### # Imports ###########################################################
import copy import copy
import json import json
import urllib import urllib
import uuid
import webob import webob
from xblock.core import XBlock from xblock.core import XBlock
...@@ -247,6 +249,12 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin): ...@@ -247,6 +249,12 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
""" """
js_templates = loader.load_unicode('/templates/html/js_templates.html') js_templates = loader.load_unicode('/templates/html/js_templates.html')
# A short random string to append to HTML element ID attributes to avoid multiple instances
# of the DnDv2 block on the same page sharing the same ID values.
# We avoid using ID attributes in preference to classes, but sometimes we still need IDs to
# connect 'for' and 'aria-describedby' attributes to the associated elements.
id_suffix = uuid.uuid4().hex[:16]
js_templates = js_templates.replace('{{id_suffix}}', id_suffix)
help_texts = { help_texts = {
field_name: self.ugettext(field.help) field_name: self.ugettext(field.help)
for field_name, field in self.fields.viewitems() if hasattr(field, "help") for field_name, field in self.fields.viewitems() if hasattr(field, "help")
...@@ -259,6 +267,7 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin): ...@@ -259,6 +267,7 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
'js_templates': js_templates, 'js_templates': js_templates,
'help_texts': help_texts, 'help_texts': help_texts,
'field_values': field_values, 'field_values': field_values,
'id_suffix': id_suffix,
'self': self, 'self': self,
'data': urllib.quote(json.dumps(self.data)), 'data': urllib.quote(json.dumps(self.data)),
} }
......
...@@ -79,11 +79,6 @@ ...@@ -79,11 +79,6 @@
font-size: 100%; font-size: 100%;
} }
.xblock--drag-and-drop--editor .tab .nested-input {
display: block;
margin-top: 8px;
}
.xblock--drag-and-drop--editor .tab-header, .xblock--drag-and-drop--editor .tab-header,
.xblock--drag-and-drop--editor .tab-content, .xblock--drag-and-drop--editor .tab-content,
.xblock--drag-and-drop--editor .tab-footer { .xblock--drag-and-drop--editor .tab-footer {
...@@ -108,6 +103,25 @@ ...@@ -108,6 +103,25 @@
width: 50%; width: 50%;
} }
.xblock--drag-and-drop--editor .target-image-form textarea {
display: block;
}
.xblock--drag-and-drop--editor label > span {
display: inline-block;
margin-bottom: 0.25em;
}
/* Main Tab */
.xblock--drag-and-drop--editor .feedback-tab input,
.xblock--drag-and-drop--editor .feedback-tab select {
display: block;
}
.xblock--drag-and-drop--editor .feedback-tab input[type=checkbox] {
display: inline-block;
}
/* Zones Tab */ /* Zones Tab */
.xblock--drag-and-drop--editor .zones-tab .zone-editor { .xblock--drag-and-drop--editor .zones-tab .zone-editor {
position: relative; position: relative;
...@@ -138,11 +152,15 @@ ...@@ -138,11 +152,15 @@
} }
.xblock--drag-and-drop--editor .zones-form .zone-row label { .xblock--drag-and-drop--editor .zones-form .zone-row label {
display: block;
}
.xblock--drag-and-drop--editor .zones-form .zone-row label > span {
display: inline-block; display: inline-block;
width: 18%; width: 6em;
} }
.xblock--drag-and-drop--editor .zones-form .zone-row > input { .xblock--drag-and-drop--editor .zones-form .zone-row label > input {
width: 60%; width: 60%;
margin: 0 0 5px; margin: 0 0 5px;
line-height: 2.664rem; /* .title gets line-height from a Studio rule that does not apply to .description; line-height: 2.664rem; /* .title gets line-height from a Studio rule that does not apply to .description;
...@@ -153,11 +171,16 @@ ...@@ -153,11 +171,16 @@
margin-bottom: 15px; margin-bottom: 15px;
} }
.xblock--drag-and-drop--editor .zones-form .zone-row .layout label {
display: inline-block;
width: 40%;
}
.xblock--drag-and-drop--editor .zones-form .zone-row .layout .size, .xblock--drag-and-drop--editor .zones-form .zone-row .layout .size,
.xblock--drag-and-drop--editor .zones-form .zone-row .layout .coord { .xblock--drag-and-drop--editor .zones-form .zone-row .layout .coord {
width: 15%; width: 35%;
margin: 0 19px 5px 0; margin: 0 19px 5px 0;
line-height: inherit;
} }
.xblock--drag-and-drop--editor .feedback-form textarea { .xblock--drag-and-drop--editor .feedback-form textarea {
...@@ -206,7 +229,6 @@ ...@@ -206,7 +229,6 @@
.xblock--drag-and-drop--editor .items-form textarea { .xblock--drag-and-drop--editor .items-form textarea {
width: 97%; width: 97%;
margin: 0 1%;
} }
.xblock--drag-and-drop--editor .items-form .zone-checkbox-label { .xblock--drag-and-drop--editor .items-form .zone-checkbox-label {
......
...@@ -29,10 +29,10 @@ function DragAndDropEditBlock(runtime, element, params) { ...@@ -29,10 +29,10 @@ function DragAndDropEditBlock(runtime, element, params) {
tpl: { tpl: {
init: function() { init: function() {
_fn.tpl = { _fn.tpl = {
zoneInput: Handlebars.compile($("#zone-input-tpl", element).html()), zoneInput: Handlebars.compile($(".zone-input-tpl", element).html()),
zoneElement: Handlebars.compile($("#zone-element-tpl", element).html()), zoneElement: Handlebars.compile($(".zone-element-tpl", element).html()),
zoneCheckbox: Handlebars.compile($("#zone-checkbox-tpl", element).html()), zoneCheckbox: Handlebars.compile($(".zone-checkbox-tpl", element).html()),
itemInput: Handlebars.compile($("#item-input-tpl", element).html()), itemInput: Handlebars.compile($(".item-input-tpl", element).html()),
}; };
} }
}, },
...@@ -66,7 +66,7 @@ function DragAndDropEditBlock(runtime, element, params) { ...@@ -66,7 +66,7 @@ function DragAndDropEditBlock(runtime, element, params) {
_fn.build.clickHandlers(); _fn.build.clickHandlers();
// Hide settings that are specific to assessment mode // Hide settings that are specific to assessment mode
_fn.build.$el.feedback.form.find('#problem-mode').trigger('change'); _fn.build.$el.feedback.form.find('.problem-mode').trigger('change');
}, },
validate: function() { validate: function() {
...@@ -120,8 +120,8 @@ function DragAndDropEditBlock(runtime, element, params) { ...@@ -120,8 +120,8 @@ function DragAndDropEditBlock(runtime, element, params) {
} }
// Set the target image and bind its event handler: // Set the target image and bind its event handler:
$('.target-image-form #background-url', element).val(_fn.data.targetImg); $('.target-image-form .background-url', element).val(_fn.data.targetImg);
$('.target-image-form #background-description', element).val(_fn.data.targetImgDescription); $('.target-image-form .background-description', element).val(_fn.data.targetImgDescription);
_fn.build.$el.targetImage.load(_fn.build.form.zone.imageLoaded); _fn.build.$el.targetImage.load(_fn.build.form.zone.imageLoaded);
_fn.build.$el.targetImage.attr('src', params.target_img_expanded_url); _fn.build.$el.targetImage.attr('src', params.target_img_expanded_url);
_fn.build.$el.targetImage.attr('alt', _fn.data.targetImgDescription); _fn.build.$el.targetImage.attr('alt', _fn.data.targetImgDescription);
...@@ -175,7 +175,7 @@ function DragAndDropEditBlock(runtime, element, params) { ...@@ -175,7 +175,7 @@ function DragAndDropEditBlock(runtime, element, params) {
}); });
$fbkTab $fbkTab
.on('change', '#problem-mode', _fn.build.form.problem.toggleAssessmentSettings); .on('change', '.problem-mode', _fn.build.form.problem.toggleAssessmentSettings);
$zoneTab $zoneTab
.on('click', '.add-zone', function(e) { .on('click', '.add-zone', function(e) {
...@@ -188,7 +188,7 @@ function DragAndDropEditBlock(runtime, element, params) { ...@@ -188,7 +188,7 @@ function DragAndDropEditBlock(runtime, element, params) {
.on('click', '.target-image-form button', function(e) { .on('click', '.target-image-form button', function(e) {
e.preventDefault(); e.preventDefault();
var new_img_url = $.trim($('.target-image-form #background-url', element).val()); var new_img_url = $.trim($('.target-image-form .background-url', element).val());
if (new_img_url) { if (new_img_url) {
// We may need to 'expand' the URL before it will be valid. // We may need to 'expand' the URL before it will be valid.
// e.g. '/static/blah.png' becomes '/asset-v1:course+id/blah.png' // e.g. '/static/blah.png' becomes '/asset-v1:course+id/blah.png'
...@@ -202,9 +202,9 @@ function DragAndDropEditBlock(runtime, element, params) { ...@@ -202,9 +202,9 @@ function DragAndDropEditBlock(runtime, element, params) {
} }
_fn.data.targetImg = new_img_url; _fn.data.targetImg = new_img_url;
}) })
.on('input', '.target-image-form #background-description', function(e) { .on('input', '.target-image-form .background-description', function(e) {
var new_description = $.trim( var new_description = $.trim(
$('.target-image-form #background-description', element).val() $('.target-image-form .background-description', element).val()
); );
_fn.build.$el.targetImage.attr('alt', new_description); _fn.build.$el.targetImage.attr('alt', new_description);
_fn.data.targetImgDescription = new_description; _fn.data.targetImgDescription = new_description;
...@@ -230,8 +230,8 @@ function DragAndDropEditBlock(runtime, element, params) { ...@@ -230,8 +230,8 @@ function DragAndDropEditBlock(runtime, element, params) {
toggleAssessmentSettings: function(e) { toggleAssessmentSettings: function(e) {
e.preventDefault(); e.preventDefault();
var $modeSetting = $(e.currentTarget), var $modeSetting = $(e.currentTarget),
$problemForm = $modeSetting.parent('form'), $problemForm = $modeSetting.closest('form'),
$assessmentSettings = $problemForm.find('.setting.assessment'); $assessmentSettings = $problemForm.find('.assessment-setting');
if ($modeSetting.val() === 'assessment') { if ($modeSetting.val() === 'assessment') {
$assessmentSettings.show(); $assessmentSettings.show();
} else { } else {
...@@ -394,8 +394,8 @@ function DragAndDropEditBlock(runtime, element, params) { ...@@ -394,8 +394,8 @@ function DragAndDropEditBlock(runtime, element, params) {
}, },
feedback: function($form) { feedback: function($form) {
_fn.data.feedback = { _fn.data.feedback = {
start: $form.find('#intro-feedback').val(), start: $form.find('.intro-feedback').val(),
finish: $form.find('#final-feedback').val() finish: $form.find('.final-feedback').val()
}; };
}, },
item: { item: {
...@@ -505,15 +505,15 @@ function DragAndDropEditBlock(runtime, element, params) { ...@@ -505,15 +505,15 @@ function DragAndDropEditBlock(runtime, element, params) {
_fn.data.zones = _fn.build.form.zone.zoneObjects; _fn.data.zones = _fn.build.form.zone.zoneObjects;
var data = { var data = {
'display_name': $element.find('#display-name').val(), 'display_name': $element.find('.display-name').val(),
'mode': $element.find("#problem-mode").val(), 'mode': $element.find(".problem-mode").val(),
'max_attempts': $element.find(".max-attempts").val(), 'max_attempts': $element.find(".max-attempts").val(),
'show_title': $element.find('.show-title').is(':checked'), 'show_title': $element.find('.show-title').is(':checked'),
'weight': $element.find('#weight').val(), 'weight': $element.find('.weight').val(),
'problem_text': $element.find('#problem-text').val(), 'problem_text': $element.find('.problem-text').val(),
'show_problem_header': $element.find('.show-problem-header').is(':checked'), 'show_problem_header': $element.find('.show-problem-header').is(':checked'),
'item_background_color': $element.find('#item-background-color').val(), 'item_background_color': $element.find('.item-background-color').val(),
'item_text_color': $element.find('#item-text-color').val(), 'item_text_color': $element.find('.item-text-color').val(),
'data': _fn.data, 'data': _fn.data,
}; };
......
...@@ -12,8 +12,10 @@ ...@@ -12,8 +12,10 @@
<section class="tab-content"> <section class="tab-content">
<form class="feedback-form"> <form class="feedback-form">
<label class="h3" for="display-name">{% trans self.fields.display_name.display_name %}</label> <label class="h3">
<input id="display-name" value="{{ self.display_name }}" /> <span>{% trans self.fields.display_name.display_name %}</span>
<input class="display-name" value="{{ self.display_name }}" />
</label>
<label title="{{ help_texts.show_title }}"> <label title="{{ help_texts.show_title }}">
<input class="show-title" type="checkbox" <input class="show-title" type="checkbox"
{% if self.show_title %}checked="checked"{% endif %}> {% if self.show_title %}checked="checked"{% endif %}>
...@@ -21,50 +23,62 @@ ...@@ -21,50 +23,62 @@
<span class="sr">{{ help_texts.show_title }}</span> <span class="sr">{{ help_texts.show_title }}</span>
</label> </label>
<label class="h3" for="problem-mode">{% trans self.fields.mode.display_name %}</label> <label class="h3">
<select id="problem-mode" aria-describedby="problem-mode-description"> <span>{% trans self.fields.mode.display_name %}</span>
{% for field_value in field_values.mode %} <select class="problem-mode" aria-describedby="problem-mode-description-{{id_suffix}}">
<option value="{{ field_value.value }}" {% if self.mode == field_value.value %}selected{% endif %}> {% for field_value in field_values.mode %}
{{ field_value.display_name }} <option value="{{ field_value.value }}" {% if self.mode == field_value.value %}selected{% endif %}>
</option> {{ field_value.display_name }}
{% endfor %} </option>
</select> {% endfor %}
<div id="problem-mode-description" class="form-help"> </select>
</label>
<div id="problem-mode-description-{{id_suffix}}" class="form-help">
{{ help_texts.mode }} {{ help_texts.mode }}
</div> </div>
<label class="h3 setting assessment"> <label class="h3 assessment-setting">
{% trans self.fields.max_attempts.display_name %} {% trans self.fields.max_attempts.display_name %}
<input class="nested-input max-attempts" <input class="max-attempts"
type="number" type="number"
min="1" min="1"
step="1" step="1"
aria-describedby="max-attempts-description" aria-describedby="max-attempts-description-{{id_suffix}}"
{% if self.max_attempts %}value="{{ self.max_attempts }}"{% endif %} /> {% if self.max_attempts %}value="{{ self.max_attempts }}"{% endif %} />
</label> </label>
<div id="max-attempts-description" class="form-help"> <div id="max-attempts-description-{{id_suffix}}" class="assessment-setting form-help">
{{ help_texts.max_attempts }} {{ help_texts.max_attempts }}
</div> </div>
<label class="h3" for="weight">{% trans self.fields.weight.display_name %}</label> <label class="h3">
<input id="weight" type="number" step="0.1" value="{{ self.weight|unlocalize }}" /> <span>{% trans self.fields.weight.display_name %}</span>
<input class="weight" type="number" step="0.1" value="{{ self.weight|unlocalize }}" />
</label>
<label class="h3" for="problem-text">{% trans self.fields.question_text.display_name %}</label> <label class="h3">
<textarea id="problem-text">{{ self.question_text }}</textarea> <span>{% trans self.fields.question_text.display_name %}</span>
<textarea class="problem-text">{{ self.question_text }}</textarea>
</label>
<label> <label>
<input class="show-problem-header" type="checkbox" aria-describedby="show-problem-header-description" <input class="show-problem-header"
type="checkbox"
aria-describedby="show-problem-header-description-{{id_suffix}}"
{% if self.show_question_header %}checked="checked"{% endif %}> {% if self.show_question_header %}checked="checked"{% endif %}>
{% trans self.fields.show_question_header.display_name %} {% trans self.fields.show_question_header.display_name %}
</label> </label>
<div id="show-problem-header-description" class="form-help"> <div id="show-problem-header-description-{{id_suffix}}" class="form-help">
{{ help_texts.show_question_header }} {{ help_texts.show_question_header }}
</div> </div>
<label class="h3" for="intro-feedback">{% trans "Introductory Feedback" %}</label> <label class="h3">
<textarea id="intro-feedback">{{ self.data.feedback.start }}</textarea> <span>{% trans "Introductory Feedback" %}</span>
<textarea class="intro-feedback">{{ self.data.feedback.start }}</textarea>
</label>
<label class="h3" for="final-feedback">{% trans "Final Feedback" %}</label> <label class="h3">
<textarea id="final-feedback">{{ self.data.feedback.finish }}</textarea> <span>{% trans "Final Feedback" %}</span>
<textarea class="final-feedback">{{ self.data.feedback.finish }}</textarea>
</label>
</form> </form>
</section> </section>
</div> </div>
...@@ -75,15 +89,21 @@ ...@@ -75,15 +89,21 @@
</header> </header>
<section class="tab-content"> <section class="tab-content">
<form class="target-image-form"> <form class="target-image-form">
<label class="h3" for="background-url">{% trans "Background URL" %}</label> <label class="h3" for="background-url-{{id_suffix}}">
<input id="background-url" <span>{% trans "Background URL" %}</span>
</label>
<input class="background-url"
id="background-url-{{id_suffix}}"
type="text" type="text"
placeholder="{% trans 'For example, http://example.com/background.png or /static/background.png' %}"> placeholder="{% trans 'For example, http://example.com/background.png or /static/background.png' %}">
</label>
<button class="btn">{% trans "Change background" %}</button> <button class="btn">{% trans "Change background" %}</button>
<label class="h3" for="background-description">{% trans "Background description" %}</label> <label class="h3">
<textarea required id="background-description" <span>{% trans "Background description" %}</span>
aria-describedby="background-description-description"></textarea> <textarea required class="background-description"
<div id="background-description-description" class="form-help"> aria-describedby="background-description-description-{{id_suffix}}"></textarea>
</label>
<div id="background-description-description-{{id_suffix}}" class="form-help">
{% blocktrans %} {% blocktrans %}
Please provide a description of the image for non-visual users. Please provide a description of the image for non-visual users.
The description should provide sufficient information to allow anyone The description should provide sufficient information to allow anyone
...@@ -95,13 +115,17 @@ ...@@ -95,13 +115,17 @@
<section class="tab-content"> <section class="tab-content">
<form class="display-labels-form"> <form class="display-labels-form">
<h3>{% trans "Zone labels" %}</h3> <h3>{% trans "Zone labels" %}</h3>
<label for="display-labels">{% trans "Display label names on the image" %}:</label> <label>
<input name="display-labels" id="display-labels" type="checkbox" /> <span>{% trans "Display label names on the image" %}:</span>
<input name="display-labels" class="display-labels" type="checkbox" />
</label>
</form> </form>
<form class="display-borders-form"> <form class="display-borders-form">
<h3>{% trans "Zone borders" %}</h3> <h3>{% trans "Zone borders" %}</h3>
<label for="display-borders">{% trans "Display zone borders on the image" %}:</label> <label>
<input name="display-borders" id="display-borders" type="checkbox" /> <span>{% trans "Display zone borders on the image" %}:</span>
<input name="display-borders" class="display-borders" type="checkbox" />
</label>
</form> </form>
</section> </section>
<section class="tab-content"> <section class="tab-content">
...@@ -125,20 +149,24 @@ ...@@ -125,20 +149,24 @@
</header> </header>
<section class="tab-content"> <section class="tab-content">
<form class="item-styles-form"> <form class="item-styles-form">
<label class="h3" for="item-background-color">{% trans self.fields.item_background_color.display_name %}</label> <label class="h3">
<input id="item-background-color" <span>{% trans self.fields.item_background_color.display_name %}</span>
placeholder="e.g. blue or #0000ff" <input class="item-background-color"
value="{{ self.item_background_color }}" placeholder="e.g. blue or #0000ff"
aria-describedby="item-background-color-description"> value="{{ self.item_background_color }}"
<div id="item-background-color-description" class="form-help"> aria-describedby="item-background-color-description-{{id_suffix}}">
</label>
<div id="item-background-color-description-{{id_suffix}}" class="form-help">
{{ help_texts.item_background_color }} {{ help_texts.item_background_color }}
</div> </div>
<label class="h3" for="item-text-color">{% trans self.fields.item_text_color.display_name %}</label> <label class="h3">
<input id="item-text-color" <span>{% trans self.fields.item_text_color.display_name %}</span>
placeholder="e.g. white or #ffffff" <input class="item-text-color"
value="{{ self.item_text_color}}" placeholder="e.g. white or #ffffff"
aria-describedby="item-text-color-description"> value="{{ self.item_text_color}}"
<div id="item-text-color-description" class="form-help"> aria-describedby="item-text-color-description-{{id_suffix}}">
</label>
<div id="item-text-color-description-{{id_suffix}}" class="form-help">
{{ help_texts.item_text_color }} {{ help_texts.item_text_color }}
</div> </div>
</form> </form>
......
<script id="zone-element-tpl" type="text/html"> <script class="zone-element-tpl" type="text/html">
<div class="zone" data-zone="{{ uid }}" style=" <div class="zone" data-zone="{{ uid }}" style="
top:{{ y_percent }}%; top:{{ y_percent }}%;
left:{{ x_percent }}%; left:{{ x_percent }}%;
...@@ -9,80 +9,84 @@ ...@@ -9,80 +9,84 @@
</div> </div>
</script> </script>
<script id="zone-input-tpl" type="text/html"> <script class="zone-input-tpl" type="text/html">
<div class="zone-row" data-uid="{{zone.uid}}"> <div class="zone-row" data-uid="{{zone.uid}}">
<!-- uid values from old versions of the block may contain spaces and other characters, so we use 'index' as an alternate unique ID here. --> <a class="remove-zone hidden" title="{{i18n 'Remove zone'}}">
<label for="zone-{{index}}-title">{{i18n "Text"}}</label>
<input type="text"
id="zone-{{index}}-title"
class="title"
value="{{ zone.title }}"
required />
<a class="remove-zone hidden">
<div class="icon remove"></div> <div class="icon remove"></div>
</a> </a>
<label for="zone-{{index}}-description">{{i18n "Description"}}</label> <label>
<input type="text" <span>{{i18n "Text"}}</span>
id="zone-{{index}}-description"
class="description"
value="{{ zone.description }}"
placeholder="{{i18n 'Describe this zone to non-visual users'}}"
required />
<div class="layout">
<label for="zone-{{index}}-width">{{i18n "width"}}</label>
<input type="text" <input type="text"
id="zone-{{index}}-width" class="title"
class="size width" value="{{ zone.title }}"
value="{{ zone.width }}" /> required />
<label for="zone-{{index}}-height">{{i18n "height"}}</label> </label>
<label>
<span>{{i18n "Description"}}</span>
<input type="text" <input type="text"
id="zone-{{index}}-height" class="description"
class="size height" value="{{ zone.description }}"
value="{{ zone.height }}" /> placeholder="{{i18n 'Describe this zone to non-visual users'}}"
required />
</label>
<div class="layout">
<label>
<span>{{i18n "width"}}</span>
<input type="text"
class="size width"
value="{{ zone.width }}" />
</label>
<label>
<span>{{i18n "height"}}</span>
<input type="text"
class="size height"
value="{{ zone.height }}" />
</label>
<br /> <br />
<label for="zone-{{index}}-x">x</label> <label>
<input type="text" <span>x</span>
id="zone-{{index}}-x" <input type="text"
class="coord x" class="coord x"
value="{{ zone.x }}" /> value="{{ zone.x }}" />
<label for="zone-{{index}}-y">y</label> </label>
<input type="text" <label>
id="zone-{{index}}-y" <span>y</span>
class="coord y" <input type="text"
value="{{ zone.y }}" /> class="coord y"
value="{{ zone.y }}" />
</label>
</div> </div>
<div class="alignment"> <div class="alignment">
<label for="zone-{{index}}-align"> <label>
{{i18n "Alignment"}} <span>{{i18n "Alignment"}}</span>
<select class="align-select"
aria-describedby="zone-align-description-{{zone.uid}}-{{id_suffix}}">
<option value=""
{{#ifeq zone.align ""}}selected{{/ifeq}}>
{{i18n "none"}}
</option>
<option value="left"
{{#ifeq zone.align "left"}}selected{{/ifeq}}>
{{i18n "left"}}
</option>
<option value="center"
{{#ifeq zone.align "center"}}selected{{/ifeq}}>
{{i18n "center"}}
</option>
<option value="right"
{{#ifeq zone.align "right"}}selected{{/ifeq}}>
{{i18n "right"}}
</option>
</select>
</label> </label>
<select id="zone-{{index}}-align" <div id="zone-align-description-{{zone.uid}}-{{id_suffix}}" class="form-help">
class="align-select"
aria-describedby="zone-align-description">
<option value=""
{{#ifeq zone.align ""}}selected{{/ifeq}}>
{{i18n "none"}}
</option>
<option value="left"
{{#ifeq zone.align "left"}}selected{{/ifeq}}>
{{i18n "left"}}
</option>
<option value="center"
{{#ifeq zone.align "center"}}selected{{/ifeq}}>
{{i18n "center"}}
</option>
<option value="right"
{{#ifeq zone.align "right"}}selected{{/ifeq}}>
{{i18n "right"}}
</option>
</select>
<div id="zone-align-description" class="zones-form-help">
{{i18n "Align dropped items to the left, center, or right. Default is no alignment (items stay exactly where the user drops them)."}} {{i18n "Align dropped items to the left, center, or right. Default is no alignment (items stay exactly where the user drops them)."}}
</div> </div>
</div> </div>
</div> </div>
</script> </script>
<script id="zone-checkbox-tpl" type="text/html"> <script class="zone-checkbox-tpl" type="text/html">
<div class="zone-checkbox-row"> <div class="zone-checkbox-row">
<label> <label>
<input type="checkbox" <input type="checkbox"
...@@ -94,7 +98,7 @@ ...@@ -94,7 +98,7 @@
</div> </div>
</script> </script>
<script id="item-input-tpl" type="text/html"> <script class="item-input-tpl" type="text/html">
<div class="item"> <div class="item">
<div class="row"> <div class="row">
<label class="h3"> <label class="h3">
...@@ -126,32 +130,38 @@ ...@@ -126,32 +130,38 @@
</label> </label>
</div> </div>
<div class="row"> <div class="row">
<label class="h3" for="item-{{id}}-image-description">{{i18n "Image description (should provide sufficient information to place the item even if the image did not load)"}}</label> <label class="h3">
<textarea id="item-{{id}}-image-description" {{#if imageURL}}required{{/if}} <span>{{i18n "Image description (should provide sufficient information to place the item even if the image did not load)"}}</span>
class="item-image-description">{{ imageDescription }}</textarea> <textarea {{#if imageURL}}required{{/if}} class="item-image-description">{{ imageDescription }}</textarea>
</label>
</div> </div>
<div class="row"> <div class="row">
<label class="h3" for="item-{{id}}-success-feedback">{{i18n "Success Feedback"}}</label> <label class="h3">
<textarea id="item-{{id}}-success-feedback" <span>{{i18n "Success Feedback"}}</span>
class="success-feedback">{{ feedback.correct }}</textarea> <textarea class="success-feedback">{{ feedback.correct }}</textarea>
</label>
</div> </div>
<div class="row"> <div class="row">
<label class="h3" for="item-{{id}}-error-feedback">{{i18n "Error Feedback"}}</label> <label class="h3">
<textarea id="item-{{id}}-error-feedback" <span>{{i18n "Error Feedback"}}</span>
class="error-feedback">{{ feedback.incorrect }}</textarea> <textarea class="error-feedback">{{ feedback.incorrect }}</textarea>
</label>
</div> </div>
<div class="row advanced-link"> <div class="row advanced-link">
<a>{{i18n "Show advanced settings" }}</a> <a>{{i18n "Show advanced settings" }}</a>
</div> </div>
<div class="row advanced"> <div class="row advanced">
<label> <label>
{{i18n "Preferred width as a percentage of the background image width (or blank for automatic width):"}} <span>
{{i18n "Preferred width as a percentage of the background image width (or blank for automatic width):"}}
</span>
<input type="number" <input type="number"
class="item-width" class="item-width"
value="{{ singleDecimalFloat widthPercent }}" value="{{ singleDecimalFloat widthPercent }}"
step="0.1" step="0.1"
min="1" min="1"
max="99" />% max="99" />%
</label>
</div> </div>
</div> </div>
</script> </script>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment