mirror of
https://github.com/moodle/moodle.git
synced 2025-03-19 23:20:09 +01:00
Merge branch 'MDL-71674-master' of https://github.com/dcai/moodle
This commit is contained in:
commit
ff6e6ca8d2
@ -21,8 +21,11 @@ Feature: Atto accessibility checker
|
||||
And I click on ".moodle-dialogue-focused .closebutton" "css_element"
|
||||
And I select the text in the "Description" Atto editor
|
||||
And I click on "Insert or edit image" "button"
|
||||
And I set the field "Enter URL" to "/decorative-image.png"
|
||||
And I set the field "Describe this image for someone who cannot see it" to ""
|
||||
And I set the field "This image is decorative only" to "1"
|
||||
And I set the field "Width" to "1"
|
||||
And I set the field "Height" to "1"
|
||||
And I click on "This image is decorative only" "checkbox"
|
||||
And I press "Save image"
|
||||
And I press "Accessibility checker"
|
||||
And I should see "Congratulations, no accessibility problems found!"
|
||||
|
@ -39,6 +39,7 @@ $string['imageproperties'] = 'Image properties';
|
||||
$string['presentation'] = 'This image is decorative only';
|
||||
$string['pluginname'] = 'Insert or edit image';
|
||||
$string['presentationoraltrequired'] = 'An image must have a description, unless it is marked as decorative only.';
|
||||
$string['imageurlrequired'] = 'An image must have a URL.';
|
||||
$string['preview'] = 'Preview';
|
||||
$string['saveimage'] = 'Save image';
|
||||
$string['size'] = 'Size';
|
||||
|
@ -47,6 +47,7 @@ function atto_image_strings_for_js() {
|
||||
'height',
|
||||
'presentation',
|
||||
'presentationoraltrequired',
|
||||
'imageurlrequired',
|
||||
'size',
|
||||
'width',
|
||||
'uploading',
|
||||
|
@ -58,7 +58,8 @@ Feature: Add images to Atto
|
||||
When I click on "Insert or edit image" "button"
|
||||
Then the field "Enter URL" matches value "/nothing/here"
|
||||
And I set the field "Describe this image for someone who cannot see it" to "Something"
|
||||
And I set the field "Enter URL" to ""
|
||||
And I set the field "Width" to "1"
|
||||
And I set the field "Height" to "1"
|
||||
And I press "Save image"
|
||||
And I set the field "Description" to "<p>Image: <img src='/nothing/again' width='123' height='456' alt='Awesome!'>.</p>"
|
||||
And I press "Update profile"
|
||||
@ -69,3 +70,24 @@ Feature: Add images to Atto
|
||||
And the field "Width" matches value "123"
|
||||
And the field "Height" matches value "456"
|
||||
And the field "Describe this image" matches value "Awesome!"
|
||||
|
||||
@javascript
|
||||
Scenario: Error handling when inserting an image manually
|
||||
Given I log in as "admin"
|
||||
And I open my profile in edit mode
|
||||
And I set the field "Description" to "<p>Image: <img src='/nothing/here'>.</p>"
|
||||
And I select the text in the "Description" Atto editor
|
||||
When I click on "Insert or edit image" "button"
|
||||
Then the field "Enter URL" matches value "/nothing/here"
|
||||
And I set the field "Describe this image for someone who cannot see it" to ""
|
||||
And I take focus off "Describe this image for someone who cannot see it" "field"
|
||||
And I should see "An image must have a description, unless it is marked as decorative only."
|
||||
And I set the field "Describe this image for someone who cannot see it" to "Something"
|
||||
And I set the field "Enter URL" to ""
|
||||
And I press "Save image"
|
||||
And I should see "An image must have a URL."
|
||||
And I set the field "Enter URL" to "/nothing/here"
|
||||
And I set the field "Width" to "1"
|
||||
And I set the field "Height" to "1"
|
||||
And I press "Save image"
|
||||
And I press "Update profile"
|
||||
|
@ -43,6 +43,7 @@ var CSS = {
|
||||
INPUTSIZE: 'atto_image_size',
|
||||
INPUTWIDTH: 'atto_image_widthentry',
|
||||
IMAGEALTWARNING: 'atto_image_altwarning',
|
||||
IMAGEURLWARNING: 'atto_image_urlwarning',
|
||||
IMAGEBROWSER: 'openimagebrowser',
|
||||
IMAGEPRESENTATION: 'atto_image_presentation',
|
||||
INPUTCONSTRAIN: 'atto_image_constrain',
|
||||
@ -51,6 +52,10 @@ var CSS = {
|
||||
IMAGEPREVIEWBOX: 'atto_image_preview_box',
|
||||
ALIGNSETTINGS: 'atto_image_button'
|
||||
},
|
||||
FORMNAMES = {
|
||||
URL: 'urlentry',
|
||||
ALT: 'altentry'
|
||||
},
|
||||
SELECTORS = {
|
||||
INPUTURL: '.' + CSS.INPUTURL
|
||||
},
|
||||
@ -96,13 +101,17 @@ var CSS = {
|
||||
|
||||
TEMPLATE = '' +
|
||||
'<form class="atto_form">' +
|
||||
|
||||
// Add the repository browser button.
|
||||
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEURLWARNING}}">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">' +
|
||||
'{{get_string "imageurlrequired" component}}' +
|
||||
'</label>' +
|
||||
'</div>' +
|
||||
'{{#if showFilepicker}}' +
|
||||
'<div class="mb-1">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
||||
'<div class="input-group input-append w-100">' +
|
||||
'<input class="form-control {{CSS.INPUTURL}}" type="url" ' +
|
||||
'<input name="{{FORMNAMES.URL}}" class="form-control {{CSS.INPUTURL}}" type="url" ' +
|
||||
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
|
||||
'<span class="input-group-append">' +
|
||||
'<button class="btn btn-secondary {{CSS.IMAGEBROWSER}}" type="button">' +
|
||||
@ -113,19 +122,21 @@ var CSS = {
|
||||
'{{else}}' +
|
||||
'<div class="mb-1">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
||||
'<input class="form-control fullwidth {{CSS.INPUTURL}}" type="url" ' +
|
||||
'<input name="{{FORMNAMES.URL}}" class="form-control fullwidth {{CSS.INPUTURL}}" type="url" ' +
|
||||
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
|
||||
'</div>' +
|
||||
'{{/if}}' +
|
||||
|
||||
// Add the Alt box.
|
||||
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEALTWARNING}}">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTALT}}">' +
|
||||
'{{get_string "presentationoraltrequired" component}}' +
|
||||
'</label>' +
|
||||
'</div>' +
|
||||
// Add the Alt box.
|
||||
'<div class="mb-1">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
|
||||
'<textarea class="form-control fullwidth {{CSS.INPUTALT}}" ' +
|
||||
'id="{{elementid}}_{{CSS.INPUTALT}}" maxlength="125"></textarea>' +
|
||||
'id="{{elementid}}_{{CSS.INPUTALT}}" name="{{FORMNAMES.ALT}}" maxlength="125"></textarea>' +
|
||||
|
||||
// Add the character count.
|
||||
'<div id="the-count" class="d-flex justify-content-end small">' +
|
||||
@ -252,7 +263,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
this.editor.on('paste', this._handlePaste, this);
|
||||
this.editor.on('drop', this._handleDragDrop, this);
|
||||
|
||||
// e.preventDefault needed to stop the default event from clobbering the desired behaviour in some browsers.
|
||||
// ...e.preventDefault needed to stop the default event from clobbering the desired behaviour in some browsers.
|
||||
this.editor.on('dragover', function(e) {
|
||||
e.preventDefault();
|
||||
}, this);
|
||||
@ -602,6 +613,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
content = Y.Node.create(template({
|
||||
elementid: this.get('host').get('elementid'),
|
||||
CSS: CSS,
|
||||
FORMNAMES: FORMNAMES,
|
||||
component: COMPONENTNAME,
|
||||
showFilepicker: canShowFilepicker,
|
||||
alignments: ALIGNMENTS
|
||||
@ -613,8 +625,9 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
this._applyImageProperties(this._form);
|
||||
|
||||
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
||||
this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._updateWarning, this);
|
||||
this._form.one('.' + CSS.INPUTALT).on('change', this._updateWarning, this);
|
||||
this._form.one('.' + CSS.INPUTURL).on('change', this._hasErrorUrlField, this);
|
||||
this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._hasErrorAltField, this);
|
||||
this._form.one('.' + CSS.INPUTALT).on('blur', this._hasErrorAltField, this);
|
||||
this._form.one('.' + CSS.INPUTWIDTH).on('blur', this._autoAdjustSize, this);
|
||||
this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
|
||||
this._form.one('.' + CSS.INPUTCONSTRAIN).on('change', function(event) {
|
||||
@ -622,7 +635,6 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
this._autoAdjustSize(event);
|
||||
}
|
||||
}, this);
|
||||
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
||||
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
|
||||
|
||||
if (canShowFilepicker) {
|
||||
@ -1049,6 +1061,38 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
return CSS.ALIGNSETTINGS + '_' + alignment;
|
||||
},
|
||||
|
||||
_toggleVisibility: function(selector, predicate) {
|
||||
var form = this._form;
|
||||
var element = form.all(selector);
|
||||
element.setStyle('display', predicate ? 'block' : 'none');
|
||||
},
|
||||
|
||||
_toggleAriaInvalid: function(selectors, predicate) {
|
||||
var form = this._form;
|
||||
selectors.forEach(function(selector) {
|
||||
var element = form.all(selector);
|
||||
element.setAttribute('aria-invalid', predicate);
|
||||
});
|
||||
},
|
||||
|
||||
_hasErrorUrlField: function() {
|
||||
var form = this._form;
|
||||
var url = form.one('.' + CSS.INPUTURL).get('value');
|
||||
var urlerror = url === '';
|
||||
this._toggleVisibility('.' + CSS.IMAGEURLWARNING, urlerror);
|
||||
this._toggleAriaInvalid(['.' + CSS.INPUTURL], urlerror);
|
||||
return urlerror;
|
||||
},
|
||||
|
||||
_hasErrorAltField: function() {
|
||||
var form = this._form;
|
||||
var alt = form.one('.' + CSS.INPUTALT).get('value');
|
||||
var presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked');
|
||||
var imagealterror = alt === '' && !presentation;
|
||||
this._toggleVisibility('.' + CSS.IMAGEALTWARNING, imagealterror);
|
||||
this._toggleAriaInvalid(['.' + CSS.INPUTALT, '.' + CSS.IMAGEPRESENTATION], imagealterror);
|
||||
return imagealterror;
|
||||
},
|
||||
/**
|
||||
* Update the alt text warning live.
|
||||
*
|
||||
@ -1057,23 +1101,11 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
* @private
|
||||
*/
|
||||
_updateWarning: function() {
|
||||
var form = this._form,
|
||||
state = true,
|
||||
alt = form.one('.' + CSS.INPUTALT).get('value'),
|
||||
presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked');
|
||||
if (alt === '' && !presentation) {
|
||||
form.one('.' + CSS.IMAGEALTWARNING).setStyle('display', 'block');
|
||||
form.one('.' + CSS.INPUTALT).setAttribute('aria-invalid', true);
|
||||
form.one('.' + CSS.IMAGEPRESENTATION).setAttribute('aria-invalid', true);
|
||||
state = true;
|
||||
} else {
|
||||
form.one('.' + CSS.IMAGEALTWARNING).setStyle('display', 'none');
|
||||
form.one('.' + CSS.INPUTALT).setAttribute('aria-invalid', false);
|
||||
form.one('.' + CSS.IMAGEPRESENTATION).setAttribute('aria-invalid', false);
|
||||
state = false;
|
||||
}
|
||||
var urlerror = this._hasErrorUrlField();
|
||||
var imagealterror = this._hasErrorAltField();
|
||||
var haserrors = urlerror || imagealterror;
|
||||
this.getDialogue().centerDialogue();
|
||||
return state;
|
||||
return haserrors;
|
||||
},
|
||||
|
||||
/**
|
||||
|
File diff suppressed because one or more lines are too long
@ -43,6 +43,7 @@ var CSS = {
|
||||
INPUTSIZE: 'atto_image_size',
|
||||
INPUTWIDTH: 'atto_image_widthentry',
|
||||
IMAGEALTWARNING: 'atto_image_altwarning',
|
||||
IMAGEURLWARNING: 'atto_image_urlwarning',
|
||||
IMAGEBROWSER: 'openimagebrowser',
|
||||
IMAGEPRESENTATION: 'atto_image_presentation',
|
||||
INPUTCONSTRAIN: 'atto_image_constrain',
|
||||
@ -51,6 +52,10 @@ var CSS = {
|
||||
IMAGEPREVIEWBOX: 'atto_image_preview_box',
|
||||
ALIGNSETTINGS: 'atto_image_button'
|
||||
},
|
||||
FORMNAMES = {
|
||||
URL: 'urlentry',
|
||||
ALT: 'altentry'
|
||||
},
|
||||
SELECTORS = {
|
||||
INPUTURL: '.' + CSS.INPUTURL
|
||||
},
|
||||
@ -96,13 +101,17 @@ var CSS = {
|
||||
|
||||
TEMPLATE = '' +
|
||||
'<form class="atto_form">' +
|
||||
|
||||
// Add the repository browser button.
|
||||
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEURLWARNING}}">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">' +
|
||||
'{{get_string "imageurlrequired" component}}' +
|
||||
'</label>' +
|
||||
'</div>' +
|
||||
'{{#if showFilepicker}}' +
|
||||
'<div class="mb-1">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
||||
'<div class="input-group input-append w-100">' +
|
||||
'<input class="form-control {{CSS.INPUTURL}}" type="url" ' +
|
||||
'<input name="{{FORMNAMES.URL}}" class="form-control {{CSS.INPUTURL}}" type="url" ' +
|
||||
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
|
||||
'<span class="input-group-append">' +
|
||||
'<button class="btn btn-secondary {{CSS.IMAGEBROWSER}}" type="button">' +
|
||||
@ -113,19 +122,21 @@ var CSS = {
|
||||
'{{else}}' +
|
||||
'<div class="mb-1">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
||||
'<input class="form-control fullwidth {{CSS.INPUTURL}}" type="url" ' +
|
||||
'<input name="{{FORMNAMES.URL}}" class="form-control fullwidth {{CSS.INPUTURL}}" type="url" ' +
|
||||
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
|
||||
'</div>' +
|
||||
'{{/if}}' +
|
||||
|
||||
// Add the Alt box.
|
||||
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEALTWARNING}}">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTALT}}">' +
|
||||
'{{get_string "presentationoraltrequired" component}}' +
|
||||
'</label>' +
|
||||
'</div>' +
|
||||
// Add the Alt box.
|
||||
'<div class="mb-1">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
|
||||
'<textarea class="form-control fullwidth {{CSS.INPUTALT}}" ' +
|
||||
'id="{{elementid}}_{{CSS.INPUTALT}}" maxlength="125"></textarea>' +
|
||||
'id="{{elementid}}_{{CSS.INPUTALT}}" name="{{FORMNAMES.ALT}}" maxlength="125"></textarea>' +
|
||||
|
||||
// Add the character count.
|
||||
'<div id="the-count" class="d-flex justify-content-end small">' +
|
||||
@ -252,7 +263,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
this.editor.on('paste', this._handlePaste, this);
|
||||
this.editor.on('drop', this._handleDragDrop, this);
|
||||
|
||||
// e.preventDefault needed to stop the default event from clobbering the desired behaviour in some browsers.
|
||||
// ...e.preventDefault needed to stop the default event from clobbering the desired behaviour in some browsers.
|
||||
this.editor.on('dragover', function(e) {
|
||||
e.preventDefault();
|
||||
}, this);
|
||||
@ -602,6 +613,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
content = Y.Node.create(template({
|
||||
elementid: this.get('host').get('elementid'),
|
||||
CSS: CSS,
|
||||
FORMNAMES: FORMNAMES,
|
||||
component: COMPONENTNAME,
|
||||
showFilepicker: canShowFilepicker,
|
||||
alignments: ALIGNMENTS
|
||||
@ -613,8 +625,9 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
this._applyImageProperties(this._form);
|
||||
|
||||
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
||||
this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._updateWarning, this);
|
||||
this._form.one('.' + CSS.INPUTALT).on('change', this._updateWarning, this);
|
||||
this._form.one('.' + CSS.INPUTURL).on('change', this._hasErrorUrlField, this);
|
||||
this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._hasErrorAltField, this);
|
||||
this._form.one('.' + CSS.INPUTALT).on('blur', this._hasErrorAltField, this);
|
||||
this._form.one('.' + CSS.INPUTWIDTH).on('blur', this._autoAdjustSize, this);
|
||||
this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
|
||||
this._form.one('.' + CSS.INPUTCONSTRAIN).on('change', function(event) {
|
||||
@ -622,7 +635,6 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
this._autoAdjustSize(event);
|
||||
}
|
||||
}, this);
|
||||
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
||||
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
|
||||
|
||||
if (canShowFilepicker) {
|
||||
@ -1047,6 +1059,38 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
return CSS.ALIGNSETTINGS + '_' + alignment;
|
||||
},
|
||||
|
||||
_toggleVisibility: function(selector, predicate) {
|
||||
var form = this._form;
|
||||
var element = form.all(selector);
|
||||
element.setStyle('display', predicate ? 'block' : 'none');
|
||||
},
|
||||
|
||||
_toggleAriaInvalid: function(selectors, predicate) {
|
||||
var form = this._form;
|
||||
selectors.forEach(function(selector) {
|
||||
var element = form.all(selector);
|
||||
element.setAttribute('aria-invalid', predicate);
|
||||
});
|
||||
},
|
||||
|
||||
_hasErrorUrlField: function() {
|
||||
var form = this._form;
|
||||
var url = form.one('.' + CSS.INPUTURL).get('value');
|
||||
var urlerror = url === '';
|
||||
this._toggleVisibility('.' + CSS.IMAGEURLWARNING, urlerror);
|
||||
this._toggleAriaInvalid(['.' + CSS.INPUTURL], urlerror);
|
||||
return urlerror;
|
||||
},
|
||||
|
||||
_hasErrorAltField: function() {
|
||||
var form = this._form;
|
||||
var alt = form.one('.' + CSS.INPUTALT).get('value');
|
||||
var presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked');
|
||||
var imagealterror = alt === '' && !presentation;
|
||||
this._toggleVisibility('.' + CSS.IMAGEALTWARNING, imagealterror);
|
||||
this._toggleAriaInvalid(['.' + CSS.INPUTALT, '.' + CSS.IMAGEPRESENTATION], imagealterror);
|
||||
return imagealterror;
|
||||
},
|
||||
/**
|
||||
* Update the alt text warning live.
|
||||
*
|
||||
@ -1055,23 +1099,11 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
* @private
|
||||
*/
|
||||
_updateWarning: function() {
|
||||
var form = this._form,
|
||||
state = true,
|
||||
alt = form.one('.' + CSS.INPUTALT).get('value'),
|
||||
presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked');
|
||||
if (alt === '' && !presentation) {
|
||||
form.one('.' + CSS.IMAGEALTWARNING).setStyle('display', 'block');
|
||||
form.one('.' + CSS.INPUTALT).setAttribute('aria-invalid', true);
|
||||
form.one('.' + CSS.IMAGEPRESENTATION).setAttribute('aria-invalid', true);
|
||||
state = true;
|
||||
} else {
|
||||
form.one('.' + CSS.IMAGEALTWARNING).setStyle('display', 'none');
|
||||
form.one('.' + CSS.INPUTALT).setAttribute('aria-invalid', false);
|
||||
form.one('.' + CSS.IMAGEPRESENTATION).setAttribute('aria-invalid', false);
|
||||
state = false;
|
||||
}
|
||||
var urlerror = this._hasErrorUrlField();
|
||||
var imagealterror = this._hasErrorAltField();
|
||||
var haserrors = urlerror || imagealterror;
|
||||
this.getDialogue().centerDialogue();
|
||||
return state;
|
||||
return haserrors;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -41,6 +41,7 @@ var CSS = {
|
||||
INPUTSIZE: 'atto_image_size',
|
||||
INPUTWIDTH: 'atto_image_widthentry',
|
||||
IMAGEALTWARNING: 'atto_image_altwarning',
|
||||
IMAGEURLWARNING: 'atto_image_urlwarning',
|
||||
IMAGEBROWSER: 'openimagebrowser',
|
||||
IMAGEPRESENTATION: 'atto_image_presentation',
|
||||
INPUTCONSTRAIN: 'atto_image_constrain',
|
||||
@ -49,6 +50,10 @@ var CSS = {
|
||||
IMAGEPREVIEWBOX: 'atto_image_preview_box',
|
||||
ALIGNSETTINGS: 'atto_image_button'
|
||||
},
|
||||
FORMNAMES = {
|
||||
URL: 'urlentry',
|
||||
ALT: 'altentry'
|
||||
},
|
||||
SELECTORS = {
|
||||
INPUTURL: '.' + CSS.INPUTURL
|
||||
},
|
||||
@ -94,13 +99,17 @@ var CSS = {
|
||||
|
||||
TEMPLATE = '' +
|
||||
'<form class="atto_form">' +
|
||||
|
||||
// Add the repository browser button.
|
||||
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEURLWARNING}}">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">' +
|
||||
'{{get_string "imageurlrequired" component}}' +
|
||||
'</label>' +
|
||||
'</div>' +
|
||||
'{{#if showFilepicker}}' +
|
||||
'<div class="mb-1">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
||||
'<div class="input-group input-append w-100">' +
|
||||
'<input class="form-control {{CSS.INPUTURL}}" type="url" ' +
|
||||
'<input name="{{FORMNAMES.URL}}" class="form-control {{CSS.INPUTURL}}" type="url" ' +
|
||||
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
|
||||
'<span class="input-group-append">' +
|
||||
'<button class="btn btn-secondary {{CSS.IMAGEBROWSER}}" type="button">' +
|
||||
@ -111,19 +120,21 @@ var CSS = {
|
||||
'{{else}}' +
|
||||
'<div class="mb-1">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
||||
'<input class="form-control fullwidth {{CSS.INPUTURL}}" type="url" ' +
|
||||
'<input name="{{FORMNAMES.URL}}" class="form-control fullwidth {{CSS.INPUTURL}}" type="url" ' +
|
||||
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
|
||||
'</div>' +
|
||||
'{{/if}}' +
|
||||
|
||||
// Add the Alt box.
|
||||
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEALTWARNING}}">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTALT}}">' +
|
||||
'{{get_string "presentationoraltrequired" component}}' +
|
||||
'</label>' +
|
||||
'</div>' +
|
||||
// Add the Alt box.
|
||||
'<div class="mb-1">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
|
||||
'<textarea class="form-control fullwidth {{CSS.INPUTALT}}" ' +
|
||||
'id="{{elementid}}_{{CSS.INPUTALT}}" maxlength="125"></textarea>' +
|
||||
'id="{{elementid}}_{{CSS.INPUTALT}}" name="{{FORMNAMES.ALT}}" maxlength="125"></textarea>' +
|
||||
|
||||
// Add the character count.
|
||||
'<div id="the-count" class="d-flex justify-content-end small">' +
|
||||
@ -250,7 +261,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
this.editor.on('paste', this._handlePaste, this);
|
||||
this.editor.on('drop', this._handleDragDrop, this);
|
||||
|
||||
// e.preventDefault needed to stop the default event from clobbering the desired behaviour in some browsers.
|
||||
// ...e.preventDefault needed to stop the default event from clobbering the desired behaviour in some browsers.
|
||||
this.editor.on('dragover', function(e) {
|
||||
e.preventDefault();
|
||||
}, this);
|
||||
@ -600,6 +611,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
content = Y.Node.create(template({
|
||||
elementid: this.get('host').get('elementid'),
|
||||
CSS: CSS,
|
||||
FORMNAMES: FORMNAMES,
|
||||
component: COMPONENTNAME,
|
||||
showFilepicker: canShowFilepicker,
|
||||
alignments: ALIGNMENTS
|
||||
@ -611,8 +623,9 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
this._applyImageProperties(this._form);
|
||||
|
||||
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
||||
this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._updateWarning, this);
|
||||
this._form.one('.' + CSS.INPUTALT).on('change', this._updateWarning, this);
|
||||
this._form.one('.' + CSS.INPUTURL).on('change', this._hasErrorUrlField, this);
|
||||
this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._hasErrorAltField, this);
|
||||
this._form.one('.' + CSS.INPUTALT).on('blur', this._hasErrorAltField, this);
|
||||
this._form.one('.' + CSS.INPUTWIDTH).on('blur', this._autoAdjustSize, this);
|
||||
this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
|
||||
this._form.one('.' + CSS.INPUTCONSTRAIN).on('change', function(event) {
|
||||
@ -620,7 +633,6 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
this._autoAdjustSize(event);
|
||||
}
|
||||
}, this);
|
||||
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
||||
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
|
||||
|
||||
if (canShowFilepicker) {
|
||||
@ -1047,6 +1059,38 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
return CSS.ALIGNSETTINGS + '_' + alignment;
|
||||
},
|
||||
|
||||
_toggleVisibility: function(selector, predicate) {
|
||||
var form = this._form;
|
||||
var element = form.all(selector);
|
||||
element.setStyle('display', predicate ? 'block' : 'none');
|
||||
},
|
||||
|
||||
_toggleAriaInvalid: function(selectors, predicate) {
|
||||
var form = this._form;
|
||||
selectors.forEach(function(selector) {
|
||||
var element = form.all(selector);
|
||||
element.setAttribute('aria-invalid', predicate);
|
||||
});
|
||||
},
|
||||
|
||||
_hasErrorUrlField: function() {
|
||||
var form = this._form;
|
||||
var url = form.one('.' + CSS.INPUTURL).get('value');
|
||||
var urlerror = url === '';
|
||||
this._toggleVisibility('.' + CSS.IMAGEURLWARNING, urlerror);
|
||||
this._toggleAriaInvalid(['.' + CSS.INPUTURL], urlerror);
|
||||
return urlerror;
|
||||
},
|
||||
|
||||
_hasErrorAltField: function() {
|
||||
var form = this._form;
|
||||
var alt = form.one('.' + CSS.INPUTALT).get('value');
|
||||
var presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked');
|
||||
var imagealterror = alt === '' && !presentation;
|
||||
this._toggleVisibility('.' + CSS.IMAGEALTWARNING, imagealterror);
|
||||
this._toggleAriaInvalid(['.' + CSS.INPUTALT, '.' + CSS.IMAGEPRESENTATION], imagealterror);
|
||||
return imagealterror;
|
||||
},
|
||||
/**
|
||||
* Update the alt text warning live.
|
||||
*
|
||||
@ -1055,23 +1099,11 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
* @private
|
||||
*/
|
||||
_updateWarning: function() {
|
||||
var form = this._form,
|
||||
state = true,
|
||||
alt = form.one('.' + CSS.INPUTALT).get('value'),
|
||||
presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked');
|
||||
if (alt === '' && !presentation) {
|
||||
form.one('.' + CSS.IMAGEALTWARNING).setStyle('display', 'block');
|
||||
form.one('.' + CSS.INPUTALT).setAttribute('aria-invalid', true);
|
||||
form.one('.' + CSS.IMAGEPRESENTATION).setAttribute('aria-invalid', true);
|
||||
state = true;
|
||||
} else {
|
||||
form.one('.' + CSS.IMAGEALTWARNING).setStyle('display', 'none');
|
||||
form.one('.' + CSS.INPUTALT).setAttribute('aria-invalid', false);
|
||||
form.one('.' + CSS.IMAGEPRESENTATION).setAttribute('aria-invalid', false);
|
||||
state = false;
|
||||
}
|
||||
var urlerror = this._hasErrorUrlField();
|
||||
var imagealterror = this._hasErrorAltField();
|
||||
var haserrors = urlerror || imagealterror;
|
||||
this.getDialogue().centerDialogue();
|
||||
return state;
|
||||
return haserrors;
|
||||
},
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user