mirror of
https://github.com/moodle/moodle.git
synced 2025-08-07 01:46:45 +02:00
Merge branch 'MDL-71674-311' of https://github.com/dcai/moodle into MOODLE_311_STABLE
This commit is contained in:
commit
0a9017dad3
8 changed files with 203 additions and 80 deletions
|
@ -21,8 +21,11 @@ Feature: Atto accessibility checker
|
||||||
And I click on ".moodle-dialogue-focused .closebutton" "css_element"
|
And I click on ".moodle-dialogue-focused .closebutton" "css_element"
|
||||||
And I select the text in the "Description" Atto editor
|
And I select the text in the "Description" Atto editor
|
||||||
And I click on "Insert or edit image" "button"
|
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 "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 "Save image"
|
||||||
And I press "Accessibility checker"
|
And I press "Accessibility checker"
|
||||||
And I should see "Congratulations, no accessibility problems found!"
|
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['presentation'] = 'This image is decorative only';
|
||||||
$string['pluginname'] = 'Insert or edit image';
|
$string['pluginname'] = 'Insert or edit image';
|
||||||
$string['presentationoraltrequired'] = 'An image must have a description, unless it is marked as decorative only.';
|
$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['preview'] = 'Preview';
|
||||||
$string['saveimage'] = 'Save image';
|
$string['saveimage'] = 'Save image';
|
||||||
$string['size'] = 'Size';
|
$string['size'] = 'Size';
|
||||||
|
|
|
@ -47,6 +47,7 @@ function atto_image_strings_for_js() {
|
||||||
'height',
|
'height',
|
||||||
'presentation',
|
'presentation',
|
||||||
'presentationoraltrequired',
|
'presentationoraltrequired',
|
||||||
|
'imageurlrequired',
|
||||||
'size',
|
'size',
|
||||||
'width',
|
'width',
|
||||||
'uploading',
|
'uploading',
|
||||||
|
|
|
@ -58,7 +58,8 @@ Feature: Add images to Atto
|
||||||
When I click on "Insert or edit image" "button"
|
When I click on "Insert or edit image" "button"
|
||||||
Then the field "Enter URL" matches value "/nothing/here"
|
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 "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 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 set the field "Description" to "<p>Image: <img src='/nothing/again' width='123' height='456' alt='Awesome!'>.</p>"
|
||||||
And I press "Update profile"
|
And I press "Update profile"
|
||||||
|
@ -69,3 +70,24 @@ Feature: Add images to Atto
|
||||||
And the field "Width" matches value "123"
|
And the field "Width" matches value "123"
|
||||||
And the field "Height" matches value "456"
|
And the field "Height" matches value "456"
|
||||||
And the field "Describe this image" matches value "Awesome!"
|
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',
|
INPUTSIZE: 'atto_image_size',
|
||||||
INPUTWIDTH: 'atto_image_widthentry',
|
INPUTWIDTH: 'atto_image_widthentry',
|
||||||
IMAGEALTWARNING: 'atto_image_altwarning',
|
IMAGEALTWARNING: 'atto_image_altwarning',
|
||||||
|
IMAGEURLWARNING: 'atto_image_urlwarning',
|
||||||
IMAGEBROWSER: 'openimagebrowser',
|
IMAGEBROWSER: 'openimagebrowser',
|
||||||
IMAGEPRESENTATION: 'atto_image_presentation',
|
IMAGEPRESENTATION: 'atto_image_presentation',
|
||||||
INPUTCONSTRAIN: 'atto_image_constrain',
|
INPUTCONSTRAIN: 'atto_image_constrain',
|
||||||
|
@ -51,6 +52,10 @@ var CSS = {
|
||||||
IMAGEPREVIEWBOX: 'atto_image_preview_box',
|
IMAGEPREVIEWBOX: 'atto_image_preview_box',
|
||||||
ALIGNSETTINGS: 'atto_image_button'
|
ALIGNSETTINGS: 'atto_image_button'
|
||||||
},
|
},
|
||||||
|
FORMNAMES = {
|
||||||
|
URL: 'urlentry',
|
||||||
|
ALT: 'altentry'
|
||||||
|
},
|
||||||
SELECTORS = {
|
SELECTORS = {
|
||||||
INPUTURL: '.' + CSS.INPUTURL
|
INPUTURL: '.' + CSS.INPUTURL
|
||||||
},
|
},
|
||||||
|
@ -96,13 +101,17 @@ var CSS = {
|
||||||
|
|
||||||
TEMPLATE = '' +
|
TEMPLATE = '' +
|
||||||
'<form class="atto_form">' +
|
'<form class="atto_form">' +
|
||||||
|
|
||||||
// Add the repository browser button.
|
// 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}}' +
|
'{{#if showFilepicker}}' +
|
||||||
'<div class="mb-1">' +
|
'<div class="mb-1">' +
|
||||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
||||||
'<div class="input-group input-append w-100">' +
|
'<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"/>' +
|
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
|
||||||
'<span class="input-group-append">' +
|
'<span class="input-group-append">' +
|
||||||
'<button class="btn btn-secondary {{CSS.IMAGEBROWSER}}" type="button">' +
|
'<button class="btn btn-secondary {{CSS.IMAGEBROWSER}}" type="button">' +
|
||||||
|
@ -113,19 +122,21 @@ var CSS = {
|
||||||
'{{else}}' +
|
'{{else}}' +
|
||||||
'<div class="mb-1">' +
|
'<div class="mb-1">' +
|
||||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
'<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"/>' +
|
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'{{/if}}' +
|
'{{/if}}' +
|
||||||
|
|
||||||
// Add the Alt box.
|
|
||||||
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEALTWARNING}}">' +
|
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEALTWARNING}}">' +
|
||||||
|
'<label for="{{elementid}}_{{CSS.INPUTALT}}">' +
|
||||||
'{{get_string "presentationoraltrequired" component}}' +
|
'{{get_string "presentationoraltrequired" component}}' +
|
||||||
|
'</label>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
|
// Add the Alt box.
|
||||||
'<div class="mb-1">' +
|
'<div class="mb-1">' +
|
||||||
'<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
|
'<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
|
||||||
'<textarea class="form-control fullwidth {{CSS.INPUTALT}}" ' +
|
'<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.
|
// Add the character count.
|
||||||
'<div id="the-count" class="d-flex justify-content-end small">' +
|
'<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('paste', this._handlePaste, this);
|
||||||
this.editor.on('drop', this._handleDragDrop, 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) {
|
this.editor.on('dragover', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}, this);
|
}, 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({
|
content = Y.Node.create(template({
|
||||||
elementid: this.get('host').get('elementid'),
|
elementid: this.get('host').get('elementid'),
|
||||||
CSS: CSS,
|
CSS: CSS,
|
||||||
|
FORMNAMES: FORMNAMES,
|
||||||
component: COMPONENTNAME,
|
component: COMPONENTNAME,
|
||||||
showFilepicker: canShowFilepicker,
|
showFilepicker: canShowFilepicker,
|
||||||
alignments: ALIGNMENTS
|
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._applyImageProperties(this._form);
|
||||||
|
|
||||||
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
||||||
this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._updateWarning, this);
|
this._form.one('.' + CSS.INPUTURL).on('change', this._hasErrorUrlField, this);
|
||||||
this._form.one('.' + CSS.INPUTALT).on('change', this._updateWarning, 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.INPUTWIDTH).on('blur', this._autoAdjustSize, this);
|
||||||
this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
|
this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
|
||||||
this._form.one('.' + CSS.INPUTCONSTRAIN).on('change', function(event) {
|
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._autoAdjustSize(event);
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
|
||||||
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
|
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
|
||||||
|
|
||||||
if (canShowFilepicker) {
|
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;
|
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.
|
* 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
|
* @private
|
||||||
*/
|
*/
|
||||||
_updateWarning: function() {
|
_updateWarning: function() {
|
||||||
var form = this._form,
|
var urlerror = this._hasErrorUrlField();
|
||||||
state = true,
|
var imagealterror = this._hasErrorAltField();
|
||||||
alt = form.one('.' + CSS.INPUTALT).get('value'),
|
var haserrors = urlerror || imagealterror;
|
||||||
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;
|
|
||||||
}
|
|
||||||
this.getDialogue().centerDialogue();
|
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',
|
INPUTSIZE: 'atto_image_size',
|
||||||
INPUTWIDTH: 'atto_image_widthentry',
|
INPUTWIDTH: 'atto_image_widthentry',
|
||||||
IMAGEALTWARNING: 'atto_image_altwarning',
|
IMAGEALTWARNING: 'atto_image_altwarning',
|
||||||
|
IMAGEURLWARNING: 'atto_image_urlwarning',
|
||||||
IMAGEBROWSER: 'openimagebrowser',
|
IMAGEBROWSER: 'openimagebrowser',
|
||||||
IMAGEPRESENTATION: 'atto_image_presentation',
|
IMAGEPRESENTATION: 'atto_image_presentation',
|
||||||
INPUTCONSTRAIN: 'atto_image_constrain',
|
INPUTCONSTRAIN: 'atto_image_constrain',
|
||||||
|
@ -51,6 +52,10 @@ var CSS = {
|
||||||
IMAGEPREVIEWBOX: 'atto_image_preview_box',
|
IMAGEPREVIEWBOX: 'atto_image_preview_box',
|
||||||
ALIGNSETTINGS: 'atto_image_button'
|
ALIGNSETTINGS: 'atto_image_button'
|
||||||
},
|
},
|
||||||
|
FORMNAMES = {
|
||||||
|
URL: 'urlentry',
|
||||||
|
ALT: 'altentry'
|
||||||
|
},
|
||||||
SELECTORS = {
|
SELECTORS = {
|
||||||
INPUTURL: '.' + CSS.INPUTURL
|
INPUTURL: '.' + CSS.INPUTURL
|
||||||
},
|
},
|
||||||
|
@ -96,13 +101,17 @@ var CSS = {
|
||||||
|
|
||||||
TEMPLATE = '' +
|
TEMPLATE = '' +
|
||||||
'<form class="atto_form">' +
|
'<form class="atto_form">' +
|
||||||
|
|
||||||
// Add the repository browser button.
|
// 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}}' +
|
'{{#if showFilepicker}}' +
|
||||||
'<div class="mb-1">' +
|
'<div class="mb-1">' +
|
||||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
||||||
'<div class="input-group input-append w-100">' +
|
'<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"/>' +
|
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
|
||||||
'<span class="input-group-append">' +
|
'<span class="input-group-append">' +
|
||||||
'<button class="btn btn-secondary {{CSS.IMAGEBROWSER}}" type="button">' +
|
'<button class="btn btn-secondary {{CSS.IMAGEBROWSER}}" type="button">' +
|
||||||
|
@ -113,19 +122,21 @@ var CSS = {
|
||||||
'{{else}}' +
|
'{{else}}' +
|
||||||
'<div class="mb-1">' +
|
'<div class="mb-1">' +
|
||||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
'<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"/>' +
|
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'{{/if}}' +
|
'{{/if}}' +
|
||||||
|
|
||||||
// Add the Alt box.
|
|
||||||
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEALTWARNING}}">' +
|
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEALTWARNING}}">' +
|
||||||
|
'<label for="{{elementid}}_{{CSS.INPUTALT}}">' +
|
||||||
'{{get_string "presentationoraltrequired" component}}' +
|
'{{get_string "presentationoraltrequired" component}}' +
|
||||||
|
'</label>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
|
// Add the Alt box.
|
||||||
'<div class="mb-1">' +
|
'<div class="mb-1">' +
|
||||||
'<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
|
'<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
|
||||||
'<textarea class="form-control fullwidth {{CSS.INPUTALT}}" ' +
|
'<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.
|
// Add the character count.
|
||||||
'<div id="the-count" class="d-flex justify-content-end small">' +
|
'<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('paste', this._handlePaste, this);
|
||||||
this.editor.on('drop', this._handleDragDrop, 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) {
|
this.editor.on('dragover', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}, this);
|
}, 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({
|
content = Y.Node.create(template({
|
||||||
elementid: this.get('host').get('elementid'),
|
elementid: this.get('host').get('elementid'),
|
||||||
CSS: CSS,
|
CSS: CSS,
|
||||||
|
FORMNAMES: FORMNAMES,
|
||||||
component: COMPONENTNAME,
|
component: COMPONENTNAME,
|
||||||
showFilepicker: canShowFilepicker,
|
showFilepicker: canShowFilepicker,
|
||||||
alignments: ALIGNMENTS
|
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._applyImageProperties(this._form);
|
||||||
|
|
||||||
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
||||||
this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._updateWarning, this);
|
this._form.one('.' + CSS.INPUTURL).on('change', this._hasErrorUrlField, this);
|
||||||
this._form.one('.' + CSS.INPUTALT).on('change', this._updateWarning, 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.INPUTWIDTH).on('blur', this._autoAdjustSize, this);
|
||||||
this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
|
this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
|
||||||
this._form.one('.' + CSS.INPUTCONSTRAIN).on('change', function(event) {
|
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._autoAdjustSize(event);
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
|
||||||
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
|
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
|
||||||
|
|
||||||
if (canShowFilepicker) {
|
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;
|
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.
|
* 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
|
* @private
|
||||||
*/
|
*/
|
||||||
_updateWarning: function() {
|
_updateWarning: function() {
|
||||||
var form = this._form,
|
var urlerror = this._hasErrorUrlField();
|
||||||
state = true,
|
var imagealterror = this._hasErrorAltField();
|
||||||
alt = form.one('.' + CSS.INPUTALT).get('value'),
|
var haserrors = urlerror || imagealterror;
|
||||||
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;
|
|
||||||
}
|
|
||||||
this.getDialogue().centerDialogue();
|
this.getDialogue().centerDialogue();
|
||||||
return state;
|
return haserrors;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -41,6 +41,7 @@ var CSS = {
|
||||||
INPUTSIZE: 'atto_image_size',
|
INPUTSIZE: 'atto_image_size',
|
||||||
INPUTWIDTH: 'atto_image_widthentry',
|
INPUTWIDTH: 'atto_image_widthentry',
|
||||||
IMAGEALTWARNING: 'atto_image_altwarning',
|
IMAGEALTWARNING: 'atto_image_altwarning',
|
||||||
|
IMAGEURLWARNING: 'atto_image_urlwarning',
|
||||||
IMAGEBROWSER: 'openimagebrowser',
|
IMAGEBROWSER: 'openimagebrowser',
|
||||||
IMAGEPRESENTATION: 'atto_image_presentation',
|
IMAGEPRESENTATION: 'atto_image_presentation',
|
||||||
INPUTCONSTRAIN: 'atto_image_constrain',
|
INPUTCONSTRAIN: 'atto_image_constrain',
|
||||||
|
@ -49,6 +50,10 @@ var CSS = {
|
||||||
IMAGEPREVIEWBOX: 'atto_image_preview_box',
|
IMAGEPREVIEWBOX: 'atto_image_preview_box',
|
||||||
ALIGNSETTINGS: 'atto_image_button'
|
ALIGNSETTINGS: 'atto_image_button'
|
||||||
},
|
},
|
||||||
|
FORMNAMES = {
|
||||||
|
URL: 'urlentry',
|
||||||
|
ALT: 'altentry'
|
||||||
|
},
|
||||||
SELECTORS = {
|
SELECTORS = {
|
||||||
INPUTURL: '.' + CSS.INPUTURL
|
INPUTURL: '.' + CSS.INPUTURL
|
||||||
},
|
},
|
||||||
|
@ -94,13 +99,17 @@ var CSS = {
|
||||||
|
|
||||||
TEMPLATE = '' +
|
TEMPLATE = '' +
|
||||||
'<form class="atto_form">' +
|
'<form class="atto_form">' +
|
||||||
|
|
||||||
// Add the repository browser button.
|
// 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}}' +
|
'{{#if showFilepicker}}' +
|
||||||
'<div class="mb-1">' +
|
'<div class="mb-1">' +
|
||||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
||||||
'<div class="input-group input-append w-100">' +
|
'<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"/>' +
|
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
|
||||||
'<span class="input-group-append">' +
|
'<span class="input-group-append">' +
|
||||||
'<button class="btn btn-secondary {{CSS.IMAGEBROWSER}}" type="button">' +
|
'<button class="btn btn-secondary {{CSS.IMAGEBROWSER}}" type="button">' +
|
||||||
|
@ -111,19 +120,21 @@ var CSS = {
|
||||||
'{{else}}' +
|
'{{else}}' +
|
||||||
'<div class="mb-1">' +
|
'<div class="mb-1">' +
|
||||||
'<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
|
'<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"/>' +
|
'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'{{/if}}' +
|
'{{/if}}' +
|
||||||
|
|
||||||
// Add the Alt box.
|
|
||||||
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEALTWARNING}}">' +
|
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.IMAGEALTWARNING}}">' +
|
||||||
|
'<label for="{{elementid}}_{{CSS.INPUTALT}}">' +
|
||||||
'{{get_string "presentationoraltrequired" component}}' +
|
'{{get_string "presentationoraltrequired" component}}' +
|
||||||
|
'</label>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
|
// Add the Alt box.
|
||||||
'<div class="mb-1">' +
|
'<div class="mb-1">' +
|
||||||
'<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
|
'<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
|
||||||
'<textarea class="form-control fullwidth {{CSS.INPUTALT}}" ' +
|
'<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.
|
// Add the character count.
|
||||||
'<div id="the-count" class="d-flex justify-content-end small">' +
|
'<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('paste', this._handlePaste, this);
|
||||||
this.editor.on('drop', this._handleDragDrop, 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) {
|
this.editor.on('dragover', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}, this);
|
}, 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({
|
content = Y.Node.create(template({
|
||||||
elementid: this.get('host').get('elementid'),
|
elementid: this.get('host').get('elementid'),
|
||||||
CSS: CSS,
|
CSS: CSS,
|
||||||
|
FORMNAMES: FORMNAMES,
|
||||||
component: COMPONENTNAME,
|
component: COMPONENTNAME,
|
||||||
showFilepicker: canShowFilepicker,
|
showFilepicker: canShowFilepicker,
|
||||||
alignments: ALIGNMENTS
|
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._applyImageProperties(this._form);
|
||||||
|
|
||||||
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
||||||
this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._updateWarning, this);
|
this._form.one('.' + CSS.INPUTURL).on('change', this._hasErrorUrlField, this);
|
||||||
this._form.one('.' + CSS.INPUTALT).on('change', this._updateWarning, 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.INPUTWIDTH).on('blur', this._autoAdjustSize, this);
|
||||||
this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
|
this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
|
||||||
this._form.one('.' + CSS.INPUTCONSTRAIN).on('change', function(event) {
|
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._autoAdjustSize(event);
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
|
|
||||||
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
|
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
|
||||||
|
|
||||||
if (canShowFilepicker) {
|
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;
|
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.
|
* 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
|
* @private
|
||||||
*/
|
*/
|
||||||
_updateWarning: function() {
|
_updateWarning: function() {
|
||||||
var form = this._form,
|
var urlerror = this._hasErrorUrlField();
|
||||||
state = true,
|
var imagealterror = this._hasErrorAltField();
|
||||||
alt = form.one('.' + CSS.INPUTALT).get('value'),
|
var haserrors = urlerror || imagealterror;
|
||||||
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;
|
|
||||||
}
|
|
||||||
this.getDialogue().centerDialogue();
|
this.getDialogue().centerDialogue();
|
||||||
return state;
|
return haserrors;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue