Merge branch 'MDL-45227-master' of git://github.com/rezaies/moodle

This commit is contained in:
Sara Arjona
2020-03-31 19:23:01 +02:00
18 changed files with 295 additions and 48 deletions
@@ -83,6 +83,15 @@ Y.namespace('M.atto_align').Button = Y.Base.create('button', Y.M.editor_atto.Edi
document.execCommand(justification, false, null);
// To clean up IE's mess.
this.editor.all('*[align]').each(function(node) {
var align = node.get('align');
if (align) {
node.setStyle('text-align', align);
node.removeAttribute('align');
}
}, this);
// Re-disable the CSS styling after making the change.
host.disableCssStyling();
@@ -1 +1 @@
YUI.add("moodle-atto_align-button",function(e,t){e.namespace("M.atto_align").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{initializer:function(){var e;e="justifyLeft",this.addButton({icon:"e/align_left",title:"leftalign",buttonName:e,callback:this._changeStyle,callbackArgs:e}),e="justifyCenter",this.addButton({icon:"e/align_center",title:"center",buttonName:e,callback:this._changeStyle,callbackArgs:e}),e="justifyRight",this.addButton({icon:"e/align_right",title:"rightalign",buttonName:e,callback:this._changeStyle,callbackArgs:e})},_changeStyle:function(e,t){var n=this.get("host");n.enableCssStyling(),document.execCommand(t,!1,null),n.disableCssStyling(),this.markUpdated(),this.editor.focus()}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
YUI.add("moodle-atto_align-button",function(e,t){e.namespace("M.atto_align").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{initializer:function(){var e;e="justifyLeft",this.addButton({icon:"e/align_left",title:"leftalign",buttonName:e,callback:this._changeStyle,callbackArgs:e}),e="justifyCenter",this.addButton({icon:"e/align_center",title:"center",buttonName:e,callback:this._changeStyle,callbackArgs:e}),e="justifyRight",this.addButton({icon:"e/align_right",title:"rightalign",buttonName:e,callback:this._changeStyle,callbackArgs:e})},_changeStyle:function(e,t){var n=this.get("host");n.enableCssStyling(),document.execCommand(t,!1,null),this.editor.all("*[align]").each(function(e){var t=e.get("align");t&&(e.setStyle("text-align",t),e.removeAttribute("align"))},this),n.disableCssStyling(),this.markUpdated(),this.editor.focus()}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
@@ -83,6 +83,15 @@ Y.namespace('M.atto_align').Button = Y.Base.create('button', Y.M.editor_atto.Edi
document.execCommand(justification, false, null);
// To clean up IE's mess.
this.editor.all('*[align]').each(function(node) {
var align = node.get('align');
if (align) {
node.setStyle('text-align', align);
node.removeAttribute('align');
}
}, this);
// Re-disable the CSS styling after making the change.
host.disableCssStyling();
@@ -81,6 +81,15 @@ Y.namespace('M.atto_align').Button = Y.Base.create('button', Y.M.editor_atto.Edi
document.execCommand(justification, false, null);
// To clean up IE's mess.
this.editor.all('*[align]').each(function(node) {
var align = node.get('align');
if (align) {
node.setStyle('text-align', align);
node.removeAttribute('align');
}
}, this);
// Re-disable the CSS styling after making the change.
host.disableCssStyling();
@@ -72,6 +72,10 @@ Y.namespace('M.atto_rtl').Button = Y.Base.create('button', Y.M.editor_atto.Edito
newDirection = {
rtl: 'ltr',
ltr: 'rtl'
},
directionAlignment = {
rtl: 'right',
ltr: 'left'
};
if (selection) {
// Format the selection to be sure it has a tag parent (not the contenteditable).
@@ -81,8 +85,10 @@ Y.namespace('M.atto_rtl').Button = Y.Base.create('button', Y.M.editor_atto.Edito
var currentDirection = parentDOMNode.getAttribute('dir');
if (currentDirection === direction) {
parentDOMNode.setAttribute("dir", newDirection[direction]);
parentDOMNode.style.textAlign = directionAlignment[newDirection[direction]];
} else {
parentDOMNode.setAttribute("dir", direction);
parentDOMNode.style.textAlign = directionAlignment[direction];
}
// Change selection from the containing paragraph to the original one.
@@ -1 +1 @@
YUI.add("moodle-atto_rtl-button",function(e,t){e.namespace("M.atto_rtl").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{initializer:function(){var e;e="ltr",this.addButton({icon:"e/left_to_right",title:e,buttonName:e,callback:this._toggleRTL,callbackArgs:e,tags:"[dir=ltr]"}),e="rtl",this.addButton({icon:"e/right_to_left",title:e,buttonName:e,callback:this._toggleRTL,callbackArgs:e,tags:"[dir=rtl]"})},_toggleRTL:function(e,t){var n=this.get("host"),r=window.rangy.saveSelection(),i=n.getSelection(),s={rtl:"ltr",ltr:"rtl"};if(i){var o=n.formatSelectionBlock(),u=o.getDOMNode(),a=u.getAttribute("dir");a===t?u.setAttribute("dir",s[t]):u.setAttribute("dir",t),window.rangy.restoreSelection(r),this.markUpdated()}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
YUI.add("moodle-atto_rtl-button",function(e,t){e.namespace("M.atto_rtl").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{initializer:function(){var e;e="ltr",this.addButton({icon:"e/left_to_right",title:e,buttonName:e,callback:this._toggleRTL,callbackArgs:e,tags:"[dir=ltr]"}),e="rtl",this.addButton({icon:"e/right_to_left",title:e,buttonName:e,callback:this._toggleRTL,callbackArgs:e,tags:"[dir=rtl]"})},_toggleRTL:function(e,t){var n=this.get("host"),r=window.rangy.saveSelection(),i=n.getSelection(),s={rtl:"ltr",ltr:"rtl"},o={rtl:"right",ltr:"left"};if(i){var u=n.formatSelectionBlock(),a=u.getDOMNode(),f=a.getAttribute("dir");f===t?(a.setAttribute("dir",s[t]),a.style.textAlign=o[s[t]]):(a.setAttribute("dir",t),a.style.textAlign=o[t]),window.rangy.restoreSelection(r),this.markUpdated()}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
@@ -72,6 +72,10 @@ Y.namespace('M.atto_rtl').Button = Y.Base.create('button', Y.M.editor_atto.Edito
newDirection = {
rtl: 'ltr',
ltr: 'rtl'
},
directionAlignment = {
rtl: 'right',
ltr: 'left'
};
if (selection) {
// Format the selection to be sure it has a tag parent (not the contenteditable).
@@ -81,8 +85,10 @@ Y.namespace('M.atto_rtl').Button = Y.Base.create('button', Y.M.editor_atto.Edito
var currentDirection = parentDOMNode.getAttribute('dir');
if (currentDirection === direction) {
parentDOMNode.setAttribute("dir", newDirection[direction]);
parentDOMNode.style.textAlign = directionAlignment[newDirection[direction]];
} else {
parentDOMNode.setAttribute("dir", direction);
parentDOMNode.style.textAlign = directionAlignment[direction];
}
// Change selection from the containing paragraph to the original one.
@@ -70,6 +70,10 @@ Y.namespace('M.atto_rtl').Button = Y.Base.create('button', Y.M.editor_atto.Edito
newDirection = {
rtl: 'ltr',
ltr: 'rtl'
},
directionAlignment = {
rtl: 'right',
ltr: 'left'
};
if (selection) {
// Format the selection to be sure it has a tag parent (not the contenteditable).
@@ -79,8 +83,10 @@ Y.namespace('M.atto_rtl').Button = Y.Base.create('button', Y.M.editor_atto.Edito
var currentDirection = parentDOMNode.getAttribute('dir');
if (currentDirection === direction) {
parentDOMNode.setAttribute("dir", newDirection[direction]);
parentDOMNode.style.textAlign = directionAlignment[newDirection[direction]];
} else {
parentDOMNode.setAttribute("dir", direction);
parentDOMNode.style.textAlign = directionAlignment[direction];
}
// Change selection from the containing paragraph to the original one.
@@ -25,6 +25,8 @@
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/../../../../behat/behat_base.php');
/**
* Steps definitions to deal with the atto text editor
*
@@ -0,0 +1,49 @@
@editor @editor_atto @atto
Feature: Add text direction and alignment
In order to generate a content that can be displayed in the proper direction to everyone
As a user
I should see the Atto editor with explicit direction and alignment being set
Background:
Given the following "user preferences" exist:
| user | preference | value |
| admin | htmleditor | atto |
And I log in as "admin"
And I navigate to "Plugins > Text editors > Atto HTML editor > Atto toolbar settings" in site administration
And I set the field "Toolbar config" to multiline:
"""
collapse = collapse
style1 = title, bold, italic
list = unorderedlist, orderedlist
links = link
files = image, media, recordrtc, managefiles, h5p
style2 = underline, strike, subscript, superscript
align = align,rtl
indent = indent
insert = equation, charmap, table, clear
undo = undo
accessibility = accessibilitychecker, accessibilityhelper
other = html
"""
And I press "Save changes"
And I log out
@javascript
Scenario Outline: Atto should apply user's direction and alignment by default
Given the following "courses" exist:
| fullname | shortname | summary | summaryformat |
| Course 1 | C1 | | 1 |
And the following "language customisations" exist:
| component | stringid | value |
| <component> | <stringid> | <localstring> |
And I log in as "admin"
And I am on "Course 1" course homepage
When I navigate to "Edit settings" in current page administration
And I press "Show more buttons"
And I press "HTML"
Then I should see "<partialtext>"
Examples:
| component | stringid | localstring | partialtext |
| core_langconfig | thisdirection | ltr | dir=\"ltr\" style=\"text-align: left;\" |
| core_langconfig | thisdirection | rtl | dir=\"rtl\" style=\"text-align: right;\" |
@@ -150,6 +150,14 @@ Y.extend(Editor, Y.Base, {
*/
plugins: null,
/**
* An indicator of the current input direction.
*
* @property coreDirection
* @type string
*/
coreDirection: null,
/**
* Event Handles to clear on editor destruction.
*
@@ -196,6 +204,9 @@ Y.extend(Editor, Y.Base, {
this.editor.setAttribute('aria-labelledby', this.textareaLabel.get("id"));
}
// Set diretcion according to current page language.
this.coreDirection = Y.one('body').hasClass('dir-rtl') ? 'rtl' : 'ltr';
// Add everything to the wrapper.
this.setupToolbar();
@@ -681,10 +692,16 @@ EditorTextArea.prototype = {
* @private
*/
_getEmptyContent: function() {
if (Y.UA.ie && Y.UA.ie < 10) {
return '<p></p>';
var alignment;
if (this.coreDirection === 'rtl') {
alignment = 'style="text-align: right;"';
} else {
return '<p><br></p>';
alignment = 'style="text-align: left;"';
}
if (Y.UA.ie && Y.UA.ie < 10) {
return '<p dir="' + this.coreDirection + '" ' + alignment + '></p>';
} else {
return '<p dir="' + this.coreDirection + '" ' + alignment + '><br></p>';
}
},
@@ -889,14 +906,24 @@ EditorAutosave.prototype = {
}
// Revert untouched editor contents to an empty string.
// Check for FF and Chrome.
if (response.result === '<p></p>' || response.result === '<p><br></p>' ||
response.result === '<br>') {
response.result = '';
}
// Check for IE 9 and 10.
if (response.result === '<p>&nbsp;</p>' || response.result === '<p><br>&nbsp;</p>') {
var emptyContents = [
// For FF and Chrome.
'<p></p>',
'<p><br></p>',
'<br>',
'<p dir="rtl" style="text-align: right;"></p>',
'<p dir="rtl" style="text-align: right;"><br></p>',
'<p dir="ltr" style="text-align: left;"></p>',
'<p dir="ltr" style="text-align: left;"><br></p>',
// For IE 9 and 10.
'<p>&nbsp;</p>',
'<p><br>&nbsp;</p>',
'<p dir="rtl" style="text-align: right;">&nbsp;</p>',
'<p dir="rtl" style="text-align: right;"><br>&nbsp;</p>',
'<p dir="ltr" style="text-align: left;">&nbsp;</p>',
'<p dir="ltr" style="text-align: left;"><br>&nbsp;</p>'
];
if (emptyContents.includes(response.result)) {
response.result = '';
}
@@ -1324,12 +1351,29 @@ EditorClean.prototype = {
html = editorClone.get('innerHTML');
// Revert untouched editor contents to an empty string.
if (html === '<p></p>' || html === '<p><br></p>') {
var emptyContents = [
// For FF and Chrome.
'<p></p>',
'<p><br></p>',
'<br>',
'<p dir="rtl" style="text-align: right;"></p>',
'<p dir="rtl" style="text-align: right;"><br></p>',
'<p dir="ltr" style="text-align: left;"></p>',
'<p dir="ltr" style="text-align: left;"><br></p>',
// For IE 9 and 10.
'<p>&nbsp;</p>',
'<p><br>&nbsp;</p>',
'<p dir="rtl" style="text-align: right;">&nbsp;</p>',
'<p dir="rtl" style="text-align: right;"><br>&nbsp;</p>',
'<p dir="ltr" style="text-align: left;">&nbsp;</p>',
'<p dir="ltr" style="text-align: left;"><br>&nbsp;</p>'
];
if (emptyContents.includes(html)) {
return '';
}
// Remove any and all nasties from source.
return this._cleanHTML(html);
return this._cleanHTML(html);
},
/**
@@ -2729,8 +2773,14 @@ EditorStyling.prototype = {
// No valid block element - make one.
if (!nearestblock) {
var alignment;
if (this.coreDirection === 'rtl') {
alignment = 'style="text-align: right;"';
} else {
alignment = 'style="text-align: left;"';
}
// There is no block node in the content, wrap the content in a p and use that.
newcontent = Y.Node.create('<p></p>');
newcontent = Y.Node.create('<p dir="' + this.coreDirection + '" ' + alignment + '></p>');
boundary.get('childNodes').each(function(child) {
newcontent.append(child.remove());
});
File diff suppressed because one or more lines are too long
@@ -150,6 +150,14 @@ Y.extend(Editor, Y.Base, {
*/
plugins: null,
/**
* An indicator of the current input direction.
*
* @property coreDirection
* @type string
*/
coreDirection: null,
/**
* Event Handles to clear on editor destruction.
*
@@ -194,6 +202,9 @@ Y.extend(Editor, Y.Base, {
this.editor.setAttribute('aria-labelledby', this.textareaLabel.get("id"));
}
// Set diretcion according to current page language.
this.coreDirection = Y.one('body').hasClass('dir-rtl') ? 'rtl' : 'ltr';
// Add everything to the wrapper.
this.setupToolbar();
@@ -676,10 +687,16 @@ EditorTextArea.prototype = {
* @private
*/
_getEmptyContent: function() {
if (Y.UA.ie && Y.UA.ie < 10) {
return '<p></p>';
var alignment;
if (this.coreDirection === 'rtl') {
alignment = 'style="text-align: right;"';
} else {
return '<p><br></p>';
alignment = 'style="text-align: left;"';
}
if (Y.UA.ie && Y.UA.ie < 10) {
return '<p dir="' + this.coreDirection + '" ' + alignment + '></p>';
} else {
return '<p dir="' + this.coreDirection + '" ' + alignment + '><br></p>';
}
},
@@ -883,14 +900,24 @@ EditorAutosave.prototype = {
}
// Revert untouched editor contents to an empty string.
// Check for FF and Chrome.
if (response.result === '<p></p>' || response.result === '<p><br></p>' ||
response.result === '<br>') {
response.result = '';
}
// Check for IE 9 and 10.
if (response.result === '<p>&nbsp;</p>' || response.result === '<p><br>&nbsp;</p>') {
var emptyContents = [
// For FF and Chrome.
'<p></p>',
'<p><br></p>',
'<br>',
'<p dir="rtl" style="text-align: right;"></p>',
'<p dir="rtl" style="text-align: right;"><br></p>',
'<p dir="ltr" style="text-align: left;"></p>',
'<p dir="ltr" style="text-align: left;"><br></p>',
// For IE 9 and 10.
'<p>&nbsp;</p>',
'<p><br>&nbsp;</p>',
'<p dir="rtl" style="text-align: right;">&nbsp;</p>',
'<p dir="rtl" style="text-align: right;"><br>&nbsp;</p>',
'<p dir="ltr" style="text-align: left;">&nbsp;</p>',
'<p dir="ltr" style="text-align: left;"><br>&nbsp;</p>'
];
if (emptyContents.includes(response.result)) {
response.result = '';
}
@@ -1313,12 +1340,29 @@ EditorClean.prototype = {
html = editorClone.get('innerHTML');
// Revert untouched editor contents to an empty string.
if (html === '<p></p>' || html === '<p><br></p>') {
var emptyContents = [
// For FF and Chrome.
'<p></p>',
'<p><br></p>',
'<br>',
'<p dir="rtl" style="text-align: right;"></p>',
'<p dir="rtl" style="text-align: right;"><br></p>',
'<p dir="ltr" style="text-align: left;"></p>',
'<p dir="ltr" style="text-align: left;"><br></p>',
// For IE 9 and 10.
'<p>&nbsp;</p>',
'<p><br>&nbsp;</p>',
'<p dir="rtl" style="text-align: right;">&nbsp;</p>',
'<p dir="rtl" style="text-align: right;"><br>&nbsp;</p>',
'<p dir="ltr" style="text-align: left;">&nbsp;</p>',
'<p dir="ltr" style="text-align: left;"><br>&nbsp;</p>'
];
if (emptyContents.includes(html)) {
return '';
}
// Remove any and all nasties from source.
return this._cleanHTML(html);
return this._cleanHTML(html);
},
/**
@@ -2715,8 +2759,14 @@ EditorStyling.prototype = {
// No valid block element - make one.
if (!nearestblock) {
var alignment;
if (this.coreDirection === 'rtl') {
alignment = 'style="text-align: right;"';
} else {
alignment = 'style="text-align: left;"';
}
// There is no block node in the content, wrap the content in a p and use that.
newcontent = Y.Node.create('<p></p>');
newcontent = Y.Node.create('<p dir="' + this.coreDirection + '" ' + alignment + '></p>');
boundary.get('childNodes').each(function(child) {
newcontent.append(child.remove());
});
+18 -8
View File
@@ -143,14 +143,24 @@ EditorAutosave.prototype = {
}
// Revert untouched editor contents to an empty string.
// Check for FF and Chrome.
if (response.result === '<p></p>' || response.result === '<p><br></p>' ||
response.result === '<br>') {
response.result = '';
}
// Check for IE 9 and 10.
if (response.result === '<p>&nbsp;</p>' || response.result === '<p><br>&nbsp;</p>') {
var emptyContents = [
// For FF and Chrome.
'<p></p>',
'<p><br></p>',
'<br>',
'<p dir="rtl" style="text-align: right;"></p>',
'<p dir="rtl" style="text-align: right;"><br></p>',
'<p dir="ltr" style="text-align: left;"></p>',
'<p dir="ltr" style="text-align: left;"><br></p>',
// For IE 9 and 10.
'<p>&nbsp;</p>',
'<p><br>&nbsp;</p>',
'<p dir="rtl" style="text-align: right;">&nbsp;</p>',
'<p dir="rtl" style="text-align: right;"><br>&nbsp;</p>',
'<p dir="ltr" style="text-align: left;">&nbsp;</p>',
'<p dir="ltr" style="text-align: left;"><br>&nbsp;</p>'
];
if (emptyContents.includes(response.result)) {
response.result = '';
}
+19 -2
View File
@@ -55,12 +55,29 @@ EditorClean.prototype = {
html = editorClone.get('innerHTML');
// Revert untouched editor contents to an empty string.
if (html === '<p></p>' || html === '<p><br></p>') {
var emptyContents = [
// For FF and Chrome.
'<p></p>',
'<p><br></p>',
'<br>',
'<p dir="rtl" style="text-align: right;"></p>',
'<p dir="rtl" style="text-align: right;"><br></p>',
'<p dir="ltr" style="text-align: left;"></p>',
'<p dir="ltr" style="text-align: left;"><br></p>',
// For IE 9 and 10.
'<p>&nbsp;</p>',
'<p><br>&nbsp;</p>',
'<p dir="rtl" style="text-align: right;">&nbsp;</p>',
'<p dir="rtl" style="text-align: right;"><br>&nbsp;</p>',
'<p dir="ltr" style="text-align: left;">&nbsp;</p>',
'<p dir="ltr" style="text-align: left;"><br>&nbsp;</p>'
];
if (emptyContents.includes(html)) {
return '';
}
// Remove any and all nasties from source.
return this._cleanHTML(html);
return this._cleanHTML(html);
},
/**
+11
View File
@@ -148,6 +148,14 @@ Y.extend(Editor, Y.Base, {
*/
plugins: null,
/**
* An indicator of the current input direction.
*
* @property coreDirection
* @type string
*/
coreDirection: null,
/**
* Event Handles to clear on editor destruction.
*
@@ -194,6 +202,9 @@ Y.extend(Editor, Y.Base, {
this.editor.setAttribute('aria-labelledby', this.textareaLabel.get("id"));
}
// Set diretcion according to current page language.
this.coreDirection = Y.one('body').hasClass('dir-rtl') ? 'rtl' : 'ltr';
// Add everything to the wrapper.
this.setupToolbar();
+7 -1
View File
@@ -175,8 +175,14 @@ EditorStyling.prototype = {
// No valid block element - make one.
if (!nearestblock) {
var alignment;
if (this.coreDirection === 'rtl') {
alignment = 'style="text-align: right;"';
} else {
alignment = 'style="text-align: left;"';
}
// There is no block node in the content, wrap the content in a p and use that.
newcontent = Y.Node.create('<p></p>');
newcontent = Y.Node.create('<p dir="' + this.coreDirection + '" ' + alignment + '></p>');
boundary.get('childNodes').each(function(child) {
newcontent.append(child.remove());
});
+9 -3
View File
@@ -45,10 +45,16 @@ EditorTextArea.prototype = {
* @private
*/
_getEmptyContent: function() {
if (Y.UA.ie && Y.UA.ie < 10) {
return '<p></p>';
var alignment;
if (this.coreDirection === 'rtl') {
alignment = 'style="text-align: right;"';
} else {
return '<p><br></p>';
alignment = 'style="text-align: left;"';
}
if (Y.UA.ie && Y.UA.ie < 10) {
return '<p dir="' + this.coreDirection + '" ' + alignment + '></p>';
} else {
return '<p dir="' + this.coreDirection + '" ' + alignment + '><br></p>';
}
},