first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for block_activity_modules.
*
* @package atto_accessibilitychecker
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace atto_accessibilitychecker\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for atto_accessibilitychecker implementing null_provider.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,36 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_accessibilitychecker', language 'en'.
*
* @package atto_accessibilitychecker
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['emptytext'] = 'Empty text';
$string['entiredocument'] = 'Entire document';
$string['imagesmissingalt'] = 'Images require alternative text. To fix this warning, add an alt attribute to your img tags. An empty alt attribute may be used, but only when the image is purely decorative and carries no information.';
$string['needsmorecontrast'] = 'The colours of the foreground and background text do not have enough contrast. To fix this warning, change either foreground or background colour of the text so that it is easier to read.';
$string['needsmoreheadings'] = 'There is a lot of text with no headings. Headings will allow screen reader users to navigate through the page easily and will make the page more usable for everyone.';
$string['nowarnings'] = 'Congratulations, no accessibility problems found!';
$string['pluginname'] = 'Accessibility checker';
$string['report'] = 'Accessibility report:';
$string['tablesmissingcaption'] = 'Tables should have captions. While it is not necessary for each table to have a caption, a caption is generally very helpful.';
$string['tablesmissingheaders'] = 'Tables should use row and/or column headers.';
$string['tableswithmergedcells'] = 'Tables should not contain merged cells. Despite being standard markup for tables for many years, some screen readers still do not fully support complex tables. When possible, try to "flatten" the table and avoid merged cells.';
$string['privacy:metadata'] = 'The atto_accessibilitychecker plugin does not store any personal data.';
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_accessibilitychecker
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Initialise this plugin
* @param string $elementid
*/
function atto_accessibilitychecker_strings_for_js() {
global $PAGE;
$PAGE->requires->strings_for_js(array('nowarnings',
'report',
'imagesmissingalt',
'needsmorecontrast',
'needsmoreheadings',
'tableswithmergedcells',
'tablesmissingcaption',
'emptytext',
'entiredocument',
'tablesmissingheaders'),
'atto_accessibilitychecker');
}
@@ -0,0 +1,4 @@
.accessibilitywarnings img {
max-width: 32px;
max-height: 32px;
}
@@ -0,0 +1,76 @@
@editor @editor_atto @atto @atto_accessibilitychecker
Feature: Atto accessibility checker
To write accessible text in Atto, I need to check for accessibility warnings.
@javascript
Scenario: Images with no alt
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<p>Some plain text</p><img src='/broken-image' width='1' height='1'/><p>Some more text</p>"
When I click on "Show more buttons" "button"
And I click on "Accessibility checker" "button"
Then I should see "Images require alternative text."
And I follow "/broken-image"
And I wait "2" seconds
And I click on "Insert or edit image" "button"
And the field "Enter URL" matches value "/broken-image"
And I set the field "Describe this image for someone who cannot see it" to "No more warning!"
And I press "Save image"
And I press "Accessibility checker"
And I should see "Congratulations, no accessibility problems found!"
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 "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!"
@javascript
Scenario: Low contrast
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<p style='color: #7c7cff; background-color: #ffffff;'>Hard to read</p>"
When I click on "Show more buttons" "button"
And I click on "Accessibility checker" "button"
Then I should see "The colours of the foreground and background text do not have enough contrast."
@javascript
Scenario: No headings
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<p>Sweet roll oat cake jelly-o macaroon donut oat cake. Caramels macaroon cookie sweet roll croissant cheesecake candy jelly-o. Gummies sugar plum sugar plum gingerbread dessert. Tiramisu bonbon jujubes danish marshmallow cookie chocolate cake cupcake tiramisu. Bear claw oat cake chocolate bar croissant. Lollipop cookie topping liquorice croissant. Brownie cookie cupcake lollipop cupcake cupcake. Fruitcake dessert sweet biscuit dragée caramels marzipan brownie. Chupa chups gingerbread apple pie cookie liquorice caramels carrot cake cookie gingerbread. Croissant candy jelly beans. Tiramisu apple pie dessert apple pie macaroon soufflé. Brownie powder carrot cake chocolate. Tart applicake croissant dragée macaroon chocolate donut.</p><p>Jelly beans gingerbread tootsie roll. Sugar plum tiramisu cotton candy toffee pie cotton candy tiramisu. Carrot cake chocolate bar sesame snaps cupcake cake dessert sweet fruitcake wafer. Marshmallow cupcake gingerbread pie sweet candy canes powder gummi bears. Jujubes cake muffin marshmallow candy jelly beans tootsie roll pie. Gummi bears applicake chocolate cake sweet jelly sesame snaps lollipop lollipop carrot cake. Marshmallow cake jelly beans. Jelly beans sesame snaps muffin halvah cookie ice cream candy canes carrot cake. Halvah donut marshmallow tiramisu. Cookie dessert gummi bears. Sugar plum apple pie jelly beans gummi bears tart chupa chups. Liquorice macaroon gummi bears gummies macaroon marshmallow sweet roll cake topping. Lemon drops caramels pie icing danish. Chocolate cake oat cake dessert halvah danish carrot cake apple pie.</p>"
When I click on "Show more buttons" "button"
And I click on "Accessibility checker" "button"
Then I should see "There is a lot of text with no headings."
@javascript
Scenario: Merged cells
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<table><caption>Dogs that look good in pants</caption><tr><th>Breed</th><th>Coolness</th></tr><tr><td>Poodle</td><td rowspan='2'>NOT COOL</td></tr><tr><td>Doberman</td></tr></table>"
When I click on "Show more buttons" "button"
And I click on "Accessibility checker" "button"
Then I should see "Tables should not contain merged cells."
@javascript
Scenario: Table missing row/column headers
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<table><caption>Dogs that look good in pants</caption><tr><th>Breed</th><td>Coolness</td></tr><tr><td>Poodle</td><td>NOT COOL</td></tr><tr><td>Doberman</td><td>COOL</td></tr></table>"
When I click on "Show more buttons" "button"
And I click on "Accessibility checker" "button"
Then I should see "Tables should use row and/or column headers."
@javascript
Scenario: Table missing caption
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<table><tr><th>Breed</th><th>Coolness</th></tr><tr><td>Poodle</td><td>NOT COOL</td></tr><tr><td>Doberman</td><td>COOL</td></tr></table>"
When I click on "Show more buttons" "button"
And I click on "Accessibility checker" "button"
Then I should see "Tables should have captions."
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_accessibilitychecker
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'atto_accessibilitychecker'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,357 @@
YUI.add('moodle-atto_accessibilitychecker-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_accessibilitychecker
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_accessibilitychecker-button
*/
/**
* Accessibility Checking tool for the Atto editor.
*
* @namespace M.atto_accessibilitychecker
* @class Button
* @extends M.editor_atto.EditorPlugin
*/
var COMPONENT = 'atto_accessibilitychecker';
Y.namespace('M.atto_accessibilitychecker').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addButton({
icon: 'e/accessibility_checker',
callback: this._displayDialogue
});
},
/**
* Display the Accessibility Checker tool.
*
* @method _displayDialogue
* @private
*/
_displayDialogue: function() {
var dialogue = this.getDialogue({
headerContent: M.util.get_string('pluginname', COMPONENT),
width: '500px',
focusAfterHide: true
});
// Set the dialogue content, and then show the dialogue.
dialogue.set('bodyContent', this._getDialogueContent())
.show();
},
/**
* Return the dialogue content for the tool.
*
* @method _getDialogueContent
* @private
* @return {Node} The content to place in the dialogue.
*/
_getDialogueContent: function() {
var content = Y.Node.create('<div style="word-wrap: break-word;"></div>');
content.append(this._getWarnings());
// Add ability to select problem areas in the editor.
content.delegate('click', function(e) {
e.preventDefault();
var host = this.get('host'),
node = e.currentTarget.getData('sourceNode'),
dialogue = this.getDialogue();
if (node) {
// Focus on the editor as we hide the dialogue.
dialogue.set('focusAfterHide', this.editor).hide();
// Then set the selection.
host.setSelection(host.getSelectionFromNode(node));
} else {
// Hide the dialogue.
dialogue.hide();
}
}, 'a', this);
return content;
},
/**
* Find all problems with the content editable region.
*
* @method _getWarnings
* @return {Node} A complete list of all warnings and problems.
* @private
*/
_getWarnings: function() {
var problemNodes,
list = Y.Node.create('<div></div>');
// Images with no alt text or dodgy alt text.
problemNodes = [];
this.editor.all('img').each(function(img) {
var alt = img.getAttribute('alt');
if (typeof alt === 'undefined' || alt === '') {
if (img.getAttribute('role') !== 'presentation') {
problemNodes.push(img);
}
}
}, this);
this._addWarnings(list, M.util.get_string('imagesmissingalt', COMPONENT), problemNodes, true);
problemNodes = [];
this.editor.all('*').each(function(node) {
var foreground,
background,
ratio,
lum1,
lum2;
// Check for non-empty text.
if (node.hasChildNodes() && Y.Lang.trim(node._node.childNodes[0].nodeValue) !== '') {
foreground = Y.Color.fromArray(
this._getComputedBackgroundColor(node, node.getComputedStyle('color')),
Y.Color.TYPES.RGBA
);
background = Y.Color.fromArray(this._getComputedBackgroundColor(node), Y.Color.TYPES.RGBA);
lum1 = this._getLuminanceFromCssColor(foreground);
lum2 = this._getLuminanceFromCssColor(background);
// Algorithm from "http://www.w3.org/TR/WCAG20-GENERAL/G18.html".
if (lum1 > lum2) {
ratio = (lum1 + 0.05) / (lum2 + 0.05);
} else {
ratio = (lum2 + 0.05) / (lum1 + 0.05);
}
if (ratio <= 4.5) {
Y.log('Contrast ratio is too low: ' + ratio +
' Colour 1: ' + foreground +
' Colour 2: ' + background +
' Luminance 1: ' + lum1 +
' Luminance 2: ' + lum2);
// We only want the highest node with dodgy contrast reported.
var i = 0;
var found = false;
for (i = 0; i < problemNodes.length; i++) {
if (node.ancestors('*').indexOf(problemNodes[i]) !== -1) {
// Do not add node - it already has a parent in the list.
found = true;
break;
} else if (problemNodes[i].ancestors('*').indexOf(node) !== -1) {
// Replace the existing node with this one because it is higher up the DOM.
problemNodes[i] = node;
found = true;
break;
}
}
if (!found) {
problemNodes.push(node);
}
}
}
}, this);
this._addWarnings(list, M.util.get_string('needsmorecontrast', COMPONENT), problemNodes, false);
// Check for lots of text with no headings.
if (this.editor.get('text').length > 1000 && !this.editor.one('h3, h4, h5')) {
this._addWarnings(list, M.util.get_string('needsmoreheadings', COMPONENT), [this.editor], false);
}
// Check for tables with no captions.
problemNodes = [];
this.editor.all('table').each(function(table) {
var caption = table.one('caption');
if (caption === null || caption.get('text').trim() === '') {
problemNodes.push(table);
}
}, this);
this._addWarnings(list, M.util.get_string('tablesmissingcaption', COMPONENT), problemNodes, false);
// Check for tables with merged cells.
problemNodes = [];
this.editor.all('table').each(function(table) {
var caption = table.one('[colspan],[rowspan]');
if (caption !== null) {
problemNodes.push(table);
}
}, this);
this._addWarnings(list, M.util.get_string('tableswithmergedcells', COMPONENT), problemNodes, false);
// Check for tables with no row/col headers
problemNodes = [];
this.editor.all('table').each(function(table) {
if (table.one('tr').one('td')) {
// First row has a non-header cell, so all rows must have at least one header.
table.all('tr').some(function(row) {
var header = row.one('th');
if (!header || (header.get('text').trim() === '')) {
problemNodes.push(table);
return true;
}
return false;
}, this);
} else {
// First row must have at least one header then.
var hasHeader = false;
table.one('tr').all('th').some(function(header) {
hasHeader = true;
if (header.get('text').trim() === '') {
problemNodes.push(table);
return true;
}
return false;
});
if (!hasHeader) {
problemNodes.push(table);
}
}
}, this);
this._addWarnings(list, M.util.get_string('tablesmissingheaders', COMPONENT), problemNodes, false);
if (!list.hasChildNodes()) {
list.append('<p>' + M.util.get_string('nowarnings', COMPONENT) + '</p>');
}
// Return the list of current warnings.
return list;
},
/**
* Generate the HTML that lists the found warnings.
*
* @method _addWarnings
* @param {Node} list Node to append the html to.
* @param {String} description Description of this failure.
* @param {array} nodes An array of failing nodes.
* @param {boolean} imagewarnings true if the warnings are related to images, false if text.
*/
_addWarnings: function(list, description, nodes, imagewarnings) {
var warning, fails, i, src, textfield, li, link, text;
if (nodes.length > 0) {
warning = Y.Node.create('<p>' + description + '</p>');
fails = Y.Node.create('<ol class="accessibilitywarnings"></ol>');
i = 0;
for (i = 0; i < nodes.length; i++) {
li = Y.Node.create('<li></li>');
if (imagewarnings) {
src = nodes[i].getAttribute('src');
link = Y.Node.create('<a href="#"><img src="' + src + '" /> ' + src + '</a>');
} else {
textfield = ('innerText' in nodes[i]) ? 'innerText' : 'textContent';
text = nodes[i].get(textfield).trim();
if (text === '') {
text = M.util.get_string('emptytext', COMPONENT);
}
if (nodes[i] === this.editor) {
text = M.util.get_string('entiredocument', COMPONENT);
}
link = Y.Node.create('<a href="#">' + text + '</a>');
}
link.setData('sourceNode', nodes[i]);
li.append(link);
fails.append(li);
}
warning.append(fails);
list.append(warning);
}
},
/**
* Convert a CSS color to a luminance value.
*
* @method _getLuminanceFromCssColor
* @param {String} colortext The Hex value for the colour
* @return {Number} The luminance value.
* @private
*/
_getLuminanceFromCssColor: function(colortext) {
var color;
if (colortext === 'transparent') {
colortext = '#ffffff';
}
color = Y.Color.toArray(Y.Color.toRGB(colortext));
// Algorithm from "http://www.w3.org/TR/WCAG20-GENERAL/G18.html".
var part1 = function(a) {
a = parseInt(a, 10) / 255.0;
if (a <= 0.03928) {
a = a / 12.92;
} else {
a = Math.pow(((a + 0.055) / 1.055), 2.4);
}
return a;
};
var r1 = part1(color[0]),
g1 = part1(color[1]),
b1 = part1(color[2]);
return 0.2126 * r1 + 0.7152 * g1 + 0.0722 * b1;
},
/**
* Get the computed RGB converted to full alpha value, considering the node hierarchy.
*
* @method _getComputedBackgroundColor
* @param {Node} node
* @param {String} color The initial colour. If not specified, fetches the backgroundColor from the node.
* @return {Array} Colour in Array form (RGBA)
* @private
*/
_getComputedBackgroundColor: function(node, color) {
color = color || node.getComputedStyle('backgroundColor');
if (color.toLowerCase() === 'transparent') {
// Y.Color doesn't handle 'transparent' properly.
color = 'rgba(1, 1, 1, 0)';
}
// Convert the colour to its constituent parts in RGBA format, then fetch the alpha.
var colorParts = Y.Color.toArray(color);
var alpha = colorParts[3];
if (alpha === 1) {
// If the alpha of the background is already 1, then the parent background colour does not change anything.
return colorParts;
}
// Fetch the computed background colour of the parent and use it to calculate the RGB of this item.
var parentColor = this._getComputedBackgroundColor(node.get('parentNode'));
return [
// RGB = (alpha * R|G|B) + (1 - alpha * solid parent colour).
(1 - alpha) * parentColor[0] + alpha * colorParts[0],
(1 - alpha) * parentColor[1] + alpha * colorParts[1],
(1 - alpha) * parentColor[2] + alpha * colorParts[2],
// We always return a colour with full alpha.
1
];
}
});
}, '@VERSION@', {"requires": ["color-base", "moodle-editor_atto-plugin"]});
@@ -0,0 +1 @@
YUI.add("moodle-atto_accessibilitychecker-button",function(d,t){var g="atto_accessibilitychecker";d.namespace("M.atto_accessibilitychecker").Button=d.Base.create("button",d.M.editor_atto.EditorPlugin,[],{initializer:function(){this.addButton({icon:"e/accessibility_checker",callback:this._displayDialogue})},_displayDialogue:function(){this.getDialogue({headerContent:M.util.get_string("pluginname",g),width:"500px",focusAfterHide:!0}).set("bodyContent",this._getDialogueContent()).show()},_getDialogueContent:function(){var t=d.Node.create('<div style="word-wrap: break-word;"></div>');return t.append(this._getWarnings()),t.delegate("click",function(t){t.preventDefault();var e=this.get("host"),t=t.currentTarget.getData("sourceNode"),i=this.getDialogue();t?(i.set("focusAfterHide",this.editor).hide(),e.setSelection(e.getSelectionFromNode(t))):i.hide()},"a",this),t},_getWarnings:function(){var t=d.Node.create("<div></div>"),n=[];return this.editor.all("img").each(function(t){var e=t.getAttribute("alt");void 0!==e&&""!==e||"presentation"!==t.getAttribute("role")&&n.push(t)},this),this._addWarnings(t,M.util.get_string("imagesmissingalt",g),n,!0),n=[],this.editor.all("*").each(function(t){var e,i,o,r;if(t.hasChildNodes()&&""!==d.Lang.trim(t._node.childNodes[0].nodeValue)&&(e=d.Color.fromArray(this._getComputedBackgroundColor(t,t.getComputedStyle("color")),d.Color.TYPES.RGBA),i=d.Color.fromArray(this._getComputedBackgroundColor(t),d.Color.TYPES.RGBA),e=this._getLuminanceFromCssColor(e),((i=this._getLuminanceFromCssColor(i))<e?(e+.05)/(i+.05):(i+.05)/(e+.05))<=4.5)){for(r=!1,o=o=0;o<n.length;o++){if(-1!==t.ancestors("*").indexOf(n[o])){r=!0;break}if(-1!==n[o].ancestors("*").indexOf(t)){n[o]=t,r=!0;break}}r||n.push(t)}},this),this._addWarnings(t,M.util.get_string("needsmorecontrast",g),n,!1),1e3<this.editor.get("text").length&&!this.editor.one("h3, h4, h5")&&this._addWarnings(t,M.util.get_string("needsmoreheadings",g),[this.editor],!1),n=[],this.editor.all("table").each(function(t){var e=t.one("caption");null!==e&&""!==e.get("text").trim()||n.push(t)},this),this._addWarnings(t,M.util.get_string("tablesmissingcaption",g),n,!1),n=[],this.editor.all("table").each(function(t){null!==t.one("[colspan],[rowspan]")&&n.push(t)},this),this._addWarnings(t,M.util.get_string("tableswithmergedcells",g),n,!1),n=[],this.editor.all("table").each(function(e){var i;e.one("tr").one("td")?e.all("tr").some(function(t){t=t.one("th");return(!t||""===t.get("text").trim())&&(n.push(e),!0)},this):(i=!1,e.one("tr").all("th").some(function(t){return i=!0,""===t.get("text").trim()&&(n.push(e),!0)}),i||n.push(e))},this),this._addWarnings(t,M.util.get_string("tablesmissingheaders",g),n,!1),t.hasChildNodes()||t.append("<p>"+M.util.get_string("nowarnings",g)+"</p>"),t},_addWarnings:function(t,e,i,o){var r,n,a,s,l;if(0<i.length){for(e=d.Node.create("<p>"+e+"</p>"),r=d.Node.create('<ol class="accessibilitywarnings"></ol>'),n=n=0;n<i.length;n++)s=d.Node.create("<li></li>"),(l=o?(a=i[n].getAttribute("src"),d.Node.create('<a href="#"><img src="'+a+'" /> '+a+"</a>")):(a="innerText"in i[n]?"innerText":"textContent",""===(l=i[n].get(a).trim())&&(l=M.util.get_string("emptytext",g)),i[n]===this.editor&&(l=M.util.get_string("entiredocument",g)),d.Node.create('<a href="#">'+l+"</a>"))).setData("sourceNode",i[n]),s.append(l),r.append(s);e.append(r),t.append(e)}},_getLuminanceFromCssColor:function(t){var e;return.2126*(e=function(t){return(t=parseInt(t,10)/255)<=.03928?t/=12.92:t=Math.pow((t+.055)/1.055,2.4),t})((t=d.Color.toArray(d.Color.toRGB(t="transparent"===t?"#ffffff":t)))[0])+.7152*e(t[1])+.0722*e(t[2])},_getComputedBackgroundColor:function(t,e){var i;return"transparent"===(e=e||t.getComputedStyle("backgroundColor")).toLowerCase()&&(e="rgba(1, 1, 1, 0)"),1===(i=(e=d.Color.toArray(e))[3])?e:[(1-i)*(t=this._getComputedBackgroundColor(t.get("parentNode")))[0]+i*e[0],(1-i)*t[1]+i*e[1],(1-i)*t[2]+i*e[2],1]}})},"@VERSION@",{requires:["color-base","moodle-editor_atto-plugin"]});
@@ -0,0 +1,352 @@
YUI.add('moodle-atto_accessibilitychecker-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_accessibilitychecker
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_accessibilitychecker-button
*/
/**
* Accessibility Checking tool for the Atto editor.
*
* @namespace M.atto_accessibilitychecker
* @class Button
* @extends M.editor_atto.EditorPlugin
*/
var COMPONENT = 'atto_accessibilitychecker';
Y.namespace('M.atto_accessibilitychecker').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addButton({
icon: 'e/accessibility_checker',
callback: this._displayDialogue
});
},
/**
* Display the Accessibility Checker tool.
*
* @method _displayDialogue
* @private
*/
_displayDialogue: function() {
var dialogue = this.getDialogue({
headerContent: M.util.get_string('pluginname', COMPONENT),
width: '500px',
focusAfterHide: true
});
// Set the dialogue content, and then show the dialogue.
dialogue.set('bodyContent', this._getDialogueContent())
.show();
},
/**
* Return the dialogue content for the tool.
*
* @method _getDialogueContent
* @private
* @return {Node} The content to place in the dialogue.
*/
_getDialogueContent: function() {
var content = Y.Node.create('<div style="word-wrap: break-word;"></div>');
content.append(this._getWarnings());
// Add ability to select problem areas in the editor.
content.delegate('click', function(e) {
e.preventDefault();
var host = this.get('host'),
node = e.currentTarget.getData('sourceNode'),
dialogue = this.getDialogue();
if (node) {
// Focus on the editor as we hide the dialogue.
dialogue.set('focusAfterHide', this.editor).hide();
// Then set the selection.
host.setSelection(host.getSelectionFromNode(node));
} else {
// Hide the dialogue.
dialogue.hide();
}
}, 'a', this);
return content;
},
/**
* Find all problems with the content editable region.
*
* @method _getWarnings
* @return {Node} A complete list of all warnings and problems.
* @private
*/
_getWarnings: function() {
var problemNodes,
list = Y.Node.create('<div></div>');
// Images with no alt text or dodgy alt text.
problemNodes = [];
this.editor.all('img').each(function(img) {
var alt = img.getAttribute('alt');
if (typeof alt === 'undefined' || alt === '') {
if (img.getAttribute('role') !== 'presentation') {
problemNodes.push(img);
}
}
}, this);
this._addWarnings(list, M.util.get_string('imagesmissingalt', COMPONENT), problemNodes, true);
problemNodes = [];
this.editor.all('*').each(function(node) {
var foreground,
background,
ratio,
lum1,
lum2;
// Check for non-empty text.
if (node.hasChildNodes() && Y.Lang.trim(node._node.childNodes[0].nodeValue) !== '') {
foreground = Y.Color.fromArray(
this._getComputedBackgroundColor(node, node.getComputedStyle('color')),
Y.Color.TYPES.RGBA
);
background = Y.Color.fromArray(this._getComputedBackgroundColor(node), Y.Color.TYPES.RGBA);
lum1 = this._getLuminanceFromCssColor(foreground);
lum2 = this._getLuminanceFromCssColor(background);
// Algorithm from "http://www.w3.org/TR/WCAG20-GENERAL/G18.html".
if (lum1 > lum2) {
ratio = (lum1 + 0.05) / (lum2 + 0.05);
} else {
ratio = (lum2 + 0.05) / (lum1 + 0.05);
}
if (ratio <= 4.5) {
// We only want the highest node with dodgy contrast reported.
var i = 0;
var found = false;
for (i = 0; i < problemNodes.length; i++) {
if (node.ancestors('*').indexOf(problemNodes[i]) !== -1) {
// Do not add node - it already has a parent in the list.
found = true;
break;
} else if (problemNodes[i].ancestors('*').indexOf(node) !== -1) {
// Replace the existing node with this one because it is higher up the DOM.
problemNodes[i] = node;
found = true;
break;
}
}
if (!found) {
problemNodes.push(node);
}
}
}
}, this);
this._addWarnings(list, M.util.get_string('needsmorecontrast', COMPONENT), problemNodes, false);
// Check for lots of text with no headings.
if (this.editor.get('text').length > 1000 && !this.editor.one('h3, h4, h5')) {
this._addWarnings(list, M.util.get_string('needsmoreheadings', COMPONENT), [this.editor], false);
}
// Check for tables with no captions.
problemNodes = [];
this.editor.all('table').each(function(table) {
var caption = table.one('caption');
if (caption === null || caption.get('text').trim() === '') {
problemNodes.push(table);
}
}, this);
this._addWarnings(list, M.util.get_string('tablesmissingcaption', COMPONENT), problemNodes, false);
// Check for tables with merged cells.
problemNodes = [];
this.editor.all('table').each(function(table) {
var caption = table.one('[colspan],[rowspan]');
if (caption !== null) {
problemNodes.push(table);
}
}, this);
this._addWarnings(list, M.util.get_string('tableswithmergedcells', COMPONENT), problemNodes, false);
// Check for tables with no row/col headers
problemNodes = [];
this.editor.all('table').each(function(table) {
if (table.one('tr').one('td')) {
// First row has a non-header cell, so all rows must have at least one header.
table.all('tr').some(function(row) {
var header = row.one('th');
if (!header || (header.get('text').trim() === '')) {
problemNodes.push(table);
return true;
}
return false;
}, this);
} else {
// First row must have at least one header then.
var hasHeader = false;
table.one('tr').all('th').some(function(header) {
hasHeader = true;
if (header.get('text').trim() === '') {
problemNodes.push(table);
return true;
}
return false;
});
if (!hasHeader) {
problemNodes.push(table);
}
}
}, this);
this._addWarnings(list, M.util.get_string('tablesmissingheaders', COMPONENT), problemNodes, false);
if (!list.hasChildNodes()) {
list.append('<p>' + M.util.get_string('nowarnings', COMPONENT) + '</p>');
}
// Return the list of current warnings.
return list;
},
/**
* Generate the HTML that lists the found warnings.
*
* @method _addWarnings
* @param {Node} list Node to append the html to.
* @param {String} description Description of this failure.
* @param {array} nodes An array of failing nodes.
* @param {boolean} imagewarnings true if the warnings are related to images, false if text.
*/
_addWarnings: function(list, description, nodes, imagewarnings) {
var warning, fails, i, src, textfield, li, link, text;
if (nodes.length > 0) {
warning = Y.Node.create('<p>' + description + '</p>');
fails = Y.Node.create('<ol class="accessibilitywarnings"></ol>');
i = 0;
for (i = 0; i < nodes.length; i++) {
li = Y.Node.create('<li></li>');
if (imagewarnings) {
src = nodes[i].getAttribute('src');
link = Y.Node.create('<a href="#"><img src="' + src + '" /> ' + src + '</a>');
} else {
textfield = ('innerText' in nodes[i]) ? 'innerText' : 'textContent';
text = nodes[i].get(textfield).trim();
if (text === '') {
text = M.util.get_string('emptytext', COMPONENT);
}
if (nodes[i] === this.editor) {
text = M.util.get_string('entiredocument', COMPONENT);
}
link = Y.Node.create('<a href="#">' + text + '</a>');
}
link.setData('sourceNode', nodes[i]);
li.append(link);
fails.append(li);
}
warning.append(fails);
list.append(warning);
}
},
/**
* Convert a CSS color to a luminance value.
*
* @method _getLuminanceFromCssColor
* @param {String} colortext The Hex value for the colour
* @return {Number} The luminance value.
* @private
*/
_getLuminanceFromCssColor: function(colortext) {
var color;
if (colortext === 'transparent') {
colortext = '#ffffff';
}
color = Y.Color.toArray(Y.Color.toRGB(colortext));
// Algorithm from "http://www.w3.org/TR/WCAG20-GENERAL/G18.html".
var part1 = function(a) {
a = parseInt(a, 10) / 255.0;
if (a <= 0.03928) {
a = a / 12.92;
} else {
a = Math.pow(((a + 0.055) / 1.055), 2.4);
}
return a;
};
var r1 = part1(color[0]),
g1 = part1(color[1]),
b1 = part1(color[2]);
return 0.2126 * r1 + 0.7152 * g1 + 0.0722 * b1;
},
/**
* Get the computed RGB converted to full alpha value, considering the node hierarchy.
*
* @method _getComputedBackgroundColor
* @param {Node} node
* @param {String} color The initial colour. If not specified, fetches the backgroundColor from the node.
* @return {Array} Colour in Array form (RGBA)
* @private
*/
_getComputedBackgroundColor: function(node, color) {
color = color || node.getComputedStyle('backgroundColor');
if (color.toLowerCase() === 'transparent') {
// Y.Color doesn't handle 'transparent' properly.
color = 'rgba(1, 1, 1, 0)';
}
// Convert the colour to its constituent parts in RGBA format, then fetch the alpha.
var colorParts = Y.Color.toArray(color);
var alpha = colorParts[3];
if (alpha === 1) {
// If the alpha of the background is already 1, then the parent background colour does not change anything.
return colorParts;
}
// Fetch the computed background colour of the parent and use it to calculate the RGB of this item.
var parentColor = this._getComputedBackgroundColor(node.get('parentNode'));
return [
// RGB = (alpha * R|G|B) + (1 - alpha * solid parent colour).
(1 - alpha) * parentColor[0] + alpha * colorParts[0],
(1 - alpha) * parentColor[1] + alpha * colorParts[1],
(1 - alpha) * parentColor[2] + alpha * colorParts[2],
// We always return a colour with full alpha.
1
];
}
});
}, '@VERSION@', {"requires": ["color-base", "moodle-editor_atto-plugin"]});
@@ -0,0 +1,10 @@
{
"name": "moodle-atto_accessibilitychecker-button",
"builds": {
"moodle-atto_accessibilitychecker-button": {
"jsfiles": [
"button.js"
]
}
}
}
@@ -0,0 +1,352 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_accessibilitychecker
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_accessibilitychecker-button
*/
/**
* Accessibility Checking tool for the Atto editor.
*
* @namespace M.atto_accessibilitychecker
* @class Button
* @extends M.editor_atto.EditorPlugin
*/
var COMPONENT = 'atto_accessibilitychecker';
Y.namespace('M.atto_accessibilitychecker').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addButton({
icon: 'e/accessibility_checker',
callback: this._displayDialogue
});
},
/**
* Display the Accessibility Checker tool.
*
* @method _displayDialogue
* @private
*/
_displayDialogue: function() {
var dialogue = this.getDialogue({
headerContent: M.util.get_string('pluginname', COMPONENT),
width: '500px',
focusAfterHide: true
});
// Set the dialogue content, and then show the dialogue.
dialogue.set('bodyContent', this._getDialogueContent())
.show();
},
/**
* Return the dialogue content for the tool.
*
* @method _getDialogueContent
* @private
* @return {Node} The content to place in the dialogue.
*/
_getDialogueContent: function() {
var content = Y.Node.create('<div style="word-wrap: break-word;"></div>');
content.append(this._getWarnings());
// Add ability to select problem areas in the editor.
content.delegate('click', function(e) {
e.preventDefault();
var host = this.get('host'),
node = e.currentTarget.getData('sourceNode'),
dialogue = this.getDialogue();
if (node) {
// Focus on the editor as we hide the dialogue.
dialogue.set('focusAfterHide', this.editor).hide();
// Then set the selection.
host.setSelection(host.getSelectionFromNode(node));
} else {
// Hide the dialogue.
dialogue.hide();
}
}, 'a', this);
return content;
},
/**
* Find all problems with the content editable region.
*
* @method _getWarnings
* @return {Node} A complete list of all warnings and problems.
* @private
*/
_getWarnings: function() {
var problemNodes,
list = Y.Node.create('<div></div>');
// Images with no alt text or dodgy alt text.
problemNodes = [];
this.editor.all('img').each(function(img) {
var alt = img.getAttribute('alt');
if (typeof alt === 'undefined' || alt === '') {
if (img.getAttribute('role') !== 'presentation') {
problemNodes.push(img);
}
}
}, this);
this._addWarnings(list, M.util.get_string('imagesmissingalt', COMPONENT), problemNodes, true);
problemNodes = [];
this.editor.all('*').each(function(node) {
var foreground,
background,
ratio,
lum1,
lum2;
// Check for non-empty text.
if (node.hasChildNodes() && Y.Lang.trim(node._node.childNodes[0].nodeValue) !== '') {
foreground = Y.Color.fromArray(
this._getComputedBackgroundColor(node, node.getComputedStyle('color')),
Y.Color.TYPES.RGBA
);
background = Y.Color.fromArray(this._getComputedBackgroundColor(node), Y.Color.TYPES.RGBA);
lum1 = this._getLuminanceFromCssColor(foreground);
lum2 = this._getLuminanceFromCssColor(background);
// Algorithm from "http://www.w3.org/TR/WCAG20-GENERAL/G18.html".
if (lum1 > lum2) {
ratio = (lum1 + 0.05) / (lum2 + 0.05);
} else {
ratio = (lum2 + 0.05) / (lum1 + 0.05);
}
if (ratio <= 4.5) {
Y.log('Contrast ratio is too low: ' + ratio +
' Colour 1: ' + foreground +
' Colour 2: ' + background +
' Luminance 1: ' + lum1 +
' Luminance 2: ' + lum2);
// We only want the highest node with dodgy contrast reported.
var i = 0;
var found = false;
for (i = 0; i < problemNodes.length; i++) {
if (node.ancestors('*').indexOf(problemNodes[i]) !== -1) {
// Do not add node - it already has a parent in the list.
found = true;
break;
} else if (problemNodes[i].ancestors('*').indexOf(node) !== -1) {
// Replace the existing node with this one because it is higher up the DOM.
problemNodes[i] = node;
found = true;
break;
}
}
if (!found) {
problemNodes.push(node);
}
}
}
}, this);
this._addWarnings(list, M.util.get_string('needsmorecontrast', COMPONENT), problemNodes, false);
// Check for lots of text with no headings.
if (this.editor.get('text').length > 1000 && !this.editor.one('h3, h4, h5')) {
this._addWarnings(list, M.util.get_string('needsmoreheadings', COMPONENT), [this.editor], false);
}
// Check for tables with no captions.
problemNodes = [];
this.editor.all('table').each(function(table) {
var caption = table.one('caption');
if (caption === null || caption.get('text').trim() === '') {
problemNodes.push(table);
}
}, this);
this._addWarnings(list, M.util.get_string('tablesmissingcaption', COMPONENT), problemNodes, false);
// Check for tables with merged cells.
problemNodes = [];
this.editor.all('table').each(function(table) {
var caption = table.one('[colspan],[rowspan]');
if (caption !== null) {
problemNodes.push(table);
}
}, this);
this._addWarnings(list, M.util.get_string('tableswithmergedcells', COMPONENT), problemNodes, false);
// Check for tables with no row/col headers
problemNodes = [];
this.editor.all('table').each(function(table) {
if (table.one('tr').one('td')) {
// First row has a non-header cell, so all rows must have at least one header.
table.all('tr').some(function(row) {
var header = row.one('th');
if (!header || (header.get('text').trim() === '')) {
problemNodes.push(table);
return true;
}
return false;
}, this);
} else {
// First row must have at least one header then.
var hasHeader = false;
table.one('tr').all('th').some(function(header) {
hasHeader = true;
if (header.get('text').trim() === '') {
problemNodes.push(table);
return true;
}
return false;
});
if (!hasHeader) {
problemNodes.push(table);
}
}
}, this);
this._addWarnings(list, M.util.get_string('tablesmissingheaders', COMPONENT), problemNodes, false);
if (!list.hasChildNodes()) {
list.append('<p>' + M.util.get_string('nowarnings', COMPONENT) + '</p>');
}
// Return the list of current warnings.
return list;
},
/**
* Generate the HTML that lists the found warnings.
*
* @method _addWarnings
* @param {Node} list Node to append the html to.
* @param {String} description Description of this failure.
* @param {array} nodes An array of failing nodes.
* @param {boolean} imagewarnings true if the warnings are related to images, false if text.
*/
_addWarnings: function(list, description, nodes, imagewarnings) {
var warning, fails, i, src, textfield, li, link, text;
if (nodes.length > 0) {
warning = Y.Node.create('<p>' + description + '</p>');
fails = Y.Node.create('<ol class="accessibilitywarnings"></ol>');
i = 0;
for (i = 0; i < nodes.length; i++) {
li = Y.Node.create('<li></li>');
if (imagewarnings) {
src = nodes[i].getAttribute('src');
link = Y.Node.create('<a href="#"><img src="' + src + '" /> ' + src + '</a>');
} else {
textfield = ('innerText' in nodes[i]) ? 'innerText' : 'textContent';
text = nodes[i].get(textfield).trim();
if (text === '') {
text = M.util.get_string('emptytext', COMPONENT);
}
if (nodes[i] === this.editor) {
text = M.util.get_string('entiredocument', COMPONENT);
}
link = Y.Node.create('<a href="#">' + text + '</a>');
}
link.setData('sourceNode', nodes[i]);
li.append(link);
fails.append(li);
}
warning.append(fails);
list.append(warning);
}
},
/**
* Convert a CSS color to a luminance value.
*
* @method _getLuminanceFromCssColor
* @param {String} colortext The Hex value for the colour
* @return {Number} The luminance value.
* @private
*/
_getLuminanceFromCssColor: function(colortext) {
var color;
if (colortext === 'transparent') {
colortext = '#ffffff';
}
color = Y.Color.toArray(Y.Color.toRGB(colortext));
// Algorithm from "http://www.w3.org/TR/WCAG20-GENERAL/G18.html".
var part1 = function(a) {
a = parseInt(a, 10) / 255.0;
if (a <= 0.03928) {
a = a / 12.92;
} else {
a = Math.pow(((a + 0.055) / 1.055), 2.4);
}
return a;
};
var r1 = part1(color[0]),
g1 = part1(color[1]),
b1 = part1(color[2]);
return 0.2126 * r1 + 0.7152 * g1 + 0.0722 * b1;
},
/**
* Get the computed RGB converted to full alpha value, considering the node hierarchy.
*
* @method _getComputedBackgroundColor
* @param {Node} node
* @param {String} color The initial colour. If not specified, fetches the backgroundColor from the node.
* @return {Array} Colour in Array form (RGBA)
* @private
*/
_getComputedBackgroundColor: function(node, color) {
color = color || node.getComputedStyle('backgroundColor');
if (color.toLowerCase() === 'transparent') {
// Y.Color doesn't handle 'transparent' properly.
color = 'rgba(1, 1, 1, 0)';
}
// Convert the colour to its constituent parts in RGBA format, then fetch the alpha.
var colorParts = Y.Color.toArray(color);
var alpha = colorParts[3];
if (alpha === 1) {
// If the alpha of the background is already 1, then the parent background colour does not change anything.
return colorParts;
}
// Fetch the computed background colour of the parent and use it to calculate the RGB of this item.
var parentColor = this._getComputedBackgroundColor(node.get('parentNode'));
return [
// RGB = (alpha * R|G|B) + (1 - alpha * solid parent colour).
(1 - alpha) * parentColor[0] + alpha * colorParts[0],
(1 - alpha) * parentColor[1] + alpha * colorParts[1],
(1 - alpha) * parentColor[2] + alpha * colorParts[2],
// We always return a colour with full alpha.
1
];
}
});
@@ -0,0 +1,8 @@
{
"moodle-atto_accessibilitychecker-button": {
"requires": [
"color-base",
"moodle-editor_atto-plugin"
]
}
}
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for block_activity_modules.
*
* @package atto_accessibilityhelper
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace atto_accessibilityhelper\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for atto_accessibilityhelper implementing null_provider.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,34 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_accessibilityhelper', language 'en'.
*
* @package atto_accessibilityhelper
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Screenreader helper';
$string['liststyles'] = 'Styles for current selection:';
$string['nostyles'] = 'No styles';
$string['listlinks'] = 'Links in text editor:';
$string['nolinks'] = 'No links';
$string['selectlink'] = 'Select link';
$string['listimages'] = 'Images in text editor:';
$string['noimages'] = 'No images';
$string['selectimage'] = 'Select image';
$string['privacy:metadata'] = 'The atto_accessibilityhelper plugin does not store any personal data.';
@@ -0,0 +1,44 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_accessibilityhelper
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Initialise this plugin
* @param string $elementid
*/
function atto_accessibilityhelper_strings_for_js() {
global $PAGE;
$PAGE->requires->strings_for_js(array('liststyles',
'nostyles',
'listlinks',
'nolinks',
'selectlink',
'listimages',
'noimages',
'selectimage'),
'atto_accessibilityhelper');
}
@@ -0,0 +1,32 @@
@editor @editor_atto @atto @atto_accessibilityhelper
Feature: Atto accessibility helper
To use a screen reader effectively in Atto, I may need additional information about the text
@javascript
Scenario: Images and links
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<p>Some plain text</p><img src='/broken-image' alt='Image 1'/><p><a href='#fsd'>Some link text</a></p>"
And I select the text in the "Description" Atto editor
When I click on "Show more buttons" "button"
And I click on "Screenreader helper" "button"
Then I should see "Links in text editor"
And I should see "Some link text"
And I should see "Images in text editor"
And I should see "Image 1"
And I should not see "No images"
And I should not see "No links"
@javascript
Scenario: Styles
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<p>Some plain text</p>"
When I click on "Show more buttons" "button"
And I select the text in the "Description" Atto editor
And I click on "Unordered list" "button"
And I click on "Screenreader helper" "button"
And I select the text in the "Description" Atto editor
# This shows the current HTML tags applied to the selected text.
# This is required because they are not always read by a screen reader.
Then I should see "UL, LI"
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_accessibilityhelper
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'atto_accessibilityhelper'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,280 @@
YUI.add('moodle-atto_accessibilityhelper-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_accessibilityhelper
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_accessibilityhelper-button
*/
/**
* Atto text editor accessibilityhelper plugin.
*
* This plugin adds some functions to do things that screen readers do not do well.
* Specifically, listing the active styles for the selected text,
* listing the images in the page, listing the links in the page.
*
*
* @namespace M.atto_accessibilityhelper
* @class Button
* @extends M.editor_atto.EditorPlugin
*/
var COMPONENT = 'atto_accessibilityhelper',
TEMPLATE = '' +
// The list of styles.
'<div><p id="{{elementid}}_{{CSS.STYLESLABEL}}">' +
'{{get_string "liststyles" component}}<br/>' +
'<span aria-labelledby="{{elementid}}_{{CSS.STYLESLABEL}}" />' +
'</p></div>' +
'<span class="listStyles"></span>' +
'<p id="{{elementid}}_{{CSS.LINKSLABEL}}">' +
'{{get_string "listlinks" component}}<br/>' +
'<span aria-labelledby="{{elementid}}_{{CSS.LINKSLABEL}}"/>' +
'</p>' +
'<span class="listLinks"></span>' +
'<p id="{{elementid}}_{{CSS.IMAGESLABEL}}">' +
'{{get_string "listimages" component}}<br/>' +
'<span aria-labelledby="{{elementid}}_{{CSS.IMAGESLABEL}}"/>' +
'</p>' +
'<span class="listImages"></span>',
CSS = {
STYLESLABEL: COMPONENT + '_styleslabel',
LINKSLABEL: COMPONENT + '_linkslabel',
IMAGESLABEL: COMPONENT + '_imageslabel'
};
Y.namespace('M.atto_accessibilityhelper').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addButton({
icon: 'e/screenreader_helper',
callback: this._displayDialogue
});
},
/**
* Display the Accessibility Helper tool.
*
* @method _displayDialogue
* @private
*/
_displayDialogue: function() {
var dialogue = this.getDialogue({
headerContent: M.util.get_string('pluginname', COMPONENT),
width: '800px',
focusAfterHide: true
});
// Set the dialogue content, and then show the dialogue.
dialogue.set('bodyContent', this._getDialogueContent())
.show();
},
/**
* Return the dialogue content for the tool, attaching any required
* events.
*
* @method _getDialogueContent
* @private
* @return {Node} The content to place in the dialogue.
*/
_getDialogueContent: function() {
var template = Y.Handlebars.compile(TEMPLATE),
content = Y.Node.create(template({
CSS: CSS,
component: COMPONENT
}));
// Add the data.
content.one('.listStyles')
.empty()
.appendChild(this._listStyles());
content.one('.listLinks')
.empty()
.appendChild(this._listLinks());
content.one('.listImages')
.empty()
.appendChild(this._listImages());
return content;
},
/**
* List the styles present for the selection.
*
* @method _listStyles
* @return {String} The list of styles in use.
* @private
*/
_listStyles: function() {
// Clear the status node.
var list = [],
host = this.get('host'),
current = host.getSelectionParentNode(),
tagname;
if (current) {
current = Y.one(current);
}
while (current && (current !== this.editor)) {
tagname = current.get('tagName');
if (typeof tagname !== 'undefined') {
list.push(Y.Escape.html(tagname));
}
current = current.ancestor();
}
if (list.length === 0) {
list.push(M.util.get_string('nostyles', COMPONENT));
}
list.reverse();
// Append the list of current styles.
return list.join(', ');
},
/**
* List the links for the current editor
*
* @method _listLinks
* @return {string}
* @private
*/
_listLinks: function() {
var list = Y.Node.create('<ol />'),
listitem,
selectlink;
this.editor.all('a').each(function(link) {
selectlink = Y.Node.create('<a href="#" title="' +
M.util.get_string('selectlink', COMPONENT) + '">' +
Y.Escape.html(link.get('text')) +
'</a>');
selectlink.setData('sourcelink', link);
selectlink.on('click', this._linkSelected, this);
listitem = Y.Node.create('<li></li>');
listitem.appendChild(selectlink);
list.appendChild(listitem);
}, this);
if (!list.hasChildNodes()) {
list.append('<li>' + M.util.get_string('nolinks', COMPONENT) + '</li>');
}
// Append the list of current styles.
return list;
},
/**
* List the images used in the editor.
*
* @method _listImages
* @return {Node} A Node containing all of the images present in the editor.
* @private
*/
_listImages: function() {
var list = Y.Node.create('<ol/>'),
listitem,
selectimage;
this.editor.all('img').each(function(image) {
// Get the alt or title or img url of the image.
var imgalt = image.getAttribute('alt');
if (imgalt === '') {
imgalt = image.getAttribute('title');
if (imgalt === '') {
imgalt = image.getAttribute('src');
}
}
selectimage = Y.Node.create('<a href="#" title="' +
M.util.get_string('selectimage', COMPONENT) + '">' +
Y.Escape.html(imgalt) +
'</a>');
selectimage.setData('sourceimage', image);
selectimage.on('click', this._imageSelected, this);
listitem = Y.Node.create('<li></li>');
listitem.append(selectimage);
list.append(listitem);
}, this);
if (!list.hasChildNodes()) {
list.append('<li>' + M.util.get_string('noimages', COMPONENT) + '</li>');
}
// Append the list of current styles.
return list;
},
/**
* Event handler for selecting an image.
*
* @method _imageSelected
* @param {EventFacade} e
* @private
*/
_imageSelected: function(e) {
e.preventDefault();
this.getDialogue({
focusAfterNode: null
}).hide();
var host = this.get('host'),
target = e.target.getData('sourceimage');
this.editor.focus();
host.setSelection(host.getSelectionFromNode(target));
},
/**
* Event handler for selecting a link.
*
* @method _linkSelected
* @param {EventFacade} e
* @private
*/
_linkSelected: function(e) {
e.preventDefault();
this.getDialogue({
focusAfterNode: null
}).hide();
var host = this.get('host'),
target = e.target.getData('sourcelink');
this.editor.focus();
host.setSelection(host.getSelectionFromNode(target));
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
@@ -0,0 +1 @@
YUI.add("moodle-atto_accessibilityhelper-button",function(a,e){var n="atto_accessibilityhelper",t={STYLESLABEL:n+"_styleslabel",LINKSLABEL:n+"_linkslabel",IMAGESLABEL:n+"_imageslabel"};a.namespace("M.atto_accessibilityhelper").Button=a.Base.create("button",a.M.editor_atto.EditorPlugin,[],{initializer:function(){this.addButton({icon:"e/screenreader_helper",callback:this._displayDialogue})},_displayDialogue:function(){this.getDialogue({headerContent:M.util.get_string("pluginname",n),width:"800px",focusAfterHide:!0}).set("bodyContent",this._getDialogueContent()).show()},_getDialogueContent:function(){var e=a.Handlebars.compile('<div><p id="{{elementid}}_{{CSS.STYLESLABEL}}">{{get_string "liststyles" component}}<br/><span aria-labelledby="{{elementid}}_{{CSS.STYLESLABEL}}" /></p></div><span class="listStyles"></span><p id="{{elementid}}_{{CSS.LINKSLABEL}}">{{get_string "listlinks" component}}<br/><span aria-labelledby="{{elementid}}_{{CSS.LINKSLABEL}}"/></p><span class="listLinks"></span><p id="{{elementid}}_{{CSS.IMAGESLABEL}}">{{get_string "listimages" component}}<br/><span aria-labelledby="{{elementid}}_{{CSS.IMAGESLABEL}}"/></p><span class="listImages"></span>'),e=a.Node.create(e({CSS:t,component:n}));return e.one(".listStyles").empty().appendChild(this._listStyles()),e.one(".listLinks").empty().appendChild(this._listLinks()),e.one(".listImages").empty().appendChild(this._listImages()),e},_listStyles:function(){for(var e,t=[],i=(i=this.get("host").getSelectionParentNode())&&a.one(i);i&&i!==this.editor;)void 0!==(e=i.get("tagName"))&&t.push(a.Escape.html(e)),i=i.ancestor();return 0===t.length&&t.push(M.util.get_string("nostyles",n)),t.reverse(),t.join(", ")},_listLinks:function(){var t,i,l=a.Node.create("<ol />");return this.editor.all("a").each(function(e){(i=a.Node.create('<a href="#" title="'+M.util.get_string("selectlink",n)+'">'+a.Escape.html(e.get("text"))+"</a>")).setData("sourcelink",e),i.on("click",this._linkSelected,this),(t=a.Node.create("<li></li>")).appendChild(i),l.appendChild(t)},this),l.hasChildNodes()||l.append("<li>"+M.util.get_string("nolinks",n)+"</li>"),l},_listImages:function(){var i,l,s=a.Node.create("<ol/>");return this.editor.all("img").each(function(e){var t=e.getAttribute("alt");""===t&&""===(t=e.getAttribute("title"))&&(t=e.getAttribute("src")),(l=a.Node.create('<a href="#" title="'+M.util.get_string("selectimage",n)+'">'+a.Escape.html(t)+"</a>")).setData("sourceimage",e),l.on("click",this._imageSelected,this),(i=a.Node.create("<li></li>")).append(l),s.append(i)},this),s.hasChildNodes()||s.append("<li>"+M.util.get_string("noimages",n)+"</li>"),s},_imageSelected:function(e){e.preventDefault(),this.getDialogue({focusAfterNode:null}).hide();var t=this.get("host"),e=e.target.getData("sourceimage");this.editor.focus(),t.setSelection(t.getSelectionFromNode(e))},_linkSelected:function(e){e.preventDefault(),this.getDialogue({focusAfterNode:null}).hide();var t=this.get("host"),e=e.target.getData("sourcelink");this.editor.focus(),t.setSelection(t.getSelectionFromNode(e))}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
@@ -0,0 +1,280 @@
YUI.add('moodle-atto_accessibilityhelper-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_accessibilityhelper
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_accessibilityhelper-button
*/
/**
* Atto text editor accessibilityhelper plugin.
*
* This plugin adds some functions to do things that screen readers do not do well.
* Specifically, listing the active styles for the selected text,
* listing the images in the page, listing the links in the page.
*
*
* @namespace M.atto_accessibilityhelper
* @class Button
* @extends M.editor_atto.EditorPlugin
*/
var COMPONENT = 'atto_accessibilityhelper',
TEMPLATE = '' +
// The list of styles.
'<div><p id="{{elementid}}_{{CSS.STYLESLABEL}}">' +
'{{get_string "liststyles" component}}<br/>' +
'<span aria-labelledby="{{elementid}}_{{CSS.STYLESLABEL}}" />' +
'</p></div>' +
'<span class="listStyles"></span>' +
'<p id="{{elementid}}_{{CSS.LINKSLABEL}}">' +
'{{get_string "listlinks" component}}<br/>' +
'<span aria-labelledby="{{elementid}}_{{CSS.LINKSLABEL}}"/>' +
'</p>' +
'<span class="listLinks"></span>' +
'<p id="{{elementid}}_{{CSS.IMAGESLABEL}}">' +
'{{get_string "listimages" component}}<br/>' +
'<span aria-labelledby="{{elementid}}_{{CSS.IMAGESLABEL}}"/>' +
'</p>' +
'<span class="listImages"></span>',
CSS = {
STYLESLABEL: COMPONENT + '_styleslabel',
LINKSLABEL: COMPONENT + '_linkslabel',
IMAGESLABEL: COMPONENT + '_imageslabel'
};
Y.namespace('M.atto_accessibilityhelper').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addButton({
icon: 'e/screenreader_helper',
callback: this._displayDialogue
});
},
/**
* Display the Accessibility Helper tool.
*
* @method _displayDialogue
* @private
*/
_displayDialogue: function() {
var dialogue = this.getDialogue({
headerContent: M.util.get_string('pluginname', COMPONENT),
width: '800px',
focusAfterHide: true
});
// Set the dialogue content, and then show the dialogue.
dialogue.set('bodyContent', this._getDialogueContent())
.show();
},
/**
* Return the dialogue content for the tool, attaching any required
* events.
*
* @method _getDialogueContent
* @private
* @return {Node} The content to place in the dialogue.
*/
_getDialogueContent: function() {
var template = Y.Handlebars.compile(TEMPLATE),
content = Y.Node.create(template({
CSS: CSS,
component: COMPONENT
}));
// Add the data.
content.one('.listStyles')
.empty()
.appendChild(this._listStyles());
content.one('.listLinks')
.empty()
.appendChild(this._listLinks());
content.one('.listImages')
.empty()
.appendChild(this._listImages());
return content;
},
/**
* List the styles present for the selection.
*
* @method _listStyles
* @return {String} The list of styles in use.
* @private
*/
_listStyles: function() {
// Clear the status node.
var list = [],
host = this.get('host'),
current = host.getSelectionParentNode(),
tagname;
if (current) {
current = Y.one(current);
}
while (current && (current !== this.editor)) {
tagname = current.get('tagName');
if (typeof tagname !== 'undefined') {
list.push(Y.Escape.html(tagname));
}
current = current.ancestor();
}
if (list.length === 0) {
list.push(M.util.get_string('nostyles', COMPONENT));
}
list.reverse();
// Append the list of current styles.
return list.join(', ');
},
/**
* List the links for the current editor
*
* @method _listLinks
* @return {string}
* @private
*/
_listLinks: function() {
var list = Y.Node.create('<ol />'),
listitem,
selectlink;
this.editor.all('a').each(function(link) {
selectlink = Y.Node.create('<a href="#" title="' +
M.util.get_string('selectlink', COMPONENT) + '">' +
Y.Escape.html(link.get('text')) +
'</a>');
selectlink.setData('sourcelink', link);
selectlink.on('click', this._linkSelected, this);
listitem = Y.Node.create('<li></li>');
listitem.appendChild(selectlink);
list.appendChild(listitem);
}, this);
if (!list.hasChildNodes()) {
list.append('<li>' + M.util.get_string('nolinks', COMPONENT) + '</li>');
}
// Append the list of current styles.
return list;
},
/**
* List the images used in the editor.
*
* @method _listImages
* @return {Node} A Node containing all of the images present in the editor.
* @private
*/
_listImages: function() {
var list = Y.Node.create('<ol/>'),
listitem,
selectimage;
this.editor.all('img').each(function(image) {
// Get the alt or title or img url of the image.
var imgalt = image.getAttribute('alt');
if (imgalt === '') {
imgalt = image.getAttribute('title');
if (imgalt === '') {
imgalt = image.getAttribute('src');
}
}
selectimage = Y.Node.create('<a href="#" title="' +
M.util.get_string('selectimage', COMPONENT) + '">' +
Y.Escape.html(imgalt) +
'</a>');
selectimage.setData('sourceimage', image);
selectimage.on('click', this._imageSelected, this);
listitem = Y.Node.create('<li></li>');
listitem.append(selectimage);
list.append(listitem);
}, this);
if (!list.hasChildNodes()) {
list.append('<li>' + M.util.get_string('noimages', COMPONENT) + '</li>');
}
// Append the list of current styles.
return list;
},
/**
* Event handler for selecting an image.
*
* @method _imageSelected
* @param {EventFacade} e
* @private
*/
_imageSelected: function(e) {
e.preventDefault();
this.getDialogue({
focusAfterNode: null
}).hide();
var host = this.get('host'),
target = e.target.getData('sourceimage');
this.editor.focus();
host.setSelection(host.getSelectionFromNode(target));
},
/**
* Event handler for selecting a link.
*
* @method _linkSelected
* @param {EventFacade} e
* @private
*/
_linkSelected: function(e) {
e.preventDefault();
this.getDialogue({
focusAfterNode: null
}).hide();
var host = this.get('host'),
target = e.target.getData('sourcelink');
this.editor.focus();
host.setSelection(host.getSelectionFromNode(target));
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
@@ -0,0 +1,10 @@
{
"name": "moodle-atto_accessibilityhelper-button",
"builds": {
"moodle-atto_accessibilityhelper-button": {
"jsfiles": [
"button.js"
]
}
}
}
@@ -0,0 +1,275 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_accessibilityhelper
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_accessibilityhelper-button
*/
/**
* Atto text editor accessibilityhelper plugin.
*
* This plugin adds some functions to do things that screen readers do not do well.
* Specifically, listing the active styles for the selected text,
* listing the images in the page, listing the links in the page.
*
*
* @namespace M.atto_accessibilityhelper
* @class Button
* @extends M.editor_atto.EditorPlugin
*/
var COMPONENT = 'atto_accessibilityhelper',
TEMPLATE = '' +
// The list of styles.
'<div><p id="{{elementid}}_{{CSS.STYLESLABEL}}">' +
'{{get_string "liststyles" component}}<br/>' +
'<span aria-labelledby="{{elementid}}_{{CSS.STYLESLABEL}}" />' +
'</p></div>' +
'<span class="listStyles"></span>' +
'<p id="{{elementid}}_{{CSS.LINKSLABEL}}">' +
'{{get_string "listlinks" component}}<br/>' +
'<span aria-labelledby="{{elementid}}_{{CSS.LINKSLABEL}}"/>' +
'</p>' +
'<span class="listLinks"></span>' +
'<p id="{{elementid}}_{{CSS.IMAGESLABEL}}">' +
'{{get_string "listimages" component}}<br/>' +
'<span aria-labelledby="{{elementid}}_{{CSS.IMAGESLABEL}}"/>' +
'</p>' +
'<span class="listImages"></span>',
CSS = {
STYLESLABEL: COMPONENT + '_styleslabel',
LINKSLABEL: COMPONENT + '_linkslabel',
IMAGESLABEL: COMPONENT + '_imageslabel'
};
Y.namespace('M.atto_accessibilityhelper').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addButton({
icon: 'e/screenreader_helper',
callback: this._displayDialogue
});
},
/**
* Display the Accessibility Helper tool.
*
* @method _displayDialogue
* @private
*/
_displayDialogue: function() {
var dialogue = this.getDialogue({
headerContent: M.util.get_string('pluginname', COMPONENT),
width: '800px',
focusAfterHide: true
});
// Set the dialogue content, and then show the dialogue.
dialogue.set('bodyContent', this._getDialogueContent())
.show();
},
/**
* Return the dialogue content for the tool, attaching any required
* events.
*
* @method _getDialogueContent
* @private
* @return {Node} The content to place in the dialogue.
*/
_getDialogueContent: function() {
var template = Y.Handlebars.compile(TEMPLATE),
content = Y.Node.create(template({
CSS: CSS,
component: COMPONENT
}));
// Add the data.
content.one('.listStyles')
.empty()
.appendChild(this._listStyles());
content.one('.listLinks')
.empty()
.appendChild(this._listLinks());
content.one('.listImages')
.empty()
.appendChild(this._listImages());
return content;
},
/**
* List the styles present for the selection.
*
* @method _listStyles
* @return {String} The list of styles in use.
* @private
*/
_listStyles: function() {
// Clear the status node.
var list = [],
host = this.get('host'),
current = host.getSelectionParentNode(),
tagname;
if (current) {
current = Y.one(current);
}
while (current && (current !== this.editor)) {
tagname = current.get('tagName');
if (typeof tagname !== 'undefined') {
list.push(Y.Escape.html(tagname));
}
current = current.ancestor();
}
if (list.length === 0) {
list.push(M.util.get_string('nostyles', COMPONENT));
}
list.reverse();
// Append the list of current styles.
return list.join(', ');
},
/**
* List the links for the current editor
*
* @method _listLinks
* @return {string}
* @private
*/
_listLinks: function() {
var list = Y.Node.create('<ol />'),
listitem,
selectlink;
this.editor.all('a').each(function(link) {
selectlink = Y.Node.create('<a href="#" title="' +
M.util.get_string('selectlink', COMPONENT) + '">' +
Y.Escape.html(link.get('text')) +
'</a>');
selectlink.setData('sourcelink', link);
selectlink.on('click', this._linkSelected, this);
listitem = Y.Node.create('<li></li>');
listitem.appendChild(selectlink);
list.appendChild(listitem);
}, this);
if (!list.hasChildNodes()) {
list.append('<li>' + M.util.get_string('nolinks', COMPONENT) + '</li>');
}
// Append the list of current styles.
return list;
},
/**
* List the images used in the editor.
*
* @method _listImages
* @return {Node} A Node containing all of the images present in the editor.
* @private
*/
_listImages: function() {
var list = Y.Node.create('<ol/>'),
listitem,
selectimage;
this.editor.all('img').each(function(image) {
// Get the alt or title or img url of the image.
var imgalt = image.getAttribute('alt');
if (imgalt === '') {
imgalt = image.getAttribute('title');
if (imgalt === '') {
imgalt = image.getAttribute('src');
}
}
selectimage = Y.Node.create('<a href="#" title="' +
M.util.get_string('selectimage', COMPONENT) + '">' +
Y.Escape.html(imgalt) +
'</a>');
selectimage.setData('sourceimage', image);
selectimage.on('click', this._imageSelected, this);
listitem = Y.Node.create('<li></li>');
listitem.append(selectimage);
list.append(listitem);
}, this);
if (!list.hasChildNodes()) {
list.append('<li>' + M.util.get_string('noimages', COMPONENT) + '</li>');
}
// Append the list of current styles.
return list;
},
/**
* Event handler for selecting an image.
*
* @method _imageSelected
* @param {EventFacade} e
* @private
*/
_imageSelected: function(e) {
e.preventDefault();
this.getDialogue({
focusAfterNode: null
}).hide();
var host = this.get('host'),
target = e.target.getData('sourceimage');
this.editor.focus();
host.setSelection(host.getSelectionFromNode(target));
},
/**
* Event handler for selecting a link.
*
* @method _linkSelected
* @param {EventFacade} e
* @private
*/
_linkSelected: function(e) {
e.preventDefault();
this.getDialogue({
focusAfterNode: null
}).hide();
var host = this.get('host'),
target = e.target.getData('sourcelink');
this.editor.focus();
host.setSelection(host.getSelectionFromNode(target));
}
});
@@ -0,0 +1,7 @@
{
"moodle-atto_accessibilityhelper-button": {
"requires": [
"moodle-editor_atto-plugin"
]
}
}
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for block_activity_modules.
*
* @package atto_align
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace atto_align\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for atto_align implementing null_provider.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_align', language 'en'.
*
* @package atto_align
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['center'] = 'Center';
$string['leftalign'] = 'Left align';
$string['pluginname'] = 'Text align';
$string['rightalign'] = 'Right align';
$string['privacy:metadata'] = 'The atto_align plugin does not store any personal data.';
+35
View File
@@ -0,0 +1,35 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor align plugin lib.
*
* @package atto_align
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Initialise the strings required for JS.
*
* @return void
*/
function atto_align_strings_for_js() {
global $PAGE;
$PAGE->requires->strings_for_js(array('center', 'leftalign', 'rightalign'), 'atto_align');
}
@@ -0,0 +1,52 @@
@editor @editor_atto @atto @atto_align
Feature: Atto align text
To format text in Atto, I need to use the align buttons.
@javascript
Scenario: Right align some text
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<p>Fascism</p>"
And I click on "Show more buttons" "button"
And I select the text in the "Description" Atto editor
When I click on "Right align" "button"
And I press "Update profile"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "Plain text area"
And I press "Save changes"
And I click on "Edit profile" "link" in the "region-main" "region"
Then I should see "style=\"text-align:right;\""
@javascript
Scenario: Left align some text
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<p>Communism</p>"
And I click on "Show more buttons" "button"
And I select the text in the "Description" Atto editor
When I click on "Right align" "button"
And I click on "Left align" "button"
And I press "Update profile"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "Plain text area"
And I press "Save changes"
And I click on "Edit profile" "link" in the "region-main" "region"
Then I should see "style=\"text-align:left;\""
@javascript
Scenario: Center align some text
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<p>United Future</p>"
And I click on "Show more buttons" "button"
And I select the text in the "Description" Atto editor
When I click on "Center" "button"
And I press "Update profile"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "Plain text area"
And I press "Save changes"
And I click on "Edit profile" "link" in the "region-main" "region"
Then I should see "style=\"text-align:center;\""
+29
View File
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor align plugin version file.
*
* @package atto_align
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'atto_align'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,106 @@
YUI.add('moodle-atto_align-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_align
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_align-button
*/
/**
* Atto text editor align plugin.
*
* @namespace M.atto_align
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_align').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
var alignment;
alignment = 'justifyLeft';
this.addButton({
icon: 'e/align_left',
title: 'leftalign',
buttonName: alignment,
callback: this._changeStyle,
callbackArgs: alignment
});
alignment = 'justifyCenter';
this.addButton({
icon: 'e/align_center',
title: 'center',
buttonName: alignment,
callback: this._changeStyle,
callbackArgs: alignment
});
alignment = 'justifyRight';
this.addButton({
icon: 'e/align_right',
title: 'rightalign',
buttonName: alignment,
callback: this._changeStyle,
callbackArgs: alignment
});
},
/**
* Change the alignment to the specified justification.
*
* @method _changeStyle
* @param {EventFacade} e
* @param {string} justification The execCommand for the new justification.
* @private
*/
_changeStyle: function(e, justification) {
var host = this.get('host');
// We temporarily re-enable CSS styling to try to have the most consistency.
// Though, IE, as always, is stubborn and will do its own thing...
host.enableCssStyling();
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();
// Mark the text as having been updated.
this.markUpdated();
this.editor.focus();
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
@@ -0,0 +1 @@
YUI.add("moodle-atto_align-button",function(t,e){t.namespace("M.atto_align").Button=t.Base.create("button",t.M.editor_atto.EditorPlugin,[],{initializer:function(){var t="justifyLeft";this.addButton({icon:"e/align_left",title:"leftalign",buttonName:t,callback:this._changeStyle,callbackArgs:t}),this.addButton({icon:"e/align_center",title:"center",buttonName:t="justifyCenter",callback:this._changeStyle,callbackArgs:t}),this.addButton({icon:"e/align_right",title:"rightalign",buttonName:t="justifyRight",callback:this._changeStyle,callbackArgs:t})},_changeStyle:function(t,e){var i=this.get("host");i.enableCssStyling(),document.execCommand(e,!1,null),this.editor.all("*[align]").each(function(t){var e=t.get("align");e&&(t.setStyle("text-align",e),t.removeAttribute("align"))},this),i.disableCssStyling(),this.markUpdated(),this.editor.focus()}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
@@ -0,0 +1,106 @@
YUI.add('moodle-atto_align-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_align
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_align-button
*/
/**
* Atto text editor align plugin.
*
* @namespace M.atto_align
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_align').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
var alignment;
alignment = 'justifyLeft';
this.addButton({
icon: 'e/align_left',
title: 'leftalign',
buttonName: alignment,
callback: this._changeStyle,
callbackArgs: alignment
});
alignment = 'justifyCenter';
this.addButton({
icon: 'e/align_center',
title: 'center',
buttonName: alignment,
callback: this._changeStyle,
callbackArgs: alignment
});
alignment = 'justifyRight';
this.addButton({
icon: 'e/align_right',
title: 'rightalign',
buttonName: alignment,
callback: this._changeStyle,
callbackArgs: alignment
});
},
/**
* Change the alignment to the specified justification.
*
* @method _changeStyle
* @param {EventFacade} e
* @param {string} justification The execCommand for the new justification.
* @private
*/
_changeStyle: function(e, justification) {
var host = this.get('host');
// We temporarily re-enable CSS styling to try to have the most consistency.
// Though, IE, as always, is stubborn and will do its own thing...
host.enableCssStyling();
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();
// Mark the text as having been updated.
this.markUpdated();
this.editor.focus();
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
@@ -0,0 +1,10 @@
{
"name": "moodle-atto_align-button",
"builds": {
"moodle-atto_align-button": {
"jsfiles": [
"button.js"
]
}
}
}
@@ -0,0 +1,101 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_align
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_align-button
*/
/**
* Atto text editor align plugin.
*
* @namespace M.atto_align
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_align').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
var alignment;
alignment = 'justifyLeft';
this.addButton({
icon: 'e/align_left',
title: 'leftalign',
buttonName: alignment,
callback: this._changeStyle,
callbackArgs: alignment
});
alignment = 'justifyCenter';
this.addButton({
icon: 'e/align_center',
title: 'center',
buttonName: alignment,
callback: this._changeStyle,
callbackArgs: alignment
});
alignment = 'justifyRight';
this.addButton({
icon: 'e/align_right',
title: 'rightalign',
buttonName: alignment,
callback: this._changeStyle,
callbackArgs: alignment
});
},
/**
* Change the alignment to the specified justification.
*
* @method _changeStyle
* @param {EventFacade} e
* @param {string} justification The execCommand for the new justification.
* @private
*/
_changeStyle: function(e, justification) {
var host = this.get('host');
// We temporarily re-enable CSS styling to try to have the most consistency.
// Though, IE, as always, is stubborn and will do its own thing...
host.enableCssStyling();
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();
// Mark the text as having been updated.
this.markUpdated();
this.editor.focus();
}
});
@@ -0,0 +1,7 @@
{
"moodle-atto_align-button": {
"requires": [
"moodle-editor_atto-plugin"
]
}
}
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for block_activity_modules.
*
* @package atto_backcolor
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace atto_backcolor\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for atto_backcolor implementing null_provider.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,32 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_backcolor', language 'en'.
*
* @package atto_backcolor
* @copyright 2014 Rossiani Wijaya <rwijaya@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['color_white'] = 'White';
$string['color_red'] = 'Red';
$string['color_yellow'] = 'Yellow';
$string['color_green'] = 'Green';
$string['color_blue'] = 'Blue';
$string['color_black'] = 'Black';
$string['pluginname'] = 'Background colour';
$string['privacy:metadata'] = 'The atto_backcolor plugin does not store any personal data.';
+41
View File
@@ -0,0 +1,41 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_backcolor
* @copyright 2021 Huong Nguyen <huongn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Initialise the js strings required for this module.
*/
function atto_backcolor_strings_for_js() {
global $PAGE;
$PAGE->requires->strings_for_js([
'color_white',
'color_red',
'color_yellow',
'color_green',
'color_blue',
'color_black'
], 'atto_backcolor');
}
@@ -0,0 +1,26 @@
.atto_backcolor_button .dropdown-menu {
min-width: inherit;
}
.atto_backcolor_button .dropdown-menu .atto_menuentry {
padding-top: 5px;
padding-bottom: 5px;
}
.atto_backcolor_button .dropdown-menu .atto_menuentry a[role=menuitem] {
display: flex;
}
.atto_backcolor_button .dropdown-menu .atto_menuentry a[role=menuitem]:hover,
.atto_backcolor_button .dropdown-menu .atto_menuentry a[role=menuitem]:focus {
box-shadow: #0f6fc5 0 0 3px 1px;
}
.atto_backcolor_button .dropdown-menu .coloroption {
display: flex;
align-items: center;
float: left;
height: 20px;
width: 20px;
border: 1px solid #ccc;
}
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_backcolor
* @copyright 2014 Rossiani Wijaya <rwijaya@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'atto_backcolor'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,97 @@
YUI.add('moodle-atto_backcolor-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_backcolor
* @copyright 2014 Rossiani Wijaya <rwijaya@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_backcolor-button
*/
/**
* Atto text editor backcolor plugin.
*
* @namespace M.atto_backcolor
* @class button
* @extends M.editor_atto.EditorPlugin
*/
var colors = [
{
name: 'white',
color: '#FFFFFF'
}, {
name: 'red',
color: '#EF4540'
}, {
name: 'yellow',
color: '#FFCF35'
}, {
name: 'green',
color: '#98CA3E'
}, {
name: 'blue',
color: '#7D9FD3'
}, {
name: 'black',
color: '#333333'
}
];
Y.namespace('M.atto_backcolor').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
var items = [];
Y.Array.each(colors, function(color) {
var colorLabel = M.util.get_string('color_' + color.name, 'atto_backcolor');
items.push({
text: '<div class="coloroption" style="background-color: '
+ color.color + '" aria-label="' + colorLabel + '" title="' + colorLabel + '"></div>',
callbackArgs: color.color
});
});
this.addToolbarMenu({
icon: 'e/text_highlight',
overlayWidth: '4',
globalItemConfig: {
inlineFormat: true,
callback: this._changeStyle
},
items: items
});
},
/**
* Change the background color to the specified color.
*
* @method _changeStyle
* @param {EventFacade} e
* @param {string} color The new background color
* @private
*/
_changeStyle: function(e, color) {
this.get('host').formatSelectionInlineStyle({
backgroundColor: color
});
}
});
}, '@VERSION@');
@@ -0,0 +1 @@
YUI.add("moodle-atto_backcolor-button",function(o,t){var e=[{name:"white",color:"#FFFFFF"},{name:"red",color:"#EF4540"},{name:"yellow",color:"#FFCF35"},{name:"green",color:"#98CA3E"},{name:"blue",color:"#7D9FD3"},{name:"black",color:"#333333"}];o.namespace("M.atto_backcolor").Button=o.Base.create("button",o.M.editor_atto.EditorPlugin,[],{initializer:function(){var a=[];o.Array.each(e,function(o){var t=M.util.get_string("color_"+o.name,"atto_backcolor");a.push({text:'<div class="coloroption" style="background-color: '+o.color+'" aria-label="'+t+'" title="'+t+'"></div>',callbackArgs:o.color})}),this.addToolbarMenu({icon:"e/text_highlight",overlayWidth:"4",globalItemConfig:{inlineFormat:!0,callback:this._changeStyle},items:a})},_changeStyle:function(o,t){this.get("host").formatSelectionInlineStyle({backgroundColor:t})}})},"@VERSION@");
@@ -0,0 +1,97 @@
YUI.add('moodle-atto_backcolor-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_backcolor
* @copyright 2014 Rossiani Wijaya <rwijaya@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_backcolor-button
*/
/**
* Atto text editor backcolor plugin.
*
* @namespace M.atto_backcolor
* @class button
* @extends M.editor_atto.EditorPlugin
*/
var colors = [
{
name: 'white',
color: '#FFFFFF'
}, {
name: 'red',
color: '#EF4540'
}, {
name: 'yellow',
color: '#FFCF35'
}, {
name: 'green',
color: '#98CA3E'
}, {
name: 'blue',
color: '#7D9FD3'
}, {
name: 'black',
color: '#333333'
}
];
Y.namespace('M.atto_backcolor').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
var items = [];
Y.Array.each(colors, function(color) {
var colorLabel = M.util.get_string('color_' + color.name, 'atto_backcolor');
items.push({
text: '<div class="coloroption" style="background-color: '
+ color.color + '" aria-label="' + colorLabel + '" title="' + colorLabel + '"></div>',
callbackArgs: color.color
});
});
this.addToolbarMenu({
icon: 'e/text_highlight',
overlayWidth: '4',
globalItemConfig: {
inlineFormat: true,
callback: this._changeStyle
},
items: items
});
},
/**
* Change the background color to the specified color.
*
* @method _changeStyle
* @param {EventFacade} e
* @param {string} color The new background color
* @private
*/
_changeStyle: function(e, color) {
this.get('host').formatSelectionInlineStyle({
backgroundColor: color
});
}
});
}, '@VERSION@');
@@ -0,0 +1,10 @@
{
"name": "moodle-atto_backcolor-button",
"builds": {
"moodle-atto_backcolor-button": {
"jsfiles": [
"button.js"
]
}
}
}
@@ -0,0 +1,92 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_backcolor
* @copyright 2014 Rossiani Wijaya <rwijaya@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_backcolor-button
*/
/**
* Atto text editor backcolor plugin.
*
* @namespace M.atto_backcolor
* @class button
* @extends M.editor_atto.EditorPlugin
*/
var colors = [
{
name: 'white',
color: '#FFFFFF'
}, {
name: 'red',
color: '#EF4540'
}, {
name: 'yellow',
color: '#FFCF35'
}, {
name: 'green',
color: '#98CA3E'
}, {
name: 'blue',
color: '#7D9FD3'
}, {
name: 'black',
color: '#333333'
}
];
Y.namespace('M.atto_backcolor').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
var items = [];
Y.Array.each(colors, function(color) {
var colorLabel = M.util.get_string('color_' + color.name, 'atto_backcolor');
items.push({
text: '<div class="coloroption" style="background-color: '
+ color.color + '" aria-label="' + colorLabel + '" title="' + colorLabel + '"></div>',
callbackArgs: color.color
});
});
this.addToolbarMenu({
icon: 'e/text_highlight',
overlayWidth: '4',
globalItemConfig: {
inlineFormat: true,
callback: this._changeStyle
},
items: items
});
},
/**
* Change the background color to the specified color.
*
* @method _changeStyle
* @param {EventFacade} e
* @param {string} color The new background color
* @private
*/
_changeStyle: function(e, color) {
this.get('host').formatSelectionInlineStyle({
backgroundColor: color
});
}
});
@@ -0,0 +1,5 @@
{
"moodle-atto_backcolor-button": {
"requires": ["node"]
}
}
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for block_activity_modules.
*
* @package atto_bold
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace atto_bold\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for atto_bold implementing null_provider.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,26 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_bold', language 'en'.
*
* @package atto_bold
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Bold';
$string['privacy:metadata'] = 'The atto_bold plugin does not store any personal data.';
@@ -0,0 +1,35 @@
@editor @editor_atto @atto @atto_bold @_bug_phantomjs
Feature: Atto bold button
To format text in Atto, I need to use the bold button.
@javascript
Scenario: Bold some text
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "Badger"
And I select the text in the "Description" Atto editor
When I click on "Bold" "button"
And I press "Update profile"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "Plain text area"
And I press "Save changes"
And I click on "Edit profile" "link" in the "region-main" "region"
Then I should see "<strong>Badger</strong>"
@javascript
Scenario: Unbold some text
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "Mouse"
And I select the text in the "Description" Atto editor
When I click on "Bold" "button"
And I click on "Bold" "button"
And I press "Update profile"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "Plain text area"
And I press "Save changes"
And I click on "Edit profile" "link" in the "region-main" "region"
Then I should not see "<strong>Mouse</strong>"
And I should see "Mouse"
+29
View File
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_bold
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'atto_bold'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,72 @@
YUI.add('moodle-atto_bold-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_bold
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_bold-button
*/
/**
* Atto text editor bold plugin.
*
* @namespace M.atto_bold
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_bold').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
var bold;
this.addButton({
callback: this._toggleBold,
icon: 'e/bold',
buttonName: bold,
inlineFormat: true,
// Key code for the keyboard shortcut which triggers this button:
keys: '66',
// Watch the following tags and add/remove highlighting as appropriate:
tags: 'strong, b'
});
},
/**
* Toggle the bold setting.
*
* @method _toggleBold
* @param {EventFacade} e
*/
_toggleBold: function() {
var host = this.get('host');
// Use the "bold" command for simplicity. This will toggle <strong> tags off as well.
document.execCommand('bold', false, null);
// Then change all <b> tags to <strong> tags. This will change any existing <b> tags as well.
host.changeToCSS('b', 'bf-editor-bold-strong');
host.changeToTags('bf-editor-bold-strong', 'strong');
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
@@ -0,0 +1 @@
YUI.add("moodle-atto_bold-button",function(o,t){o.namespace("M.atto_bold").Button=o.Base.create("button",o.M.editor_atto.EditorPlugin,[],{initializer:function(){this.addButton({callback:this._toggleBold,icon:"e/bold",buttonName:void 0,inlineFormat:!0,keys:"66",tags:"strong, b"})},_toggleBold:function(){var o=this.get("host");document.execCommand("bold",!1,null),o.changeToCSS("b","bf-editor-bold-strong"),o.changeToTags("bf-editor-bold-strong","strong")}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
@@ -0,0 +1,72 @@
YUI.add('moodle-atto_bold-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_bold
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_bold-button
*/
/**
* Atto text editor bold plugin.
*
* @namespace M.atto_bold
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_bold').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
var bold;
this.addButton({
callback: this._toggleBold,
icon: 'e/bold',
buttonName: bold,
inlineFormat: true,
// Key code for the keyboard shortcut which triggers this button:
keys: '66',
// Watch the following tags and add/remove highlighting as appropriate:
tags: 'strong, b'
});
},
/**
* Toggle the bold setting.
*
* @method _toggleBold
* @param {EventFacade} e
*/
_toggleBold: function() {
var host = this.get('host');
// Use the "bold" command for simplicity. This will toggle <strong> tags off as well.
document.execCommand('bold', false, null);
// Then change all <b> tags to <strong> tags. This will change any existing <b> tags as well.
host.changeToCSS('b', 'bf-editor-bold-strong');
host.changeToTags('bf-editor-bold-strong', 'strong');
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
@@ -0,0 +1,10 @@
{
"name": "moodle-atto_bold-button",
"builds": {
"moodle-atto_bold-button": {
"jsfiles": [
"button.js"
]
}
}
}
@@ -0,0 +1,67 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_bold
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_bold-button
*/
/**
* Atto text editor bold plugin.
*
* @namespace M.atto_bold
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_bold').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
var bold;
this.addButton({
callback: this._toggleBold,
icon: 'e/bold',
buttonName: bold,
inlineFormat: true,
// Key code for the keyboard shortcut which triggers this button:
keys: '66',
// Watch the following tags and add/remove highlighting as appropriate:
tags: 'strong, b'
});
},
/**
* Toggle the bold setting.
*
* @method _toggleBold
* @param {EventFacade} e
*/
_toggleBold: function() {
var host = this.get('host');
// Use the "bold" command for simplicity. This will toggle <strong> tags off as well.
document.execCommand('bold', false, null);
// Then change all <b> tags to <strong> tags. This will change any existing <b> tags as well.
host.changeToCSS('b', 'bf-editor-bold-strong');
host.changeToTags('bf-editor-bold-strong', 'strong');
}
});
@@ -0,0 +1,7 @@
{
"moodle-atto_bold-button": {
"requires": [
"moodle-editor_atto-plugin"
]
}
}
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for block_activity_modules.
*
* @package atto_charmap
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace atto_charmap\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for atto_charmap implementing null_provider.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,293 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_charmap', language 'en'.
*
* @package atto_charmap
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['insertcharacter'] = 'Insert character';
$string['pluginname'] = 'Insert character';
// The following are not ordered on purpose, they are kept
// in the order in which they appear in the user interface.
$string['interrobang'] = 'interrobang';
$string['nobreakspace'] = 'no-break space';
$string['ampersand'] = 'ampersand';
$string['quotationmark'] = 'quotation mark';
$string['centsign'] = 'cent sign';
$string['eurosign'] = 'euro sign';
$string['poundsign'] = 'pound sign';
$string['yensign'] = 'yen sign';
$string['copyrightsign'] = 'copyright sign';
$string['registeredsign'] = 'registered sign';
$string['trademarksign'] = 'trade mark sign';
$string['permillesign'] = 'per mille sign';
$string['microsign'] = 'micro sign';
$string['middledot'] = 'middle dot';
$string['bullet'] = 'bullet';
$string['threedotleader'] = 'three dot leader';
$string['minutesfeet'] = 'minutes / feet';
$string['secondsinches'] = 'seconds / inches';
$string['sectionsign'] = 'section sign';
$string['paragraphsign'] = 'paragraph sign';
$string['sharpsesszed'] = 'sharp s / ess-zed';
$string['singleleftpointinganglequotationmark'] = 'single left-pointing angle quotation mark';
$string['singlerightpointinganglequotationmark'] = 'single right-pointing angle quotation mark';
$string['leftpointingguillemet'] = 'left pointing guillemet';
$string['rightpointingguillemet'] = 'right pointing guillemet';
$string['leftsinglequotationmark'] = 'left single quotation mark';
$string['rightsinglequotationmark'] = 'right single quotation mark';
$string['leftdoublequotationmark'] = 'left double quotation mark';
$string['rightdoublequotationmark'] = 'right double quotation mark';
$string['singlelow9quotationmark'] = 'single low-9 quotation mark';
$string['doublelow9quotationmark'] = 'double low-9 quotation mark';
$string['lessthansign'] = 'less-than sign';
$string['greaterthansign'] = 'greater-than sign';
$string['lessthanorequalto'] = 'less-than or equal to';
$string['greaterthanorequalto'] = 'greater-than or equal to';
$string['endash'] = 'en dash';
$string['emdash'] = 'em dash';
$string['macron'] = 'macron';
$string['overline'] = 'overline';
$string['currencysign'] = 'currency sign';
$string['brokenbar'] = 'broken bar';
$string['diaeresis'] = 'diaeresis';
$string['invertedexclamationmark'] = 'inverted exclamation mark';
$string['turnedquestionmark'] = 'turned question mark';
$string['circumflexaccent'] = 'circumflex accent';
$string['smalltilde'] = 'small tilde';
$string['degreesign'] = 'degree sign';
$string['minussign'] = 'minus sign';
$string['plusminussign'] = 'plus-minus sign';
$string['divisionsign'] = 'division sign';
$string['fractionslash'] = 'fraction slash';
$string['multiplicationsign'] = 'multiplication sign';
$string['superscriptone'] = 'superscript one';
$string['superscripttwo'] = 'superscript two';
$string['superscriptthree'] = 'superscript three';
$string['fractiononequarter'] = 'fraction one quarter';
$string['fractiononehalf'] = 'fraction one half';
$string['fractionthreequarters'] = 'fraction three quarters';
$string['functionflorin'] = 'function / florin';
$string['integral'] = 'integral';
$string['narysumation'] = 'n-ary sumation';
$string['infinity'] = 'infinity';
$string['squareroot'] = 'square root';
$string['similarto'] = 'similar to';
$string['approximatelyequalto'] = 'approximately equal to';
$string['almostequalto'] = 'almost equal to';
$string['notequalto'] = 'not equal to';
$string['identicalto'] = 'identical to';
$string['elementof'] = 'element of';
$string['notanelementof'] = 'not an element of';
$string['containsasmember'] = 'contains as member';
$string['naryproduct'] = 'n-ary product';
$string['logicaland'] = 'logical and';
$string['logicalor'] = 'logical or';
$string['notsign'] = 'not sign';
$string['intersection'] = 'intersection';
$string['union'] = 'union';
$string['partialdifferential'] = 'partial differential';
$string['forall'] = 'for all';
$string['thereexists'] = 'there exists';
$string['diameter'] = 'diameter';
$string['backwarddifference'] = 'backward difference';
$string['asteriskoperator'] = 'asterisk operator';
$string['proportionalto'] = 'proportional to';
$string['angle'] = 'angle';
$string['acuteaccent'] = 'acute accent';
$string['cedilla'] = 'cedilla';
$string['feminineordinalindicator'] = 'feminine ordinal indicator';
$string['masculineordinalindicator'] = 'masculine ordinal indicator';
$string['dagger'] = 'dagger';
$string['doubledagger'] = 'double dagger';
$string['agrave_caps'] = 'A - grave';
$string['aacute_caps'] = 'A - acute';
$string['acircumflex_caps'] = 'A - circumflex';
$string['atilde_caps'] = 'A - tilde';
$string['adiaeresis_caps'] = 'A - diaeresis';
$string['aringabove_caps'] = 'A - ring above';
$string['amacron_caps'] = 'A - macron';
$string['ligatureae_caps'] = 'ligature AE';
$string['ccedilla_caps'] = 'C - cedilla';
$string['egrave_caps'] = 'E - grave';
$string['eacute_caps'] = 'E - acute';
$string['ecircumflex_caps'] = 'E - circumflex';
$string['ediaeresis_caps'] = 'E - diaeresis';
$string['emacron_caps'] = 'E - macron';
$string['igrave_caps'] = 'I - grave';
$string['iacute_caps'] = 'I - acute';
$string['icircumflex_caps'] = 'I - circumflex';
$string['idiaeresis_caps'] = 'I - diaeresis';
$string['imacron_caps'] = 'I - macron';
$string['eth_caps'] = 'ETH';
$string['ntilde_caps'] = 'N - tilde';
$string['ograve_caps'] = 'O - grave';
$string['oacute_caps'] = 'O - acute';
$string['ocircumflex_caps'] = 'O - circumflex';
$string['otilde_caps'] = 'O - tilde';
$string['odiaeresis_caps'] = 'O - diaeresis';
$string['oslash_caps'] = 'O - slash';
$string['omacron_caps'] = 'O - macron';
$string['ligatureoe_caps'] = 'ligature OE';
$string['scaron_caps'] = 'S - caron';
$string['ugrave_caps'] = 'U - grave';
$string['uacute_caps'] = 'U - acute';
$string['ucircumflex_caps'] = 'U - circumflex';
$string['udiaeresis_caps'] = 'U - diaeresis';
$string['umacron_caps'] = 'U - macron';
$string['yacute_caps'] = 'Y - acute';
$string['ydiaeresis_caps'] = 'Y - diaeresis';
$string['thorn_caps'] = 'THORN';
$string['agrave'] = 'a - grave';
$string['aacute'] = 'a - acute';
$string['acircumflex'] = 'a - circumflex';
$string['atilde'] = 'a - tilde';
$string['adiaeresis'] = 'a - diaeresis';
$string['aringabove'] = 'a - ring above';
$string['amacron'] = 'a - macron';
$string['ligatureae'] = 'ligature ae';
$string['ccedilla'] = 'c - cedilla';
$string['egrave'] = 'e - grave';
$string['eacute'] = 'e - acute';
$string['ecircumflex'] = 'e - circumflex';
$string['ediaeresis'] = 'e - diaeresis';
$string['emacron'] = 'e - macron';
$string['igrave'] = 'i - grave';
$string['iacute'] = 'i - acute';
$string['icircumflex'] = 'i - circumflex';
$string['idiaeresis'] = 'i - diaeresis';
$string['imacron'] = 'i - macron';
$string['eth'] = 'eth';
$string['ntilde'] = 'n - tilde';
$string['ograve'] = 'o - grave';
$string['oacute'] = 'o - acute';
$string['ocircumflex'] = 'o - circumflex';
$string['otilde'] = 'o - tilde';
$string['odiaeresis'] = 'o - diaeresis';
$string['oslash'] = 'o slash';
$string['omacron'] = 'o - macron';
$string['ligatureoe'] = 'ligature oe';
$string['scaron'] = 's - caron';
$string['ugrave'] = 'u - grave';
$string['uacute'] = 'u - acute';
$string['ucircumflex'] = 'u - circumflex';
$string['udiaeresis'] = 'u - diaeresis';
$string['umacron'] = 'u - macron';
$string['yacute'] = 'y - acute';
$string['thorn'] = 'thorn';
$string['ydiaeresis'] = 'y - diaeresis';
$string['alpha_caps'] = 'Alpha';
$string['beta_caps'] = 'Beta';
$string['gamma_caps'] = 'Gamma';
$string['delta_caps'] = 'Delta';
$string['epsilon_caps'] = 'Epsilon';
$string['zeta_caps'] = 'Zeta';
$string['eta_caps'] = 'Eta';
$string['theta_caps'] = 'Theta';
$string['iota_caps'] = 'Iota';
$string['kappa_caps'] = 'Kappa';
$string['lambda_caps'] = 'Lambda';
$string['mu_caps'] = 'Mu';
$string['nu_caps'] = 'Nu';
$string['xi_caps'] = 'Xi';
$string['omicron_caps'] = 'Omicron';
$string['pi_caps'] = 'Pi';
$string['rho_caps'] = 'Rho';
$string['sigma_caps'] = 'Sigma';
$string['tau_caps'] = 'Tau';
$string['upsilon_caps'] = 'Upsilon';
$string['phi_caps'] = 'Phi';
$string['chi_caps'] = 'Chi';
$string['psi_caps'] = 'Psi';
$string['omega_caps'] = 'Omega';
$string['alpha'] = 'alpha';
$string['beta'] = 'beta';
$string['gamma'] = 'gamma';
$string['delta'] = 'delta';
$string['epsilon'] = 'epsilon';
$string['zeta'] = 'zeta';
$string['eta'] = 'eta';
$string['theta'] = 'theta';
$string['iota'] = 'iota';
$string['kappa'] = 'kappa';
$string['lambda'] = 'lambda';
$string['mu'] = 'mu';
$string['nu'] = 'nu';
$string['xi'] = 'xi';
$string['omicron'] = 'omicron';
$string['pi'] = 'pi';
$string['rho'] = 'rho';
$string['finalsigma'] = 'final sigma';
$string['sigma'] = 'sigma';
$string['tau'] = 'tau';
$string['upsilon'] = 'upsilon';
$string['phi'] = 'phi';
$string['chi'] = 'chi';
$string['psi'] = 'psi';
$string['omega'] = 'omega';
$string['alefsymbol'] = 'alef symbol';
$string['pisymbol'] = 'pi symbol';
$string['realpartsymbol'] = 'real part symbol';
$string['thetasymbol'] = 'theta symbol';
$string['upsilonhooksymbol'] = 'upsilon - hook symbol';
$string['weierstrassp'] = 'Weierstrass p';
$string['imaginarypart'] = 'imaginary part';
$string['leftwardsarrow'] = 'leftwards arrow';
$string['upwardsarrow'] = 'upwards arrow';
$string['rightwardsarrow'] = 'rightwards arrow';
$string['downwardsarrow'] = 'downwards arrow';
$string['leftrightarrow'] = 'left right arrow';
$string['carriagereturn'] = 'carriage return';
$string['leftwardsdoublearrow'] = 'leftwards double arrow';
$string['upwardsdoublearrow'] = 'upwards double arrow';
$string['rightwardsdoublearrow'] = 'rightwards double arrow';
$string['downwardsdoublearrow'] = 'downwards double arrow';
$string['leftrightdoublearrow'] = 'left right double arrow';
$string['therefore'] = 'therefore';
$string['subsetof'] = 'subset of';
$string['supersetof'] = 'superset of';
$string['notasubsetof'] = 'not a subset of';
$string['subsetoforequalto'] = 'subset of or equal to';
$string['supersetoforequalto'] = 'superset of or equal to';
$string['circledplus'] = 'circled plus';
$string['circledtimes'] = 'circled times';
$string['perpendicular'] = 'perpendicular';
$string['dotoperator'] = 'dot operator';
$string['leftceiling'] = 'left ceiling';
$string['rightceiling'] = 'right ceiling';
$string['leftfloor'] = 'left floor';
$string['rightfloor'] = 'right floor';
$string['leftpointinganglebracket'] = 'left-pointing angle bracket';
$string['rightpointinganglebracket'] = 'right-pointing angle bracket';
$string['lozenge'] = 'lozenge';
$string['blackspadesuit'] = 'black spade suit';
$string['blackclubsuit'] = 'black club suit';
$string['blackheartsuit'] = 'black heart suit';
$string['blackdiamondsuit'] = 'black diamond suit';
$string['enspace'] = 'en space';
$string['emspace'] = 'em space';
$string['thinspace'] = 'thin space';
$string['zerowidthnonjoiner'] = 'zero width non-joiner';
$string['zerowidthjoiner'] = 'zero width joiner';
$string['lefttorightmark'] = 'left-to-right mark';
$string['righttoleftmark'] = 'right-to-left mark';
$string['softhyphen'] = 'soft hyphen';
$string['privacy:metadata'] = 'The atto_charmap plugin does not store any personal data.';
+306
View File
@@ -0,0 +1,306 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor charmap plugin lib.
*
* @package atto_charmap
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Initialise the strings required for JS.
*
* @return void
*/
function atto_charmap_strings_for_js() {
global $PAGE;
// In order to prevent extra strings to be imported, comment/uncomment the characters
// which are enabled in the JavaScript part of this plugin.
$PAGE->requires->strings_for_js(
array(
'amacron',
'emacron',
'imacron',
'omacron',
'umacron',
'amacron_caps',
'emacron_caps',
'imacron_caps',
'omacron_caps',
'umacron_caps',
'interrobang',
'insertcharacter',
'nobreakspace',
'ampersand',
'quotationmark',
'centsign',
'eurosign',
'poundsign',
'yensign',
'copyrightsign',
'registeredsign',
'trademarksign',
'permillesign',
'microsign',
'middledot',
'bullet',
'threedotleader',
'minutesfeet',
'secondsinches',
'sectionsign',
'paragraphsign',
'sharpsesszed',
'singleleftpointinganglequotationmark',
'singlerightpointinganglequotationmark',
'leftpointingguillemet',
'rightpointingguillemet',
'leftsinglequotationmark',
'rightsinglequotationmark',
'leftdoublequotationmark',
'rightdoublequotationmark',
'singlelow9quotationmark',
'doublelow9quotationmark',
'lessthansign',
'greaterthansign',
'lessthanorequalto',
'greaterthanorequalto',
'endash',
'emdash',
'macron',
'overline',
'currencysign',
'brokenbar',
'diaeresis',
'invertedexclamationmark',
'turnedquestionmark',
'circumflexaccent',
'smalltilde',
'degreesign',
'minussign',
'plusminussign',
'divisionsign',
'fractionslash',
'multiplicationsign',
'superscriptone',
'superscripttwo',
'superscriptthree',
'fractiononequarter',
'fractiononehalf',
'fractionthreequarters',
'functionflorin',
'integral',
'narysumation',
'infinity',
'squareroot',
// 'similarto',
// 'approximatelyequalto',
'almostequalto',
'notequalto',
'identicalto',
// 'elementof',
// 'notanelementof',
// 'containsasmember',
'naryproduct',
// 'logicaland',
// 'logicalor',
'notsign',
'intersection',
// 'union',
'partialdifferential',
// 'forall',
// 'thereexists',
// 'diameter',
// 'backwarddifference',
// 'asteriskoperator',
// 'proportionalto',
// 'angle',
'acuteaccent',
'cedilla',
'feminineordinalindicator',
'masculineordinalindicator',
'dagger',
'doubledagger',
'agrave_caps',
'aacute_caps',
'acircumflex_caps',
'atilde_caps',
'adiaeresis_caps',
'aringabove_caps',
'ligatureae_caps',
'ccedilla_caps',
'egrave_caps',
'eacute_caps',
'ecircumflex_caps',
'ediaeresis_caps',
'igrave_caps',
'iacute_caps',
'icircumflex_caps',
'idiaeresis_caps',
'eth_caps',
'ntilde_caps',
'ograve_caps',
'oacute_caps',
'ocircumflex_caps',
'otilde_caps',
'odiaeresis_caps',
'oslash_caps',
'ligatureoe_caps',
'scaron_caps',
'ugrave_caps',
'uacute_caps',
'ucircumflex_caps',
'udiaeresis_caps',
'yacute_caps',
'ydiaeresis_caps',
'thorn_caps',
'agrave',
'aacute',
'acircumflex',
'atilde',
'adiaeresis',
'aringabove',
'ligatureae',
'ccedilla',
'egrave',
'eacute',
'ecircumflex',
'ediaeresis',
'igrave',
'iacute',
'icircumflex',
'idiaeresis',
'eth',
'ntilde',
'ograve',
'oacute',
'ocircumflex',
'otilde',
'odiaeresis',
'oslash',
'ligatureoe',
'scaron',
'ugrave',
'uacute',
'ucircumflex',
'udiaeresis',
'yacute',
'thorn',
'ydiaeresis',
'alpha_caps',
'beta_caps',
'gamma_caps',
'delta_caps',
'epsilon_caps',
'zeta_caps',
'eta_caps',
'theta_caps',
'iota_caps',
'kappa_caps',
'lambda_caps',
'mu_caps',
'nu_caps',
'xi_caps',
'omicron_caps',
'pi_caps',
'rho_caps',
'sigma_caps',
'tau_caps',
'upsilon_caps',
'phi_caps',
'chi_caps',
'psi_caps',
'omega_caps',
'alpha',
'beta',
'gamma',
'delta',
'epsilon',
'zeta',
'eta',
'theta',
'iota',
'kappa',
'lambda',
'mu',
'nu',
'xi',
'omicron',
'pi',
'rho',
'finalsigma',
'sigma',
'tau',
'upsilon',
'phi',
'chi',
'psi',
'omega',
// 'alefsymbol',
// 'pisymbol',
// 'realpartsymbol',
// 'thetasymbol',
// 'upsilonhooksymbol',
// 'weierstrassp',
// 'imaginarypart',
'leftwardsarrow',
'upwardsarrow',
'rightwardsarrow',
'downwardsarrow',
'leftrightarrow',
// 'carriagereturn',
// 'leftwardsdoublearrow',
// 'upwardsdoublearrow',
// 'rightwardsdoublearrow',
// 'downwardsdoublearrow',
// 'leftrightdoublearrow',
// 'therefore',
// 'subsetof',
// 'supersetof',
// 'notasubsetof',
// 'subsetoforequalto',
// 'supersetoforequalto',
// 'circledplus',
// 'circledtimes',
// 'perpendicular',
// 'dotoperator',
// 'leftceiling',
// 'rightceiling',
// 'leftfloor',
// 'rightfloor',
// 'leftpointinganglebracket',
// 'rightpointinganglebracket',
'lozenge',
'blackspadesuit',
'blackclubsuit',
'blackheartsuit',
'blackdiamondsuit',
// 'enspace',
// 'emspace',
// 'thinspace',
// 'zerowidthnonjoiner',
// 'zerowidthjoiner',
// 'lefttorightmark',
// 'righttoleftmark',
// 'softhyphen',
),
'atto_charmap'
);
}
@@ -0,0 +1,4 @@
.atto_charmap_selector button {
width: 2.18rem;
margin: 0.1rem;
}
@@ -0,0 +1,20 @@
@editor @editor_atto @atto @atto_charmap
Feature: Atto charmap button
To format text in Atto, I need to add symbols
@javascript
Scenario: Insert symbols
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "<p>1980 Mullet</p>"
And I select the text in the "Description" Atto editor
When I click on "Show more buttons" "button"
And I click on "Insert character" "button"
And I click on "a - macron" "button"
And I press "Update profile"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "Plain text area"
And I press "Save changes"
And I click on "Edit profile" "link" in the "region-main" "region"
Then I should see "ā"
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor charmap plugin version file.
*
* @package atto_charmap
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'atto_charmap'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,431 @@
YUI.add('moodle-atto_charmap-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_charmap
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Atto text editor character map plugin
*
* @module moodle-atto_charmap-button
*/
var COMPONENTNAME = 'atto_charmap',
CSS = {
BUTTON: 'atto_charmap_character',
CHARMAP: 'atto_charmap_selector'
},
/*
* Map of special characters, kindly borrowed from TinyMCE.
*
* Each entries contains in order:
* - {String} HTML code
* - {String} HTML numerical code
* - {Boolean} Whether or not to include it in the list
* - {String} The language string key
*
* @property CHARMAP
* @type {Array}
*/
CHARMAP = [
['&nbsp;', '&#160;', true, 'nobreakspace'],
['&amp;', '&#38;', true, 'ampersand'],
['&quot;', '&#34;', true, 'quotationmark'],
['&#8253;', '&#8253;', true, 'interrobang'],
// Finance.
['&cent;', '&#162;', true, 'centsign'],
['&euro;', '&#8364;', true, 'eurosign'],
['&pound;', '&#163;', true, 'poundsign'],
['&yen;', '&#165;', true, 'yensign'],
// Signs.
['&copy;', '&#169;', true, 'copyrightsign'],
['&reg;', '&#174;', true, 'registeredsign'],
['&trade;', '&#8482;', true, 'trademarksign'],
['&permil;', '&#8240;', true, 'permillesign'],
['&micro;', '&#181;', true, 'microsign'],
['&middot;', '&#183;', true, 'middledot'],
['&bull;', '&#8226;', true, 'bullet'],
['&hellip;', '&#8230;', true, 'threedotleader'],
['&prime;', '&#8242;', true, 'minutesfeet'],
['&Prime;', '&#8243;', true, 'secondsinches'],
['&sect;', '&#167;', true, 'sectionsign'],
['&para;', '&#182;', true, 'paragraphsign'],
['&szlig;', '&#223;', true, 'sharpsesszed'],
// Quotations.
['&lsaquo;', '&#8249;', true, 'singleleftpointinganglequotationmark'],
['&rsaquo;', '&#8250;', true, 'singlerightpointinganglequotationmark'],
['&laquo;', '&#171;', true, 'leftpointingguillemet'],
['&raquo;', '&#187;', true, 'rightpointingguillemet'],
['&lsquo;', '&#8216;', true, 'leftsinglequotationmark'],
['&rsquo;', '&#8217;', true, 'rightsinglequotationmark'],
['&ldquo;', '&#8220;', true, 'leftdoublequotationmark'],
['&rdquo;', '&#8221;', true, 'rightdoublequotationmark'],
['&sbquo;', '&#8218;', true, 'singlelow9quotationmark'],
['&bdquo;', '&#8222;', true, 'doublelow9quotationmark'],
['&lt;', '&#60;', true, 'lessthansign'],
['&gt;', '&#62;', true, 'greaterthansign'],
['&le;', '&#8804;', true, 'lessthanorequalto'],
['&ge;', '&#8805;', true, 'greaterthanorequalto'],
['&ndash;', '&#8211;', true, 'endash'],
['&mdash;', '&#8212;', true, 'emdash'],
['&macr;', '&#175;', true, 'macron'],
['&oline;', '&#8254;', true, 'overline'],
['&curren;', '&#164;', true, 'currencysign'],
['&brvbar;', '&#166;', true, 'brokenbar'],
['&uml;', '&#168;', true, 'diaeresis'],
['&iexcl;', '&#161;', true, 'invertedexclamationmark'],
['&iquest;', '&#191;', true, 'turnedquestionmark'],
['&circ;', '&#710;', true, 'circumflexaccent'],
['&tilde;', '&#732;', true, 'smalltilde'],
['&deg;', '&#176;', true, 'degreesign'],
['&minus;', '&#8722;', true, 'minussign'],
['&plusmn;', '&#177;', true, 'plusminussign'],
['&divide;', '&#247;', true, 'divisionsign'],
['&frasl;', '&#8260;', true, 'fractionslash'],
['&times;', '&#215;', true, 'multiplicationsign'],
['&sup1;', '&#185;', true, 'superscriptone'],
['&sup2;', '&#178;', true, 'superscripttwo'],
['&sup3;', '&#179;', true, 'superscriptthree'],
['&frac14;', '&#188;', true, 'fractiononequarter'],
['&frac12;', '&#189;', true, 'fractiononehalf'],
['&frac34;', '&#190;', true, 'fractionthreequarters'],
// Math / logical.
['&fnof;', '&#402;', true, 'functionflorin'],
['&int;', '&#8747;', true, 'integral'],
['&sum;', '&#8721;', true, 'narysumation'],
['&infin;', '&#8734;', true, 'infinity'],
['&radic;', '&#8730;', true, 'squareroot'],
['&sim;', '&#8764;', false, 'similarto'],
['&cong;', '&#8773;', false, 'approximatelyequalto'],
['&asymp;', '&#8776;', true, 'almostequalto'],
['&ne;', '&#8800;', true, 'notequalto'],
['&equiv;', '&#8801;', true, 'identicalto'],
['&isin;', '&#8712;', false, 'elementof'],
['&notin;', '&#8713;', false, 'notanelementof'],
['&ni;', '&#8715;', false, 'containsasmember'],
['&prod;', '&#8719;', true, 'naryproduct'],
['&and;', '&#8743;', false, 'logicaland'],
['&or;', '&#8744;', false, 'logicalor'],
['&not;', '&#172;', true, 'notsign'],
['&cap;', '&#8745;', true, 'intersection'],
['&cup;', '&#8746;', false, 'union'],
['&part;', '&#8706;', true, 'partialdifferential'],
['&forall;', '&#8704;', false, 'forall'],
['&exist;', '&#8707;', false, 'thereexists'],
['&empty;', '&#8709;', false, 'diameter'],
['&nabla;', '&#8711;', false, 'backwarddifference'],
['&lowast;', '&#8727;', false, 'asteriskoperator'],
['&prop;', '&#8733;', false, 'proportionalto'],
['&ang;', '&#8736;', false, 'angle'],
// Undefined.
['&acute;', '&#180;', true, 'acuteaccent'],
['&cedil;', '&#184;', true, 'cedilla'],
['&ordf;', '&#170;', true, 'feminineordinalindicator'],
['&ordm;', '&#186;', true, 'masculineordinalindicator'],
['&dagger;', '&#8224;', true, 'dagger'],
['&Dagger;', '&#8225;', true, 'doubledagger'],
// Alphabetical special chars.
['&Agrave;', '&#192;', true, 'agrave_caps'],
['&Aacute;', '&#193;', true, 'aacute_caps'],
['&Acirc;', '&#194;', true, 'acircumflex_caps'],
['&Atilde;', '&#195;', true, 'atilde_caps'],
['&Auml;', '&#196;', true, 'adiaeresis_caps'],
['&Aring;', '&#197;', true, 'aringabove_caps'],
['&#256;', '&#256;', true, 'amacron_caps'],
['&AElig;', '&#198;', true, 'ligatureae_caps'],
['&Ccedil;', '&#199;', true, 'ccedilla_caps'],
['&Egrave;', '&#200;', true, 'egrave_caps'],
['&Eacute;', '&#201;', true, 'eacute_caps'],
['&Ecirc;', '&#202;', true, 'ecircumflex_caps'],
['&Euml;', '&#203;', true, 'ediaeresis_caps'],
['&#274;', '&#274;', true, 'emacron_caps'],
['&Igrave;', '&#204;', true, 'igrave_caps'],
['&Iacute;', '&#205;', true, 'iacute_caps'],
['&Icirc;', '&#206;', true, 'icircumflex_caps'],
['&Iuml;', '&#207;', true, 'idiaeresis_caps'],
['&#298;', '&#298;', true, 'imacron_caps'],
['&ETH;', '&#208;', true, 'eth_caps'],
['&Ntilde;', '&#209;', true, 'ntilde_caps'],
['&Ograve;', '&#210;', true, 'ograve_caps'],
['&Oacute;', '&#211;', true, 'oacute_caps'],
['&Ocirc;', '&#212;', true, 'ocircumflex_caps'],
['&Otilde;', '&#213;', true, 'otilde_caps'],
['&Ouml;', '&#214;', true, 'odiaeresis_caps'],
['&Oslash;', '&#216;', true, 'oslash_caps'],
['&#332;', '&#332;', true, 'omacron_caps'],
['&OElig;', '&#338;', true, 'ligatureoe_caps'],
['&Scaron;', '&#352;', true, 'scaron_caps'],
['&Ugrave;', '&#217;', true, 'ugrave_caps'],
['&Uacute;', '&#218;', true, 'uacute_caps'],
['&Ucirc;', '&#219;', true, 'ucircumflex_caps'],
['&Uuml;', '&#220;', true, 'udiaeresis_caps'],
['&#362;', '&#362;', true, 'umacron_caps'],
['&Yacute;', '&#221;', true, 'yacute_caps'],
['&Yuml;', '&#376;', true, 'ydiaeresis_caps'],
['&THORN;', '&#222;', true, 'thorn_caps'],
['&agrave;', '&#224;', true, 'agrave'],
['&aacute;', '&#225;', true, 'aacute'],
['&acirc;', '&#226;', true, 'acircumflex'],
['&atilde;', '&#227;', true, 'atilde'],
['&auml;', '&#228;', true, 'adiaeresis'],
['&aring;', '&#229;', true, 'aringabove'],
['&#257;', '&#257;', true, 'amacron'],
['&aelig;', '&#230;', true, 'ligatureae'],
['&ccedil;', '&#231;', true, 'ccedilla'],
['&egrave;', '&#232;', true, 'egrave'],
['&eacute;', '&#233;', true, 'eacute'],
['&ecirc;', '&#234;', true, 'ecircumflex'],
['&euml;', '&#235;', true, 'ediaeresis'],
['&#275;', '&#275;', true, 'emacron'],
['&igrave;', '&#236;', true, 'igrave'],
['&iacute;', '&#237;', true, 'iacute'],
['&icirc;', '&#238;', true, 'icircumflex'],
['&iuml;', '&#239;', true, 'idiaeresis'],
['&#299;', '&#299;', true, 'imacron'],
['&eth;', '&#240;', true, 'eth'],
['&ntilde;', '&#241;', true, 'ntilde'],
['&ograve;', '&#242;', true, 'ograve'],
['&oacute;', '&#243;', true, 'oacute'],
['&ocirc;', '&#244;', true, 'ocircumflex'],
['&otilde;', '&#245;', true, 'otilde'],
['&ouml;', '&#246;', true, 'odiaeresis'],
['&oslash;', '&#248;', true, 'oslash'],
['&#333;', '&#333;', true, 'omacron'],
['&oelig;', '&#339;', true, 'ligatureoe'],
['&scaron;', '&#353;', true, 'scaron'],
['&ugrave;', '&#249;', true, 'ugrave'],
['&uacute;', '&#250;', true, 'uacute'],
['&ucirc;', '&#251;', true, 'ucircumflex'],
['&uuml;', '&#252;', true, 'udiaeresis'],
['&#363;', '&#363;', true, 'umacron'],
['&yacute;', '&#253;', true, 'yacute'],
['&thorn;', '&#254;', true, 'thorn'],
['&yuml;', '&#255;', true, 'ydiaeresis'],
['&Alpha;', '&#913;', true, 'alpha_caps'],
['&Beta;', '&#914;', true, 'beta_caps'],
['&Gamma;', '&#915;', true, 'gamma_caps'],
['&Delta;', '&#916;', true, 'delta_caps'],
['&Epsilon;', '&#917;', true, 'epsilon_caps'],
['&Zeta;', '&#918;', true, 'zeta_caps'],
['&Eta;', '&#919;', true, 'eta_caps'],
['&Theta;', '&#920;', true, 'theta_caps'],
['&Iota;', '&#921;', true, 'iota_caps'],
['&Kappa;', '&#922;', true, 'kappa_caps'],
['&Lambda;', '&#923;', true, 'lambda_caps'],
['&Mu;', '&#924;', true, 'mu_caps'],
['&Nu;', '&#925;', true, 'nu_caps'],
['&Xi;', '&#926;', true, 'xi_caps'],
['&Omicron;', '&#927;', true, 'omicron_caps'],
['&Pi;', '&#928;', true, 'pi_caps'],
['&Rho;', '&#929;', true, 'rho_caps'],
['&Sigma;', '&#931;', true, 'sigma_caps'],
['&Tau;', '&#932;', true, 'tau_caps'],
['&Upsilon;', '&#933;', true, 'upsilon_caps'],
['&Phi;', '&#934;', true, 'phi_caps'],
['&Chi;', '&#935;', true, 'chi_caps'],
['&Psi;', '&#936;', true, 'psi_caps'],
['&Omega;', '&#937;', true, 'omega_caps'],
['&alpha;', '&#945;', true, 'alpha'],
['&beta;', '&#946;', true, 'beta'],
['&gamma;', '&#947;', true, 'gamma'],
['&delta;', '&#948;', true, 'delta'],
['&epsilon;', '&#949;', true, 'epsilon'],
['&zeta;', '&#950;', true, 'zeta'],
['&eta;', '&#951;', true, 'eta'],
['&theta;', '&#952;', true, 'theta'],
['&iota;', '&#953;', true, 'iota'],
['&kappa;', '&#954;', true, 'kappa'],
['&lambda;', '&#955;', true, 'lambda'],
['&mu;', '&#956;', true, 'mu'],
['&nu;', '&#957;', true, 'nu'],
['&xi;', '&#958;', true, 'xi'],
['&omicron;', '&#959;', true, 'omicron'],
['&pi;', '&#960;', true, 'pi'],
['&rho;', '&#961;', true, 'rho'],
['&sigmaf;', '&#962;', true, 'finalsigma'],
['&sigma;', '&#963;', true, 'sigma'],
['&tau;', '&#964;', true, 'tau'],
['&upsilon;', '&#965;', true, 'upsilon'],
['&phi;', '&#966;', true, 'phi'],
['&chi;', '&#967;', true, 'chi'],
['&psi;', '&#968;', true, 'psi'],
['&omega;', '&#969;', true, 'omega'],
// Symbols.
['&alefsym;', '&#8501;', false, 'alefsymbol'],
['&piv;', '&#982;', false, 'pisymbol'],
['&real;', '&#8476;', false, 'realpartsymbol'],
['&thetasym;', '&#977;', false, 'thetasymbol'],
['&upsih;', '&#978;', false, 'upsilonhooksymbol'],
['&weierp;', '&#8472;', false, 'weierstrassp'],
['&image;', '&#8465;', false, 'imaginarypart'],
// Arrows.
['&larr;', '&#8592;', true, 'leftwardsarrow'],
['&uarr;', '&#8593;', true, 'upwardsarrow'],
['&rarr;', '&#8594;', true, 'rightwardsarrow'],
['&darr;', '&#8595;', true, 'downwardsarrow'],
['&harr;', '&#8596;', true, 'leftrightarrow'],
['&crarr;', '&#8629;', false, 'carriagereturn'],
['&lArr;', '&#8656;', false, 'leftwardsdoublearrow'],
['&uArr;', '&#8657;', false, 'upwardsdoublearrow'],
['&rArr;', '&#8658;', false, 'rightwardsdoublearrow'],
['&dArr;', '&#8659;', false, 'downwardsdoublearrow'],
['&hArr;', '&#8660;', false, 'leftrightdoublearrow'],
['&there4;', '&#8756;', false, 'therefore'],
['&sub;', '&#8834;', false, 'subsetof'],
['&sup;', '&#8835;', false, 'supersetof'],
['&nsub;', '&#8836;', false, 'notasubsetof'],
['&sube;', '&#8838;', false, 'subsetoforequalto'],
['&supe;', '&#8839;', false, 'supersetoforequalto'],
['&oplus;', '&#8853;', false, 'circledplus'],
['&otimes;', '&#8855;', false, 'circledtimes'],
['&perp;', '&#8869;', false, 'perpendicular'],
['&sdot;', '&#8901;', false, 'dotoperator'],
['&lceil;', '&#8968;', false, 'leftceiling'],
['&rceil;', '&#8969;', false, 'rightceiling'],
['&lfloor;', '&#8970;', false, 'leftfloor'],
['&rfloor;', '&#8971;', false, 'rightfloor'],
['&lang;', '&#9001;', false, 'leftpointinganglebracket'],
['&rang;', '&#9002;', false, 'rightpointinganglebracket'],
['&loz;', '&#9674;', true, 'lozenge'],
['&spades;', '&#9824;', true, 'blackspadesuit'],
['&clubs;', '&#9827;', true, 'blackclubsuit'],
['&hearts;', '&#9829;', true, 'blackheartsuit'],
['&diams;', '&#9830;', true, 'blackdiamondsuit'],
['&ensp;', '&#8194;', false, 'enspace'],
['&emsp;', '&#8195;', false, 'emspace'],
['&thinsp;', '&#8201;', false, 'thinspace'],
['&zwnj;', '&#8204;', false, 'zerowidthnonjoiner'],
['&zwj;', '&#8205;', false, 'zerowidthjoiner'],
['&lrm;', '&#8206;', false, 'lefttorightmark'],
['&rlm;', '&#8207;', false, 'righttoleftmark'],
['&shy;', '&#173;', false, 'softhyphen']
];
/**
* Atto text editor charmap plugin.
*
* @namespace M.atto_charmap
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_charmap').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
/**
* A reference to the current selection at the time that the dialogue
* was opened.
*
* @property _currentSelection
* @type Range
* @private
*/
_currentSelection: null,
initializer: function() {
this.addButton({
icon: 'e/special_character',
callback: this._displayDialogue
});
},
/**
* Display the Character Map selector.
*
* @method _displayDialogue
* @private
*/
_displayDialogue: function() {
// Store the current selection.
this._currentSelection = this.get('host').getSelection();
if (this._currentSelection === false) {
return;
}
var dialogue = this.getDialogue({
headerContent: M.util.get_string('insertcharacter', COMPONENTNAME),
focusAfterHide: true
}, true);
// Set the dialogue content, and then show the dialogue.
dialogue.set('bodyContent', this._getDialogueContent())
.show();
},
/**
* Return the dialogue content for the tool.
*
* @method _getDialogueContent
* @private
* @return {Node} The content to place in the dialogue.
*/
_getDialogueContent: function() {
var template = Y.Handlebars.compile(
'<div class="{{CSS.CHARMAP}}">' +
'{{#each CHARMAP}}' +
'{{#if this.[2]}}' +
'<button class="btn btn-secondary btn-sm {{../../CSS.BUTTON}}" ' +
'aria-label="{{get_string this.[3] ../../component}}" ' +
'title="{{get_string this.[3] ../../component}}" ' +
'data-character="{{this.[0]}}" ' +
'>{{{this.[0]}}}</button>' +
'{{/if}}' +
'{{/each}}' +
'</div>'
);
var content = Y.Node.create(template({
component: COMPONENTNAME,
CSS: CSS,
CHARMAP: CHARMAP
}));
content.delegate('click', this._insertChar, '.' + CSS.BUTTON, this);
return content;
},
/**
* Insert the picked character into the editor.
*
* @method _insertChar
* @param {EventFacade} e
* @private
*/
_insertChar: function(e) {
var character = e.target.getData('character');
// Hide the dialogue.
this.getDialogue({
focusAfterHide: null
}).hide();
var host = this.get('host');
// Focus on the last point.
host.setSelection(this._currentSelection);
// And add the character.
host.insertContentAtFocusPoint(character);
// And mark the text area as updated.
this.markUpdated();
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
File diff suppressed because one or more lines are too long
@@ -0,0 +1,431 @@
YUI.add('moodle-atto_charmap-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_charmap
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Atto text editor character map plugin
*
* @module moodle-atto_charmap-button
*/
var COMPONENTNAME = 'atto_charmap',
CSS = {
BUTTON: 'atto_charmap_character',
CHARMAP: 'atto_charmap_selector'
},
/*
* Map of special characters, kindly borrowed from TinyMCE.
*
* Each entries contains in order:
* - {String} HTML code
* - {String} HTML numerical code
* - {Boolean} Whether or not to include it in the list
* - {String} The language string key
*
* @property CHARMAP
* @type {Array}
*/
CHARMAP = [
['&nbsp;', '&#160;', true, 'nobreakspace'],
['&amp;', '&#38;', true, 'ampersand'],
['&quot;', '&#34;', true, 'quotationmark'],
['&#8253;', '&#8253;', true, 'interrobang'],
// Finance.
['&cent;', '&#162;', true, 'centsign'],
['&euro;', '&#8364;', true, 'eurosign'],
['&pound;', '&#163;', true, 'poundsign'],
['&yen;', '&#165;', true, 'yensign'],
// Signs.
['&copy;', '&#169;', true, 'copyrightsign'],
['&reg;', '&#174;', true, 'registeredsign'],
['&trade;', '&#8482;', true, 'trademarksign'],
['&permil;', '&#8240;', true, 'permillesign'],
['&micro;', '&#181;', true, 'microsign'],
['&middot;', '&#183;', true, 'middledot'],
['&bull;', '&#8226;', true, 'bullet'],
['&hellip;', '&#8230;', true, 'threedotleader'],
['&prime;', '&#8242;', true, 'minutesfeet'],
['&Prime;', '&#8243;', true, 'secondsinches'],
['&sect;', '&#167;', true, 'sectionsign'],
['&para;', '&#182;', true, 'paragraphsign'],
['&szlig;', '&#223;', true, 'sharpsesszed'],
// Quotations.
['&lsaquo;', '&#8249;', true, 'singleleftpointinganglequotationmark'],
['&rsaquo;', '&#8250;', true, 'singlerightpointinganglequotationmark'],
['&laquo;', '&#171;', true, 'leftpointingguillemet'],
['&raquo;', '&#187;', true, 'rightpointingguillemet'],
['&lsquo;', '&#8216;', true, 'leftsinglequotationmark'],
['&rsquo;', '&#8217;', true, 'rightsinglequotationmark'],
['&ldquo;', '&#8220;', true, 'leftdoublequotationmark'],
['&rdquo;', '&#8221;', true, 'rightdoublequotationmark'],
['&sbquo;', '&#8218;', true, 'singlelow9quotationmark'],
['&bdquo;', '&#8222;', true, 'doublelow9quotationmark'],
['&lt;', '&#60;', true, 'lessthansign'],
['&gt;', '&#62;', true, 'greaterthansign'],
['&le;', '&#8804;', true, 'lessthanorequalto'],
['&ge;', '&#8805;', true, 'greaterthanorequalto'],
['&ndash;', '&#8211;', true, 'endash'],
['&mdash;', '&#8212;', true, 'emdash'],
['&macr;', '&#175;', true, 'macron'],
['&oline;', '&#8254;', true, 'overline'],
['&curren;', '&#164;', true, 'currencysign'],
['&brvbar;', '&#166;', true, 'brokenbar'],
['&uml;', '&#168;', true, 'diaeresis'],
['&iexcl;', '&#161;', true, 'invertedexclamationmark'],
['&iquest;', '&#191;', true, 'turnedquestionmark'],
['&circ;', '&#710;', true, 'circumflexaccent'],
['&tilde;', '&#732;', true, 'smalltilde'],
['&deg;', '&#176;', true, 'degreesign'],
['&minus;', '&#8722;', true, 'minussign'],
['&plusmn;', '&#177;', true, 'plusminussign'],
['&divide;', '&#247;', true, 'divisionsign'],
['&frasl;', '&#8260;', true, 'fractionslash'],
['&times;', '&#215;', true, 'multiplicationsign'],
['&sup1;', '&#185;', true, 'superscriptone'],
['&sup2;', '&#178;', true, 'superscripttwo'],
['&sup3;', '&#179;', true, 'superscriptthree'],
['&frac14;', '&#188;', true, 'fractiononequarter'],
['&frac12;', '&#189;', true, 'fractiononehalf'],
['&frac34;', '&#190;', true, 'fractionthreequarters'],
// Math / logical.
['&fnof;', '&#402;', true, 'functionflorin'],
['&int;', '&#8747;', true, 'integral'],
['&sum;', '&#8721;', true, 'narysumation'],
['&infin;', '&#8734;', true, 'infinity'],
['&radic;', '&#8730;', true, 'squareroot'],
['&sim;', '&#8764;', false, 'similarto'],
['&cong;', '&#8773;', false, 'approximatelyequalto'],
['&asymp;', '&#8776;', true, 'almostequalto'],
['&ne;', '&#8800;', true, 'notequalto'],
['&equiv;', '&#8801;', true, 'identicalto'],
['&isin;', '&#8712;', false, 'elementof'],
['&notin;', '&#8713;', false, 'notanelementof'],
['&ni;', '&#8715;', false, 'containsasmember'],
['&prod;', '&#8719;', true, 'naryproduct'],
['&and;', '&#8743;', false, 'logicaland'],
['&or;', '&#8744;', false, 'logicalor'],
['&not;', '&#172;', true, 'notsign'],
['&cap;', '&#8745;', true, 'intersection'],
['&cup;', '&#8746;', false, 'union'],
['&part;', '&#8706;', true, 'partialdifferential'],
['&forall;', '&#8704;', false, 'forall'],
['&exist;', '&#8707;', false, 'thereexists'],
['&empty;', '&#8709;', false, 'diameter'],
['&nabla;', '&#8711;', false, 'backwarddifference'],
['&lowast;', '&#8727;', false, 'asteriskoperator'],
['&prop;', '&#8733;', false, 'proportionalto'],
['&ang;', '&#8736;', false, 'angle'],
// Undefined.
['&acute;', '&#180;', true, 'acuteaccent'],
['&cedil;', '&#184;', true, 'cedilla'],
['&ordf;', '&#170;', true, 'feminineordinalindicator'],
['&ordm;', '&#186;', true, 'masculineordinalindicator'],
['&dagger;', '&#8224;', true, 'dagger'],
['&Dagger;', '&#8225;', true, 'doubledagger'],
// Alphabetical special chars.
['&Agrave;', '&#192;', true, 'agrave_caps'],
['&Aacute;', '&#193;', true, 'aacute_caps'],
['&Acirc;', '&#194;', true, 'acircumflex_caps'],
['&Atilde;', '&#195;', true, 'atilde_caps'],
['&Auml;', '&#196;', true, 'adiaeresis_caps'],
['&Aring;', '&#197;', true, 'aringabove_caps'],
['&#256;', '&#256;', true, 'amacron_caps'],
['&AElig;', '&#198;', true, 'ligatureae_caps'],
['&Ccedil;', '&#199;', true, 'ccedilla_caps'],
['&Egrave;', '&#200;', true, 'egrave_caps'],
['&Eacute;', '&#201;', true, 'eacute_caps'],
['&Ecirc;', '&#202;', true, 'ecircumflex_caps'],
['&Euml;', '&#203;', true, 'ediaeresis_caps'],
['&#274;', '&#274;', true, 'emacron_caps'],
['&Igrave;', '&#204;', true, 'igrave_caps'],
['&Iacute;', '&#205;', true, 'iacute_caps'],
['&Icirc;', '&#206;', true, 'icircumflex_caps'],
['&Iuml;', '&#207;', true, 'idiaeresis_caps'],
['&#298;', '&#298;', true, 'imacron_caps'],
['&ETH;', '&#208;', true, 'eth_caps'],
['&Ntilde;', '&#209;', true, 'ntilde_caps'],
['&Ograve;', '&#210;', true, 'ograve_caps'],
['&Oacute;', '&#211;', true, 'oacute_caps'],
['&Ocirc;', '&#212;', true, 'ocircumflex_caps'],
['&Otilde;', '&#213;', true, 'otilde_caps'],
['&Ouml;', '&#214;', true, 'odiaeresis_caps'],
['&Oslash;', '&#216;', true, 'oslash_caps'],
['&#332;', '&#332;', true, 'omacron_caps'],
['&OElig;', '&#338;', true, 'ligatureoe_caps'],
['&Scaron;', '&#352;', true, 'scaron_caps'],
['&Ugrave;', '&#217;', true, 'ugrave_caps'],
['&Uacute;', '&#218;', true, 'uacute_caps'],
['&Ucirc;', '&#219;', true, 'ucircumflex_caps'],
['&Uuml;', '&#220;', true, 'udiaeresis_caps'],
['&#362;', '&#362;', true, 'umacron_caps'],
['&Yacute;', '&#221;', true, 'yacute_caps'],
['&Yuml;', '&#376;', true, 'ydiaeresis_caps'],
['&THORN;', '&#222;', true, 'thorn_caps'],
['&agrave;', '&#224;', true, 'agrave'],
['&aacute;', '&#225;', true, 'aacute'],
['&acirc;', '&#226;', true, 'acircumflex'],
['&atilde;', '&#227;', true, 'atilde'],
['&auml;', '&#228;', true, 'adiaeresis'],
['&aring;', '&#229;', true, 'aringabove'],
['&#257;', '&#257;', true, 'amacron'],
['&aelig;', '&#230;', true, 'ligatureae'],
['&ccedil;', '&#231;', true, 'ccedilla'],
['&egrave;', '&#232;', true, 'egrave'],
['&eacute;', '&#233;', true, 'eacute'],
['&ecirc;', '&#234;', true, 'ecircumflex'],
['&euml;', '&#235;', true, 'ediaeresis'],
['&#275;', '&#275;', true, 'emacron'],
['&igrave;', '&#236;', true, 'igrave'],
['&iacute;', '&#237;', true, 'iacute'],
['&icirc;', '&#238;', true, 'icircumflex'],
['&iuml;', '&#239;', true, 'idiaeresis'],
['&#299;', '&#299;', true, 'imacron'],
['&eth;', '&#240;', true, 'eth'],
['&ntilde;', '&#241;', true, 'ntilde'],
['&ograve;', '&#242;', true, 'ograve'],
['&oacute;', '&#243;', true, 'oacute'],
['&ocirc;', '&#244;', true, 'ocircumflex'],
['&otilde;', '&#245;', true, 'otilde'],
['&ouml;', '&#246;', true, 'odiaeresis'],
['&oslash;', '&#248;', true, 'oslash'],
['&#333;', '&#333;', true, 'omacron'],
['&oelig;', '&#339;', true, 'ligatureoe'],
['&scaron;', '&#353;', true, 'scaron'],
['&ugrave;', '&#249;', true, 'ugrave'],
['&uacute;', '&#250;', true, 'uacute'],
['&ucirc;', '&#251;', true, 'ucircumflex'],
['&uuml;', '&#252;', true, 'udiaeresis'],
['&#363;', '&#363;', true, 'umacron'],
['&yacute;', '&#253;', true, 'yacute'],
['&thorn;', '&#254;', true, 'thorn'],
['&yuml;', '&#255;', true, 'ydiaeresis'],
['&Alpha;', '&#913;', true, 'alpha_caps'],
['&Beta;', '&#914;', true, 'beta_caps'],
['&Gamma;', '&#915;', true, 'gamma_caps'],
['&Delta;', '&#916;', true, 'delta_caps'],
['&Epsilon;', '&#917;', true, 'epsilon_caps'],
['&Zeta;', '&#918;', true, 'zeta_caps'],
['&Eta;', '&#919;', true, 'eta_caps'],
['&Theta;', '&#920;', true, 'theta_caps'],
['&Iota;', '&#921;', true, 'iota_caps'],
['&Kappa;', '&#922;', true, 'kappa_caps'],
['&Lambda;', '&#923;', true, 'lambda_caps'],
['&Mu;', '&#924;', true, 'mu_caps'],
['&Nu;', '&#925;', true, 'nu_caps'],
['&Xi;', '&#926;', true, 'xi_caps'],
['&Omicron;', '&#927;', true, 'omicron_caps'],
['&Pi;', '&#928;', true, 'pi_caps'],
['&Rho;', '&#929;', true, 'rho_caps'],
['&Sigma;', '&#931;', true, 'sigma_caps'],
['&Tau;', '&#932;', true, 'tau_caps'],
['&Upsilon;', '&#933;', true, 'upsilon_caps'],
['&Phi;', '&#934;', true, 'phi_caps'],
['&Chi;', '&#935;', true, 'chi_caps'],
['&Psi;', '&#936;', true, 'psi_caps'],
['&Omega;', '&#937;', true, 'omega_caps'],
['&alpha;', '&#945;', true, 'alpha'],
['&beta;', '&#946;', true, 'beta'],
['&gamma;', '&#947;', true, 'gamma'],
['&delta;', '&#948;', true, 'delta'],
['&epsilon;', '&#949;', true, 'epsilon'],
['&zeta;', '&#950;', true, 'zeta'],
['&eta;', '&#951;', true, 'eta'],
['&theta;', '&#952;', true, 'theta'],
['&iota;', '&#953;', true, 'iota'],
['&kappa;', '&#954;', true, 'kappa'],
['&lambda;', '&#955;', true, 'lambda'],
['&mu;', '&#956;', true, 'mu'],
['&nu;', '&#957;', true, 'nu'],
['&xi;', '&#958;', true, 'xi'],
['&omicron;', '&#959;', true, 'omicron'],
['&pi;', '&#960;', true, 'pi'],
['&rho;', '&#961;', true, 'rho'],
['&sigmaf;', '&#962;', true, 'finalsigma'],
['&sigma;', '&#963;', true, 'sigma'],
['&tau;', '&#964;', true, 'tau'],
['&upsilon;', '&#965;', true, 'upsilon'],
['&phi;', '&#966;', true, 'phi'],
['&chi;', '&#967;', true, 'chi'],
['&psi;', '&#968;', true, 'psi'],
['&omega;', '&#969;', true, 'omega'],
// Symbols.
['&alefsym;', '&#8501;', false, 'alefsymbol'],
['&piv;', '&#982;', false, 'pisymbol'],
['&real;', '&#8476;', false, 'realpartsymbol'],
['&thetasym;', '&#977;', false, 'thetasymbol'],
['&upsih;', '&#978;', false, 'upsilonhooksymbol'],
['&weierp;', '&#8472;', false, 'weierstrassp'],
['&image;', '&#8465;', false, 'imaginarypart'],
// Arrows.
['&larr;', '&#8592;', true, 'leftwardsarrow'],
['&uarr;', '&#8593;', true, 'upwardsarrow'],
['&rarr;', '&#8594;', true, 'rightwardsarrow'],
['&darr;', '&#8595;', true, 'downwardsarrow'],
['&harr;', '&#8596;', true, 'leftrightarrow'],
['&crarr;', '&#8629;', false, 'carriagereturn'],
['&lArr;', '&#8656;', false, 'leftwardsdoublearrow'],
['&uArr;', '&#8657;', false, 'upwardsdoublearrow'],
['&rArr;', '&#8658;', false, 'rightwardsdoublearrow'],
['&dArr;', '&#8659;', false, 'downwardsdoublearrow'],
['&hArr;', '&#8660;', false, 'leftrightdoublearrow'],
['&there4;', '&#8756;', false, 'therefore'],
['&sub;', '&#8834;', false, 'subsetof'],
['&sup;', '&#8835;', false, 'supersetof'],
['&nsub;', '&#8836;', false, 'notasubsetof'],
['&sube;', '&#8838;', false, 'subsetoforequalto'],
['&supe;', '&#8839;', false, 'supersetoforequalto'],
['&oplus;', '&#8853;', false, 'circledplus'],
['&otimes;', '&#8855;', false, 'circledtimes'],
['&perp;', '&#8869;', false, 'perpendicular'],
['&sdot;', '&#8901;', false, 'dotoperator'],
['&lceil;', '&#8968;', false, 'leftceiling'],
['&rceil;', '&#8969;', false, 'rightceiling'],
['&lfloor;', '&#8970;', false, 'leftfloor'],
['&rfloor;', '&#8971;', false, 'rightfloor'],
['&lang;', '&#9001;', false, 'leftpointinganglebracket'],
['&rang;', '&#9002;', false, 'rightpointinganglebracket'],
['&loz;', '&#9674;', true, 'lozenge'],
['&spades;', '&#9824;', true, 'blackspadesuit'],
['&clubs;', '&#9827;', true, 'blackclubsuit'],
['&hearts;', '&#9829;', true, 'blackheartsuit'],
['&diams;', '&#9830;', true, 'blackdiamondsuit'],
['&ensp;', '&#8194;', false, 'enspace'],
['&emsp;', '&#8195;', false, 'emspace'],
['&thinsp;', '&#8201;', false, 'thinspace'],
['&zwnj;', '&#8204;', false, 'zerowidthnonjoiner'],
['&zwj;', '&#8205;', false, 'zerowidthjoiner'],
['&lrm;', '&#8206;', false, 'lefttorightmark'],
['&rlm;', '&#8207;', false, 'righttoleftmark'],
['&shy;', '&#173;', false, 'softhyphen']
];
/**
* Atto text editor charmap plugin.
*
* @namespace M.atto_charmap
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_charmap').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
/**
* A reference to the current selection at the time that the dialogue
* was opened.
*
* @property _currentSelection
* @type Range
* @private
*/
_currentSelection: null,
initializer: function() {
this.addButton({
icon: 'e/special_character',
callback: this._displayDialogue
});
},
/**
* Display the Character Map selector.
*
* @method _displayDialogue
* @private
*/
_displayDialogue: function() {
// Store the current selection.
this._currentSelection = this.get('host').getSelection();
if (this._currentSelection === false) {
return;
}
var dialogue = this.getDialogue({
headerContent: M.util.get_string('insertcharacter', COMPONENTNAME),
focusAfterHide: true
}, true);
// Set the dialogue content, and then show the dialogue.
dialogue.set('bodyContent', this._getDialogueContent())
.show();
},
/**
* Return the dialogue content for the tool.
*
* @method _getDialogueContent
* @private
* @return {Node} The content to place in the dialogue.
*/
_getDialogueContent: function() {
var template = Y.Handlebars.compile(
'<div class="{{CSS.CHARMAP}}">' +
'{{#each CHARMAP}}' +
'{{#if this.[2]}}' +
'<button class="btn btn-secondary btn-sm {{../../CSS.BUTTON}}" ' +
'aria-label="{{get_string this.[3] ../../component}}" ' +
'title="{{get_string this.[3] ../../component}}" ' +
'data-character="{{this.[0]}}" ' +
'>{{{this.[0]}}}</button>' +
'{{/if}}' +
'{{/each}}' +
'</div>'
);
var content = Y.Node.create(template({
component: COMPONENTNAME,
CSS: CSS,
CHARMAP: CHARMAP
}));
content.delegate('click', this._insertChar, '.' + CSS.BUTTON, this);
return content;
},
/**
* Insert the picked character into the editor.
*
* @method _insertChar
* @param {EventFacade} e
* @private
*/
_insertChar: function(e) {
var character = e.target.getData('character');
// Hide the dialogue.
this.getDialogue({
focusAfterHide: null
}).hide();
var host = this.get('host');
// Focus on the last point.
host.setSelection(this._currentSelection);
// And add the character.
host.insertContentAtFocusPoint(character);
// And mark the text area as updated.
this.markUpdated();
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
@@ -0,0 +1,10 @@
{
"name": "moodle-atto_charmap-button",
"builds": {
"moodle-atto_charmap-button": {
"jsfiles": [
"button.js"
]
}
}
}
@@ -0,0 +1,426 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_charmap
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Atto text editor character map plugin
*
* @module moodle-atto_charmap-button
*/
var COMPONENTNAME = 'atto_charmap',
CSS = {
BUTTON: 'atto_charmap_character',
CHARMAP: 'atto_charmap_selector'
},
/*
* Map of special characters, kindly borrowed from TinyMCE.
*
* Each entries contains in order:
* - {String} HTML code
* - {String} HTML numerical code
* - {Boolean} Whether or not to include it in the list
* - {String} The language string key
*
* @property CHARMAP
* @type {Array}
*/
CHARMAP = [
['&nbsp;', '&#160;', true, 'nobreakspace'],
['&amp;', '&#38;', true, 'ampersand'],
['&quot;', '&#34;', true, 'quotationmark'],
['&#8253;', '&#8253;', true, 'interrobang'],
// Finance.
['&cent;', '&#162;', true, 'centsign'],
['&euro;', '&#8364;', true, 'eurosign'],
['&pound;', '&#163;', true, 'poundsign'],
['&yen;', '&#165;', true, 'yensign'],
// Signs.
['&copy;', '&#169;', true, 'copyrightsign'],
['&reg;', '&#174;', true, 'registeredsign'],
['&trade;', '&#8482;', true, 'trademarksign'],
['&permil;', '&#8240;', true, 'permillesign'],
['&micro;', '&#181;', true, 'microsign'],
['&middot;', '&#183;', true, 'middledot'],
['&bull;', '&#8226;', true, 'bullet'],
['&hellip;', '&#8230;', true, 'threedotleader'],
['&prime;', '&#8242;', true, 'minutesfeet'],
['&Prime;', '&#8243;', true, 'secondsinches'],
['&sect;', '&#167;', true, 'sectionsign'],
['&para;', '&#182;', true, 'paragraphsign'],
['&szlig;', '&#223;', true, 'sharpsesszed'],
// Quotations.
['&lsaquo;', '&#8249;', true, 'singleleftpointinganglequotationmark'],
['&rsaquo;', '&#8250;', true, 'singlerightpointinganglequotationmark'],
['&laquo;', '&#171;', true, 'leftpointingguillemet'],
['&raquo;', '&#187;', true, 'rightpointingguillemet'],
['&lsquo;', '&#8216;', true, 'leftsinglequotationmark'],
['&rsquo;', '&#8217;', true, 'rightsinglequotationmark'],
['&ldquo;', '&#8220;', true, 'leftdoublequotationmark'],
['&rdquo;', '&#8221;', true, 'rightdoublequotationmark'],
['&sbquo;', '&#8218;', true, 'singlelow9quotationmark'],
['&bdquo;', '&#8222;', true, 'doublelow9quotationmark'],
['&lt;', '&#60;', true, 'lessthansign'],
['&gt;', '&#62;', true, 'greaterthansign'],
['&le;', '&#8804;', true, 'lessthanorequalto'],
['&ge;', '&#8805;', true, 'greaterthanorequalto'],
['&ndash;', '&#8211;', true, 'endash'],
['&mdash;', '&#8212;', true, 'emdash'],
['&macr;', '&#175;', true, 'macron'],
['&oline;', '&#8254;', true, 'overline'],
['&curren;', '&#164;', true, 'currencysign'],
['&brvbar;', '&#166;', true, 'brokenbar'],
['&uml;', '&#168;', true, 'diaeresis'],
['&iexcl;', '&#161;', true, 'invertedexclamationmark'],
['&iquest;', '&#191;', true, 'turnedquestionmark'],
['&circ;', '&#710;', true, 'circumflexaccent'],
['&tilde;', '&#732;', true, 'smalltilde'],
['&deg;', '&#176;', true, 'degreesign'],
['&minus;', '&#8722;', true, 'minussign'],
['&plusmn;', '&#177;', true, 'plusminussign'],
['&divide;', '&#247;', true, 'divisionsign'],
['&frasl;', '&#8260;', true, 'fractionslash'],
['&times;', '&#215;', true, 'multiplicationsign'],
['&sup1;', '&#185;', true, 'superscriptone'],
['&sup2;', '&#178;', true, 'superscripttwo'],
['&sup3;', '&#179;', true, 'superscriptthree'],
['&frac14;', '&#188;', true, 'fractiononequarter'],
['&frac12;', '&#189;', true, 'fractiononehalf'],
['&frac34;', '&#190;', true, 'fractionthreequarters'],
// Math / logical.
['&fnof;', '&#402;', true, 'functionflorin'],
['&int;', '&#8747;', true, 'integral'],
['&sum;', '&#8721;', true, 'narysumation'],
['&infin;', '&#8734;', true, 'infinity'],
['&radic;', '&#8730;', true, 'squareroot'],
['&sim;', '&#8764;', false, 'similarto'],
['&cong;', '&#8773;', false, 'approximatelyequalto'],
['&asymp;', '&#8776;', true, 'almostequalto'],
['&ne;', '&#8800;', true, 'notequalto'],
['&equiv;', '&#8801;', true, 'identicalto'],
['&isin;', '&#8712;', false, 'elementof'],
['&notin;', '&#8713;', false, 'notanelementof'],
['&ni;', '&#8715;', false, 'containsasmember'],
['&prod;', '&#8719;', true, 'naryproduct'],
['&and;', '&#8743;', false, 'logicaland'],
['&or;', '&#8744;', false, 'logicalor'],
['&not;', '&#172;', true, 'notsign'],
['&cap;', '&#8745;', true, 'intersection'],
['&cup;', '&#8746;', false, 'union'],
['&part;', '&#8706;', true, 'partialdifferential'],
['&forall;', '&#8704;', false, 'forall'],
['&exist;', '&#8707;', false, 'thereexists'],
['&empty;', '&#8709;', false, 'diameter'],
['&nabla;', '&#8711;', false, 'backwarddifference'],
['&lowast;', '&#8727;', false, 'asteriskoperator'],
['&prop;', '&#8733;', false, 'proportionalto'],
['&ang;', '&#8736;', false, 'angle'],
// Undefined.
['&acute;', '&#180;', true, 'acuteaccent'],
['&cedil;', '&#184;', true, 'cedilla'],
['&ordf;', '&#170;', true, 'feminineordinalindicator'],
['&ordm;', '&#186;', true, 'masculineordinalindicator'],
['&dagger;', '&#8224;', true, 'dagger'],
['&Dagger;', '&#8225;', true, 'doubledagger'],
// Alphabetical special chars.
['&Agrave;', '&#192;', true, 'agrave_caps'],
['&Aacute;', '&#193;', true, 'aacute_caps'],
['&Acirc;', '&#194;', true, 'acircumflex_caps'],
['&Atilde;', '&#195;', true, 'atilde_caps'],
['&Auml;', '&#196;', true, 'adiaeresis_caps'],
['&Aring;', '&#197;', true, 'aringabove_caps'],
['&#256;', '&#256;', true, 'amacron_caps'],
['&AElig;', '&#198;', true, 'ligatureae_caps'],
['&Ccedil;', '&#199;', true, 'ccedilla_caps'],
['&Egrave;', '&#200;', true, 'egrave_caps'],
['&Eacute;', '&#201;', true, 'eacute_caps'],
['&Ecirc;', '&#202;', true, 'ecircumflex_caps'],
['&Euml;', '&#203;', true, 'ediaeresis_caps'],
['&#274;', '&#274;', true, 'emacron_caps'],
['&Igrave;', '&#204;', true, 'igrave_caps'],
['&Iacute;', '&#205;', true, 'iacute_caps'],
['&Icirc;', '&#206;', true, 'icircumflex_caps'],
['&Iuml;', '&#207;', true, 'idiaeresis_caps'],
['&#298;', '&#298;', true, 'imacron_caps'],
['&ETH;', '&#208;', true, 'eth_caps'],
['&Ntilde;', '&#209;', true, 'ntilde_caps'],
['&Ograve;', '&#210;', true, 'ograve_caps'],
['&Oacute;', '&#211;', true, 'oacute_caps'],
['&Ocirc;', '&#212;', true, 'ocircumflex_caps'],
['&Otilde;', '&#213;', true, 'otilde_caps'],
['&Ouml;', '&#214;', true, 'odiaeresis_caps'],
['&Oslash;', '&#216;', true, 'oslash_caps'],
['&#332;', '&#332;', true, 'omacron_caps'],
['&OElig;', '&#338;', true, 'ligatureoe_caps'],
['&Scaron;', '&#352;', true, 'scaron_caps'],
['&Ugrave;', '&#217;', true, 'ugrave_caps'],
['&Uacute;', '&#218;', true, 'uacute_caps'],
['&Ucirc;', '&#219;', true, 'ucircumflex_caps'],
['&Uuml;', '&#220;', true, 'udiaeresis_caps'],
['&#362;', '&#362;', true, 'umacron_caps'],
['&Yacute;', '&#221;', true, 'yacute_caps'],
['&Yuml;', '&#376;', true, 'ydiaeresis_caps'],
['&THORN;', '&#222;', true, 'thorn_caps'],
['&agrave;', '&#224;', true, 'agrave'],
['&aacute;', '&#225;', true, 'aacute'],
['&acirc;', '&#226;', true, 'acircumflex'],
['&atilde;', '&#227;', true, 'atilde'],
['&auml;', '&#228;', true, 'adiaeresis'],
['&aring;', '&#229;', true, 'aringabove'],
['&#257;', '&#257;', true, 'amacron'],
['&aelig;', '&#230;', true, 'ligatureae'],
['&ccedil;', '&#231;', true, 'ccedilla'],
['&egrave;', '&#232;', true, 'egrave'],
['&eacute;', '&#233;', true, 'eacute'],
['&ecirc;', '&#234;', true, 'ecircumflex'],
['&euml;', '&#235;', true, 'ediaeresis'],
['&#275;', '&#275;', true, 'emacron'],
['&igrave;', '&#236;', true, 'igrave'],
['&iacute;', '&#237;', true, 'iacute'],
['&icirc;', '&#238;', true, 'icircumflex'],
['&iuml;', '&#239;', true, 'idiaeresis'],
['&#299;', '&#299;', true, 'imacron'],
['&eth;', '&#240;', true, 'eth'],
['&ntilde;', '&#241;', true, 'ntilde'],
['&ograve;', '&#242;', true, 'ograve'],
['&oacute;', '&#243;', true, 'oacute'],
['&ocirc;', '&#244;', true, 'ocircumflex'],
['&otilde;', '&#245;', true, 'otilde'],
['&ouml;', '&#246;', true, 'odiaeresis'],
['&oslash;', '&#248;', true, 'oslash'],
['&#333;', '&#333;', true, 'omacron'],
['&oelig;', '&#339;', true, 'ligatureoe'],
['&scaron;', '&#353;', true, 'scaron'],
['&ugrave;', '&#249;', true, 'ugrave'],
['&uacute;', '&#250;', true, 'uacute'],
['&ucirc;', '&#251;', true, 'ucircumflex'],
['&uuml;', '&#252;', true, 'udiaeresis'],
['&#363;', '&#363;', true, 'umacron'],
['&yacute;', '&#253;', true, 'yacute'],
['&thorn;', '&#254;', true, 'thorn'],
['&yuml;', '&#255;', true, 'ydiaeresis'],
['&Alpha;', '&#913;', true, 'alpha_caps'],
['&Beta;', '&#914;', true, 'beta_caps'],
['&Gamma;', '&#915;', true, 'gamma_caps'],
['&Delta;', '&#916;', true, 'delta_caps'],
['&Epsilon;', '&#917;', true, 'epsilon_caps'],
['&Zeta;', '&#918;', true, 'zeta_caps'],
['&Eta;', '&#919;', true, 'eta_caps'],
['&Theta;', '&#920;', true, 'theta_caps'],
['&Iota;', '&#921;', true, 'iota_caps'],
['&Kappa;', '&#922;', true, 'kappa_caps'],
['&Lambda;', '&#923;', true, 'lambda_caps'],
['&Mu;', '&#924;', true, 'mu_caps'],
['&Nu;', '&#925;', true, 'nu_caps'],
['&Xi;', '&#926;', true, 'xi_caps'],
['&Omicron;', '&#927;', true, 'omicron_caps'],
['&Pi;', '&#928;', true, 'pi_caps'],
['&Rho;', '&#929;', true, 'rho_caps'],
['&Sigma;', '&#931;', true, 'sigma_caps'],
['&Tau;', '&#932;', true, 'tau_caps'],
['&Upsilon;', '&#933;', true, 'upsilon_caps'],
['&Phi;', '&#934;', true, 'phi_caps'],
['&Chi;', '&#935;', true, 'chi_caps'],
['&Psi;', '&#936;', true, 'psi_caps'],
['&Omega;', '&#937;', true, 'omega_caps'],
['&alpha;', '&#945;', true, 'alpha'],
['&beta;', '&#946;', true, 'beta'],
['&gamma;', '&#947;', true, 'gamma'],
['&delta;', '&#948;', true, 'delta'],
['&epsilon;', '&#949;', true, 'epsilon'],
['&zeta;', '&#950;', true, 'zeta'],
['&eta;', '&#951;', true, 'eta'],
['&theta;', '&#952;', true, 'theta'],
['&iota;', '&#953;', true, 'iota'],
['&kappa;', '&#954;', true, 'kappa'],
['&lambda;', '&#955;', true, 'lambda'],
['&mu;', '&#956;', true, 'mu'],
['&nu;', '&#957;', true, 'nu'],
['&xi;', '&#958;', true, 'xi'],
['&omicron;', '&#959;', true, 'omicron'],
['&pi;', '&#960;', true, 'pi'],
['&rho;', '&#961;', true, 'rho'],
['&sigmaf;', '&#962;', true, 'finalsigma'],
['&sigma;', '&#963;', true, 'sigma'],
['&tau;', '&#964;', true, 'tau'],
['&upsilon;', '&#965;', true, 'upsilon'],
['&phi;', '&#966;', true, 'phi'],
['&chi;', '&#967;', true, 'chi'],
['&psi;', '&#968;', true, 'psi'],
['&omega;', '&#969;', true, 'omega'],
// Symbols.
['&alefsym;', '&#8501;', false, 'alefsymbol'],
['&piv;', '&#982;', false, 'pisymbol'],
['&real;', '&#8476;', false, 'realpartsymbol'],
['&thetasym;', '&#977;', false, 'thetasymbol'],
['&upsih;', '&#978;', false, 'upsilonhooksymbol'],
['&weierp;', '&#8472;', false, 'weierstrassp'],
['&image;', '&#8465;', false, 'imaginarypart'],
// Arrows.
['&larr;', '&#8592;', true, 'leftwardsarrow'],
['&uarr;', '&#8593;', true, 'upwardsarrow'],
['&rarr;', '&#8594;', true, 'rightwardsarrow'],
['&darr;', '&#8595;', true, 'downwardsarrow'],
['&harr;', '&#8596;', true, 'leftrightarrow'],
['&crarr;', '&#8629;', false, 'carriagereturn'],
['&lArr;', '&#8656;', false, 'leftwardsdoublearrow'],
['&uArr;', '&#8657;', false, 'upwardsdoublearrow'],
['&rArr;', '&#8658;', false, 'rightwardsdoublearrow'],
['&dArr;', '&#8659;', false, 'downwardsdoublearrow'],
['&hArr;', '&#8660;', false, 'leftrightdoublearrow'],
['&there4;', '&#8756;', false, 'therefore'],
['&sub;', '&#8834;', false, 'subsetof'],
['&sup;', '&#8835;', false, 'supersetof'],
['&nsub;', '&#8836;', false, 'notasubsetof'],
['&sube;', '&#8838;', false, 'subsetoforequalto'],
['&supe;', '&#8839;', false, 'supersetoforequalto'],
['&oplus;', '&#8853;', false, 'circledplus'],
['&otimes;', '&#8855;', false, 'circledtimes'],
['&perp;', '&#8869;', false, 'perpendicular'],
['&sdot;', '&#8901;', false, 'dotoperator'],
['&lceil;', '&#8968;', false, 'leftceiling'],
['&rceil;', '&#8969;', false, 'rightceiling'],
['&lfloor;', '&#8970;', false, 'leftfloor'],
['&rfloor;', '&#8971;', false, 'rightfloor'],
['&lang;', '&#9001;', false, 'leftpointinganglebracket'],
['&rang;', '&#9002;', false, 'rightpointinganglebracket'],
['&loz;', '&#9674;', true, 'lozenge'],
['&spades;', '&#9824;', true, 'blackspadesuit'],
['&clubs;', '&#9827;', true, 'blackclubsuit'],
['&hearts;', '&#9829;', true, 'blackheartsuit'],
['&diams;', '&#9830;', true, 'blackdiamondsuit'],
['&ensp;', '&#8194;', false, 'enspace'],
['&emsp;', '&#8195;', false, 'emspace'],
['&thinsp;', '&#8201;', false, 'thinspace'],
['&zwnj;', '&#8204;', false, 'zerowidthnonjoiner'],
['&zwj;', '&#8205;', false, 'zerowidthjoiner'],
['&lrm;', '&#8206;', false, 'lefttorightmark'],
['&rlm;', '&#8207;', false, 'righttoleftmark'],
['&shy;', '&#173;', false, 'softhyphen']
];
/**
* Atto text editor charmap plugin.
*
* @namespace M.atto_charmap
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_charmap').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
/**
* A reference to the current selection at the time that the dialogue
* was opened.
*
* @property _currentSelection
* @type Range
* @private
*/
_currentSelection: null,
initializer: function() {
this.addButton({
icon: 'e/special_character',
callback: this._displayDialogue
});
},
/**
* Display the Character Map selector.
*
* @method _displayDialogue
* @private
*/
_displayDialogue: function() {
// Store the current selection.
this._currentSelection = this.get('host').getSelection();
if (this._currentSelection === false) {
return;
}
var dialogue = this.getDialogue({
headerContent: M.util.get_string('insertcharacter', COMPONENTNAME),
focusAfterHide: true
}, true);
// Set the dialogue content, and then show the dialogue.
dialogue.set('bodyContent', this._getDialogueContent())
.show();
},
/**
* Return the dialogue content for the tool.
*
* @method _getDialogueContent
* @private
* @return {Node} The content to place in the dialogue.
*/
_getDialogueContent: function() {
var template = Y.Handlebars.compile(
'<div class="{{CSS.CHARMAP}}">' +
'{{#each CHARMAP}}' +
'{{#if this.[2]}}' +
'<button class="btn btn-secondary btn-sm {{../../CSS.BUTTON}}" ' +
'aria-label="{{get_string this.[3] ../../component}}" ' +
'title="{{get_string this.[3] ../../component}}" ' +
'data-character="{{this.[0]}}" ' +
'>{{{this.[0]}}}</button>' +
'{{/if}}' +
'{{/each}}' +
'</div>'
);
var content = Y.Node.create(template({
component: COMPONENTNAME,
CSS: CSS,
CHARMAP: CHARMAP
}));
content.delegate('click', this._insertChar, '.' + CSS.BUTTON, this);
return content;
},
/**
* Insert the picked character into the editor.
*
* @method _insertChar
* @param {EventFacade} e
* @private
*/
_insertChar: function(e) {
var character = e.target.getData('character');
// Hide the dialogue.
this.getDialogue({
focusAfterHide: null
}).hide();
var host = this.get('host');
// Focus on the last point.
host.setSelection(this._currentSelection);
// And add the character.
host.insertContentAtFocusPoint(character);
// And mark the text area as updated.
this.markUpdated();
}
});
@@ -0,0 +1,7 @@
{
"moodle-atto_charmap-button": {
"requires": [
"moodle-editor_atto-plugin"
]
}
}
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for block_activity_modules.
*
* @package atto_clear
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace atto_clear\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for atto_clear implementing null_provider.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,26 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_clear', language 'en'.
*
* @package atto_clear
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Clear formatting';
$string['privacy:metadata'] = 'The atto_clear plugin does not store any personal data.';
@@ -0,0 +1,21 @@
@editor @editor_atto @atto @atto_clear @_bug_phantomjs
Feature: Atto clear button
To format text in Atto, I need to remove formatting
@javascript
Scenario: Clear formatting
Given I log in as "admin"
And I open my profile in edit mode
And I set the field "Description" to "Pisa"
And I select the text in the "Description" Atto editor
And I click on "Italic" "button"
And I click on "Show more buttons" "button"
And I select the text in the "Description" Atto editor
When I click on "Clear formatting" "button"
And I press "Update profile"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "Plain text area"
And I press "Save changes"
And I click on "Edit profile" "link" in the "region-main" "region"
Then I should not see "<i>Pisa"
+29
View File
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_clear
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'atto_clear'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,46 @@
YUI.add('moodle-atto_clear-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_clear
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_clear-button
*/
/**
* Atto text editor clear plugin.
*
* @namespace M.atto_clear
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_clear').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addBasicButton({
exec: 'removeFormat',
icon: 'e/clear_formatting'
});
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
@@ -0,0 +1 @@
YUI.add("moodle-atto_clear-button",function(t,o){t.namespace("M.atto_clear").Button=t.Base.create("button",t.M.editor_atto.EditorPlugin,[],{initializer:function(){this.addBasicButton({exec:"removeFormat",icon:"e/clear_formatting"})}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
@@ -0,0 +1,46 @@
YUI.add('moodle-atto_clear-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_clear
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_clear-button
*/
/**
* Atto text editor clear plugin.
*
* @namespace M.atto_clear
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_clear').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addBasicButton({
exec: 'removeFormat',
icon: 'e/clear_formatting'
});
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
@@ -0,0 +1,10 @@
{
"name": "moodle-atto_clear-button",
"builds": {
"moodle-atto_clear-button": {
"jsfiles": [
"button.js"
]
}
}
}
@@ -0,0 +1,41 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_clear
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_clear-button
*/
/**
* Atto text editor clear plugin.
*
* @namespace M.atto_clear
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_clear').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addBasicButton({
exec: 'removeFormat',
icon: 'e/clear_formatting'
});
}
});
@@ -0,0 +1,7 @@
{
"moodle-atto_clear-button": {
"requires": [
"moodle-editor_atto-plugin"
]
}
}
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for block_activity_modules.
*
* @package atto_collapse
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace atto_collapse\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for atto_collapse implementing null_provider.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,32 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_collapse', language 'en'.
*
* @package atto_collapse
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Show/hide advanced buttons';
$string['privacy:metadata'] = 'The atto_collapse plugin does not store any personal data.';
$string['settings'] = 'Collapse toolbar settings';
$string['showfewer'] = 'Show fewer buttons';
$string['showgroups'] = 'Show first (n) groups when collapsed.';
$string['showgroups_desc'] = 'When the toolbar is collapsed (it is by default) only this many groups will be displayed at once.';
$string['showmore'] = 'Show more buttons';
$string['youareonsecondrow'] = 'You are now on another row of the editor\'s toolbar, where there are more buttons.';
+53
View File
@@ -0,0 +1,53 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_collapse
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Initialise the js strings required for this module.
*/
function atto_collapse_strings_for_js() {
global $PAGE;
$PAGE->requires->strings_for_js(array('showmore', 'showfewer', 'youareonsecondrow'), 'atto_collapse');
}
/**
* Set params for this plugin
* @param string $elementid
*/
function atto_collapse_params_for_js($elementid, $options, $fpoptions) {
// Pass the number of visible groups as a param.
$params = array('showgroups' => get_config('atto_collapse', 'showgroups'));
return $params;
}
/**
* Map icons for font-awesome themes.
*/
function atto_collapse_get_fontawesome_icon_map() {
return [
'atto_collapse:icon' => 'fa-level-down'
];
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
width="16"
height="16"
sodipodi:docname="icon.svg" preserveAspectRatio="xMinYMid meet">
<metadata
id="metadata8">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs6" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="640"
inkscape:window-height="480"
id="namedview4"
showgrid="true"
inkscape:zoom="14.75"
inkscape:cx="8"
inkscape:cy="8"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="0"
inkscape:current-layer="svg2">
<inkscape:grid
type="xygrid"
id="grid3033"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<g
id="g3027"
transform="translate(-9.9661016,-5.4237289)">
<path
style="fill:#ffffff"
d="m 10.966102,16.923729 0,-3.5 7,0 7,0 0,3.5 0,3.5 -7,0 -7,0 0,-3.5 z m 5,1.5 c 0,-0.55 -0.9,-1 -2,-1 -1.1,0 -2,0.45 -2,1 0,0.55 0.9,1 2,1 1.1,0 2,-0.45 2,-1 z m 3,0 c 0,-0.55 -0.45,-1 -1,-1 -0.55,0 -1,0.45 -1,1 0,0.55 0.45,1 1,1 0.55,0 1,-0.45 1,-1 z m 5,0 c 0,-0.55 -0.45,-1 -1,-1 -0.55,0 -1,0.45 -1,1 0,0.55 0.45,1 1,1 0.55,0 1,-0.45 1,-1 z m -10,-3 c 0,-0.55 -0.45,-1 -1,-1 -0.55,0 -1,0.45 -1,1 0,0.55 0.45,1 1,1 0.55,0 1,-0.45 1,-1 z m 3,0 c 0,-0.55 -0.45,-1 -1,-1 -0.55,0 -1,0.45 -1,1 0,0.55 0.45,1 1,1 0.55,0 1,-0.45 1,-1 z m 3,0 c 0,-0.55 -0.45,-1 -1,-1 -0.55,0 -1,0.45 -1,1 0,0.55 0.45,1 1,1 0.55,0 1,-0.45 1,-1 z m 4,0 c 0,-0.55 -0.675,-1 -1.5,-1 -0.825,0 -1.5,0.45 -1.5,1 0,0.55 0.675,1 1.5,1 0.825,0 1.5,-0.45 1.5,-1 z m -10.25,-3.732205 c 2.3375,-0.210535 6.1625,-0.210535 8.5,0 2.3375,0.210535 0.425,0.382791 -4.25,0.382791 -4.675,0 -6.5875,-0.172256 -4.25,-0.382791 z m -2.75,-3.2677951 c 0,-1.8095238 0.666666,-2 7,-2 6.333333,0 7,0.1904762 7,2 0,1.8095241 -0.666667,2.0000001 -7,2.0000001 -6.333334,0 -7,-0.190476 -7,-2.0000001 z m 3,0 c 0,-0.55 -0.45,-1 -1,-1 -0.55,0 -1,0.45 -1,1 0,0.55 0.45,1 1,1 0.55,0 1,-0.45 1,-1 z m 3,0 c 0,-0.55 -0.45,-1 -1,-1 -0.55,0 -1,0.45 -1,1 0,0.55 0.45,1 1,1 0.55,0 1,-0.45 1,-1 z m 3,0 c 0,-0.55 -0.45,-1 -1,-1 -0.55,0 -1,0.45 -1,1 0,0.55 0.45,1 1,1 0.55,0 1,-0.45 1,-1 z m 4,0 c 0,-0.55 -0.675,-1 -1.5,-1 -0.825,0 -1.5,0.45 -1.5,1 0,0.55 0.675,1 1.5,1 0.825,0 1.5,-0.45 1.5,-1 z"
id="path3031"
inkscape:connector-curvature="0" />
<path
style="fill:#888"
d="m 9.9661016,16.923729 0,-4.5 8.0000004,0 8,0 0,4.5 0,4.5 -8,0 -8.0000004,0 0,-4.5 z m 15.0000004,0 0,-3.5 -7,0 -7,0 0,3.5 0,3.5 7,0 7,0 0,-3.5 z m -13,1.5 c 0,-0.55 0.9,-1 2,-1 1.1,0 2,0.45 2,1 0,0.55 -0.9,1 -2,1 -1.1,0 -2,-0.45 -2,-1 z m 5,0 c 0,-0.55 0.45,-1 1,-1 0.55,0 1,0.45 1,1 0,0.55 -0.45,1 -1,1 -0.55,0 -1,-0.45 -1,-1 z m 5,0 c 0,-0.55 0.45,-1 1,-1 0.55,0 1,0.45 1,1 0,0.55 -0.45,1 -1,1 -0.55,0 -1,-0.45 -1,-1 z m -10,-3 c 0,-0.55 0.45,-1 1,-1 0.55,0 1,0.45 1,1 0,0.55 -0.45,1 -1,1 -0.55,0 -1,-0.45 -1,-1 z m 3,0 c 0,-0.55 0.45,-1 1,-1 0.55,0 1,0.45 1,1 0,0.55 -0.45,1 -1,1 -0.55,0 -1,-0.45 -1,-1 z m 3,0 c 0,-0.55 0.45,-1 1,-1 0.55,0 1,0.45 1,1 0,0.55 -0.45,1 -1,1 -0.55,0 -1,-0.45 -1,-1 z m 3,0 c 0,-0.55 0.675,-1 1.5,-1 0.825,0 1.5,0.45 1.5,1 0,0.55 -0.675,1 -1.5,1 -0.825,0 -1.5,-0.45 -1.5,-1 z M 9.9661016,8.4237289 l 0,-3 8.0000004,0 8,0 0,3 0,3.0000001 -8,0 -8.0000004,0 0,-3.0000001 z m 15.0000004,0 c 0,-1.8095238 -0.666667,-2 -7,-2 -6.333334,0 -7,0.1904762 -7,2 0,1.8095241 0.666666,2.0000001 7,2.0000001 6.333333,0 7,-0.190476 7,-2.0000001 z m -13,0 c 0,-0.55 0.45,-1 1,-1 0.55,0 1,0.45 1,1 0,0.55 -0.45,1 -1,1 -0.55,0 -1,-0.45 -1,-1 z m 3,0 c 0,-0.55 0.45,-1 1,-1 0.55,0 1,0.45 1,1 0,0.55 -0.45,1 -1,1 -0.55,0 -1,-0.45 -1,-1 z m 3,0 c 0,-0.55 0.45,-1 1,-1 0.55,0 1,0.45 1,1 0,0.55 -0.45,1 -1,1 -0.55,0 -1,-0.45 -1,-1 z m 3,0 c 0,-0.55 0.675,-1 1.5,-1 0.825,0 1.5,0.45 1.5,1 0,0.55 -0.675,1 -1.5,1 -0.825,0 -1.5,-0.45 -1.5,-1 z"
id="path3029"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

@@ -0,0 +1,43 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Settings that allow configuration of the list of tex examples in the equation editor.
*
* @package atto_collapse
* @copyright 2013 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$ADMIN->add('editoratto', new admin_category('atto_collapse', new lang_string('pluginname', 'atto_collapse')));
$settings = new admin_settingpage('atto_collapse_settings', new lang_string('settings', 'atto_collapse'));
if ($ADMIN->fulltree) {
// Number of groups to show when collapsed.
$name = new lang_string('showgroups', 'atto_collapse');
$desc = new lang_string('showgroups_desc', 'atto_collapse');
$default = 6;
$options = array_combine(range(1, 20), range(1, 20));
$setting = new admin_setting_configselect('atto_collapse/showgroups',
$name,
$desc,
$default,
$options);
$settings->add($setting);
}
@@ -0,0 +1,5 @@
@media (max-width: 768px) {
.toolbarbreak {
display: none;
}
}
@@ -0,0 +1,12 @@
@editor @editor_atto @atto @atto_collapse
Feature: Atto collapse button
To access all the tools in Atto, I need to toggle the toolbar
@javascript
Scenario: Toggle toolbar
Given I log in as "admin"
And I open my profile in edit mode
When I click on "Show more buttons" "button"
Then "Equation editor" "button" should be visible
And I click on "Show fewer buttons" "button"
Then "Equation editor" "button" should not be visible
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_collapse
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'atto_collapse'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,153 @@
YUI.add('moodle-atto_collapse-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_collapse
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_collapse-button
*/
/**
* Atto text editor collapse plugin.
*
* @namespace M.atto_collapse
* @class button
* @extends M.editor_atto.EditorPlugin
*/
var PLUGINNAME = 'atto_collapse',
ATTRSHOWGROUPS = 'showgroups',
COLLAPSE = 'collapse',
COLLAPSED = 'collapsed',
GROUPS = '.atto_group',
ROWS = '.atto_toolbar_row';
Y.namespace('M.atto_collapse').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
var toolbarGroupCount = Y.Object.size(this.get('host').get('plugins'));
if (toolbarGroupCount <= 1 + parseInt(this.get(ATTRSHOWGROUPS), 10)) {
Y.log("There are not enough groups to require toggling - not adding the button",
'debug', 'moodle-atto_collapse');
return;
}
if (this.toolbar.all(GROUPS).size() > this.get(ATTRSHOWGROUPS)) {
Y.log("The collapse plugin is shown after it's cut-off - not adding the button",
'debug', 'moodle-atto_collapse');
return;
}
var button = this.addButton({
icon: 'icon',
iconComponent: PLUGINNAME,
callback: this._toggle
});
// Perform a toggle after all plugins have been loaded for the first time.
this.get('host').on('pluginsloaded', function(e, button) {
// Add 2 rows in the toolbar.
var toolbarRows = [
Y.Node.create('<div class="atto_toolbar_row" role="group"></div>'),
Y.Node.create('<div class="atto_toolbar_row" role="group" aria-label="' +
M.util.get_string('youareonsecondrow', PLUGINNAME) +
'" tabindex="-1"></div>'),
];
this.toolbar.appendChild(toolbarRows[0]).insert(toolbarRows[1], 'after');
// Split toolbar buttons between the 2 rows created above.
var buttonGroups = this.toolbar.all(GROUPS);
buttonGroups.slice(0, this.get(ATTRSHOWGROUPS)).each(function(buttonGroup) {
toolbarRows[0].appendChild(buttonGroup);
});
buttonGroups.slice(this.get(ATTRSHOWGROUPS)).each(function(buttonGroup) {
toolbarRows[1].appendChild(buttonGroup);
});
this._setVisibility(button);
button.setAttribute('aria-expanded', 'false');
}, this, button);
},
/**
* Toggle the visibility of the extra groups in the toolbar.
*
* @method _toggle
* @param {EventFacade} e
* @private
*/
_toggle: function(e) {
e.preventDefault();
var button = this.buttons[COLLAPSE];
if (button.getData(COLLAPSED)) {
this.highlightButtons(COLLAPSE);
this._setVisibility(button, true);
this.toolbar.all(ROWS).item(1).focus();
} else {
this.unHighlightButtons(COLLAPSE);
this._setVisibility(button);
this.buttons[this.name].focus();
}
},
/**
* Set the visibility of the toolbar groups.
*
* @method _setVisibility
* @param {Node} button The collapse button
* @param {Booelan} visibility Whether the groups should be made visible
* @private
*/
_setVisibility: function(button, visibility) {
var secondaryRow = this.toolbar.all(ROWS).item(1);
if (visibility) {
button.set('title', M.util.get_string('showfewer', PLUGINNAME));
secondaryRow.show();
button.setData(COLLAPSED, false);
button.setAttribute('aria-expanded', 'true');
} else {
button.set('title', M.util.get_string('showmore', PLUGINNAME));
secondaryRow.hide();
button.setData(COLLAPSED, true);
button.setAttribute('aria-expanded', 'false');
}
// We don't want to have both aria-pressed and aria-expanded set. So we remove aria-pressed here.
button.removeAttribute('aria-pressed');
}
}, {
ATTRS: {
/**
* How many groups to show when collapsed.
*
* @attribute showgroups
* @type Number
* @default 3
*/
showgroups: {
value: 3
}
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
@@ -0,0 +1 @@
YUI.add("moodle-atto_collapse-button",function(s,t){var a="atto_collapse",l="showgroups",e="collapse",o="collapsed",r=".atto_group",n=".atto_toolbar_row";s.namespace("M.atto_collapse").Button=s.Base.create("button",s.M.editor_atto.EditorPlugin,[],{initializer:function(){var t=s.Object.size(this.get("host").get("plugins"));t<=1+parseInt(this.get(l),10)||this.toolbar.all(r).size()>this.get(l)||(t=this.addButton({icon:"icon",iconComponent:a,callback:this._toggle}),this.get("host").on("pluginsloaded",function(t,e){var i,o=[s.Node.create('<div class="atto_toolbar_row" role="group"></div>'),s.Node.create('<div class="atto_toolbar_row" role="group" aria-label="'+M.util.get_string("youareonsecondrow",a)+'" tabindex="-1"></div>')];this.toolbar.appendChild(o[0]).insert(o[1],"after"),(i=this.toolbar.all(r)).slice(0,this.get(l)).each(function(t){o[0].appendChild(t)}),i.slice(this.get(l)).each(function(t){o[1].appendChild(t)}),this._setVisibility(e),e.setAttribute("aria-expanded","false")},this,t))},_toggle:function(t){t.preventDefault();t=this.buttons[e];t.getData(o)?(this.highlightButtons(e),this._setVisibility(t,!0),this.toolbar.all(n).item(1).focus()):(this.unHighlightButtons(e),this._setVisibility(t),this.buttons[this.name].focus())},_setVisibility:function(t,e){var i=this.toolbar.all(n).item(1);e?(t.set("title",M.util.get_string("showfewer",a)),i.show(),t.setData(o,!1),t.setAttribute("aria-expanded","true")):(t.set("title",M.util.get_string("showmore",a)),i.hide(),t.setData(o,!0),t.setAttribute("aria-expanded","false")),t.removeAttribute("aria-pressed")}},{ATTRS:{showgroups:{value:3}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
@@ -0,0 +1,149 @@
YUI.add('moodle-atto_collapse-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_collapse
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_collapse-button
*/
/**
* Atto text editor collapse plugin.
*
* @namespace M.atto_collapse
* @class button
* @extends M.editor_atto.EditorPlugin
*/
var PLUGINNAME = 'atto_collapse',
ATTRSHOWGROUPS = 'showgroups',
COLLAPSE = 'collapse',
COLLAPSED = 'collapsed',
GROUPS = '.atto_group',
ROWS = '.atto_toolbar_row';
Y.namespace('M.atto_collapse').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
var toolbarGroupCount = Y.Object.size(this.get('host').get('plugins'));
if (toolbarGroupCount <= 1 + parseInt(this.get(ATTRSHOWGROUPS), 10)) {
return;
}
if (this.toolbar.all(GROUPS).size() > this.get(ATTRSHOWGROUPS)) {
return;
}
var button = this.addButton({
icon: 'icon',
iconComponent: PLUGINNAME,
callback: this._toggle
});
// Perform a toggle after all plugins have been loaded for the first time.
this.get('host').on('pluginsloaded', function(e, button) {
// Add 2 rows in the toolbar.
var toolbarRows = [
Y.Node.create('<div class="atto_toolbar_row" role="group"></div>'),
Y.Node.create('<div class="atto_toolbar_row" role="group" aria-label="' +
M.util.get_string('youareonsecondrow', PLUGINNAME) +
'" tabindex="-1"></div>'),
];
this.toolbar.appendChild(toolbarRows[0]).insert(toolbarRows[1], 'after');
// Split toolbar buttons between the 2 rows created above.
var buttonGroups = this.toolbar.all(GROUPS);
buttonGroups.slice(0, this.get(ATTRSHOWGROUPS)).each(function(buttonGroup) {
toolbarRows[0].appendChild(buttonGroup);
});
buttonGroups.slice(this.get(ATTRSHOWGROUPS)).each(function(buttonGroup) {
toolbarRows[1].appendChild(buttonGroup);
});
this._setVisibility(button);
button.setAttribute('aria-expanded', 'false');
}, this, button);
},
/**
* Toggle the visibility of the extra groups in the toolbar.
*
* @method _toggle
* @param {EventFacade} e
* @private
*/
_toggle: function(e) {
e.preventDefault();
var button = this.buttons[COLLAPSE];
if (button.getData(COLLAPSED)) {
this.highlightButtons(COLLAPSE);
this._setVisibility(button, true);
this.toolbar.all(ROWS).item(1).focus();
} else {
this.unHighlightButtons(COLLAPSE);
this._setVisibility(button);
this.buttons[this.name].focus();
}
},
/**
* Set the visibility of the toolbar groups.
*
* @method _setVisibility
* @param {Node} button The collapse button
* @param {Booelan} visibility Whether the groups should be made visible
* @private
*/
_setVisibility: function(button, visibility) {
var secondaryRow = this.toolbar.all(ROWS).item(1);
if (visibility) {
button.set('title', M.util.get_string('showfewer', PLUGINNAME));
secondaryRow.show();
button.setData(COLLAPSED, false);
button.setAttribute('aria-expanded', 'true');
} else {
button.set('title', M.util.get_string('showmore', PLUGINNAME));
secondaryRow.hide();
button.setData(COLLAPSED, true);
button.setAttribute('aria-expanded', 'false');
}
// We don't want to have both aria-pressed and aria-expanded set. So we remove aria-pressed here.
button.removeAttribute('aria-pressed');
}
}, {
ATTRS: {
/**
* How many groups to show when collapsed.
*
* @attribute showgroups
* @type Number
* @default 3
*/
showgroups: {
value: 3
}
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
@@ -0,0 +1,10 @@
{
"name": "moodle-atto_collapse-button",
"builds": {
"moodle-atto_collapse-button": {
"jsfiles": [
"button.js"
]
}
}
}
@@ -0,0 +1,148 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_collapse
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_collapse-button
*/
/**
* Atto text editor collapse plugin.
*
* @namespace M.atto_collapse
* @class button
* @extends M.editor_atto.EditorPlugin
*/
var PLUGINNAME = 'atto_collapse',
ATTRSHOWGROUPS = 'showgroups',
COLLAPSE = 'collapse',
COLLAPSED = 'collapsed',
GROUPS = '.atto_group',
ROWS = '.atto_toolbar_row';
Y.namespace('M.atto_collapse').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
var toolbarGroupCount = Y.Object.size(this.get('host').get('plugins'));
if (toolbarGroupCount <= 1 + parseInt(this.get(ATTRSHOWGROUPS), 10)) {
Y.log("There are not enough groups to require toggling - not adding the button",
'debug', 'moodle-atto_collapse');
return;
}
if (this.toolbar.all(GROUPS).size() > this.get(ATTRSHOWGROUPS)) {
Y.log("The collapse plugin is shown after it's cut-off - not adding the button",
'debug', 'moodle-atto_collapse');
return;
}
var button = this.addButton({
icon: 'icon',
iconComponent: PLUGINNAME,
callback: this._toggle
});
// Perform a toggle after all plugins have been loaded for the first time.
this.get('host').on('pluginsloaded', function(e, button) {
// Add 2 rows in the toolbar.
var toolbarRows = [
Y.Node.create('<div class="atto_toolbar_row" role="group"></div>'),
Y.Node.create('<div class="atto_toolbar_row" role="group" aria-label="' +
M.util.get_string('youareonsecondrow', PLUGINNAME) +
'" tabindex="-1"></div>'),
];
this.toolbar.appendChild(toolbarRows[0]).insert(toolbarRows[1], 'after');
// Split toolbar buttons between the 2 rows created above.
var buttonGroups = this.toolbar.all(GROUPS);
buttonGroups.slice(0, this.get(ATTRSHOWGROUPS)).each(function(buttonGroup) {
toolbarRows[0].appendChild(buttonGroup);
});
buttonGroups.slice(this.get(ATTRSHOWGROUPS)).each(function(buttonGroup) {
toolbarRows[1].appendChild(buttonGroup);
});
this._setVisibility(button);
button.setAttribute('aria-expanded', 'false');
}, this, button);
},
/**
* Toggle the visibility of the extra groups in the toolbar.
*
* @method _toggle
* @param {EventFacade} e
* @private
*/
_toggle: function(e) {
e.preventDefault();
var button = this.buttons[COLLAPSE];
if (button.getData(COLLAPSED)) {
this.highlightButtons(COLLAPSE);
this._setVisibility(button, true);
this.toolbar.all(ROWS).item(1).focus();
} else {
this.unHighlightButtons(COLLAPSE);
this._setVisibility(button);
this.buttons[this.name].focus();
}
},
/**
* Set the visibility of the toolbar groups.
*
* @method _setVisibility
* @param {Node} button The collapse button
* @param {Booelan} visibility Whether the groups should be made visible
* @private
*/
_setVisibility: function(button, visibility) {
var secondaryRow = this.toolbar.all(ROWS).item(1);
if (visibility) {
button.set('title', M.util.get_string('showfewer', PLUGINNAME));
secondaryRow.show();
button.setData(COLLAPSED, false);
button.setAttribute('aria-expanded', 'true');
} else {
button.set('title', M.util.get_string('showmore', PLUGINNAME));
secondaryRow.hide();
button.setData(COLLAPSED, true);
button.setAttribute('aria-expanded', 'false');
}
// We don't want to have both aria-pressed and aria-expanded set. So we remove aria-pressed here.
button.removeAttribute('aria-pressed');
}
}, {
ATTRS: {
/**
* How many groups to show when collapsed.
*
* @attribute showgroups
* @type Number
* @default 3
*/
showgroups: {
value: 3
}
}
});
@@ -0,0 +1,7 @@
{
"moodle-atto_collapse-button": {
"requires": [
"moodle-editor_atto-plugin"
]
}
}
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for atto_emojipicker.
*
* @package atto_emojipicker
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace atto_emojipicker\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for atto_emojipicker implementing null_provider.
*
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,27 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_emojipicker', language 'en'.
*
* @package atto_emojipicker
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['emojipicker'] = 'Emoji picker';
$string['pluginname'] = 'Emoji picker';
$string['privacy:metadata'] = 'The atto_emojipicker plugin does not store any personal data.';
@@ -0,0 +1,48 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor emoji picker plugin lib.
*
* @package atto_emojipicker
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Initialise the strings required for JS.
*
* @return void
*/
function atto_emojipicker_strings_for_js() {
global $PAGE;
$PAGE->requires->strings_for_js(['emojipicker'], 'atto_emojipicker');
}
/**
* Sends the parameters to JS module.
*
* @return array
*/
function atto_emojipicker_params_for_js() {
global $CFG;
return [
'disabled' => empty($CFG->allowemojipicker) ? true : false
];
}
@@ -0,0 +1,8 @@
.emoji-picker-dialogue.moodle-dialogue-base .moodle-dialogue .moodle-dialogue-bd {
padding: 0;
}
.emoji-picker-dialogue .emoji-picker {
box-shadow: none !important; /* stylelint-disable-line declaration-no-important */
border: 0;
}
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor emoji picker plugin version file.
*
* @package atto_emojipicker
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'atto_emojipicker'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,143 @@
YUI.add('moodle-atto_emojipicker-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_emojipicker
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_emojipicker-button
*/
var COMPONENTNAME = 'atto_emojipicker',
EMOJI_PICKER_SEARCH_INPUT = '[data-region="search-input"]';
/**
* Atto text editor emoji picker plugin.
*
* @namespace M.atto_emojipicker
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_emojipicker').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
/**
* A reference to the current selection at the time that the dialogue
* was opened.
*
* @property _currentSelection
* @type Range
* @private
*/
_currentSelection: null,
initializer: function() {
if (this.get('disabled')) {
return;
}
this.addButton({
icon: 'e/emoticons',
callback: this._displayDialogue
});
},
/**
* Display the emoji picker.
*
* @method _displayDialogue
* @private
*/
_displayDialogue: function() {
// Store the current selection.
this._currentSelection = this.get('host').getSelection();
if (this._currentSelection === false) {
return;
}
var dialogue = this.getDialogue({
headerContent: M.util.get_string('emojipicker', COMPONENTNAME),
width: 'auto',
focusAfterHide: true,
additionalBaseClass: 'emoji-picker-dialogue'
}, true);
// Set the dialogue content, and then show the dialogue.
dialogue.set('bodyContent', this._getDialogueContent())
.show();
},
/**
* Insert the emoticon.
*
* @method _insertEmote
* @param {String} emoji
* @private
*/
_insertEmoji: function(emoji) {
var host = this.get('host');
// Hide the dialogue.
this.getDialogue({
focusAfterHide: null
}).hide();
// Focus on the previous selection.
host.setSelection(this._currentSelection);
// And add the character.
host.insertContentAtFocusPoint(emoji);
this.markUpdated();
},
/**
* Generates the content of the dialogue, attaching event listeners to
* the content.
*
* @method _getDialogueContent
* @return {Node} Node containing the dialogue content
* @private
*/
_getDialogueContent: function() {
var wrapper = Y.Node.create('<div></div>');
require(['core/templates', 'core/emoji/picker'], function(Templates, initialiseEmojiPicker) {
Templates.render('core/emoji/picker', {}).then(function(html) {
var domNode = wrapper.getDOMNode();
domNode.innerHTML = html;
initialiseEmojiPicker(domNode, this._insertEmoji.bind(this));
this.getDialogue().centerDialogue();
domNode.querySelector(EMOJI_PICKER_SEARCH_INPUT).focus();
}.bind(this));
}.bind(this));
return wrapper;
}
}, {
ATTRS: {
disabled: {
value: true
}
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
@@ -0,0 +1 @@
YUI.add("moodle-atto_emojipicker-button",function(e,t){e.namespace("M.atto_emojipicker").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,initializer:function(){this.get("disabled")||this.addButton({icon:"e/emoticons",callback:this._displayDialogue})},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection(),!1!==this._currentSelection&&this.getDialogue({headerContent:M.util.get_string("emojipicker","atto_emojipicker"),width:"auto",focusAfterHide:!0,additionalBaseClass:"emoji-picker-dialogue"},!0).set("bodyContent",this._getDialogueContent()).show()},_insertEmoji:function(e){var t=this.get("host");this.getDialogue({focusAfterHide:null}).hide(),t.setSelection(this._currentSelection),t.insertContentAtFocusPoint(e),this.markUpdated()},_getDialogueContent:function(){var o=e.Node.create("<div></div>");return require(["core/templates","core/emoji/picker"],function(e,i){e.render("core/emoji/picker",{}).then(function(e){var t=o.getDOMNode();t.innerHTML=e,i(t,this._insertEmoji.bind(this)),this.getDialogue().centerDialogue(),t.querySelector('[data-region="search-input"]').focus()}.bind(this))}.bind(this)),o}},{ATTRS:{disabled:{value:!0}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
@@ -0,0 +1,143 @@
YUI.add('moodle-atto_emojipicker-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/*
* @package atto_emojipicker
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_emojipicker-button
*/
var COMPONENTNAME = 'atto_emojipicker',
EMOJI_PICKER_SEARCH_INPUT = '[data-region="search-input"]';
/**
* Atto text editor emoji picker plugin.
*
* @namespace M.atto_emojipicker
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_emojipicker').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
/**
* A reference to the current selection at the time that the dialogue
* was opened.
*
* @property _currentSelection
* @type Range
* @private
*/
_currentSelection: null,
initializer: function() {
if (this.get('disabled')) {
return;
}
this.addButton({
icon: 'e/emoticons',
callback: this._displayDialogue
});
},
/**
* Display the emoji picker.
*
* @method _displayDialogue
* @private
*/
_displayDialogue: function() {
// Store the current selection.
this._currentSelection = this.get('host').getSelection();
if (this._currentSelection === false) {
return;
}
var dialogue = this.getDialogue({
headerContent: M.util.get_string('emojipicker', COMPONENTNAME),
width: 'auto',
focusAfterHide: true,
additionalBaseClass: 'emoji-picker-dialogue'
}, true);
// Set the dialogue content, and then show the dialogue.
dialogue.set('bodyContent', this._getDialogueContent())
.show();
},
/**
* Insert the emoticon.
*
* @method _insertEmote
* @param {String} emoji
* @private
*/
_insertEmoji: function(emoji) {
var host = this.get('host');
// Hide the dialogue.
this.getDialogue({
focusAfterHide: null
}).hide();
// Focus on the previous selection.
host.setSelection(this._currentSelection);
// And add the character.
host.insertContentAtFocusPoint(emoji);
this.markUpdated();
},
/**
* Generates the content of the dialogue, attaching event listeners to
* the content.
*
* @method _getDialogueContent
* @return {Node} Node containing the dialogue content
* @private
*/
_getDialogueContent: function() {
var wrapper = Y.Node.create('<div></div>');
require(['core/templates', 'core/emoji/picker'], function(Templates, initialiseEmojiPicker) {
Templates.render('core/emoji/picker', {}).then(function(html) {
var domNode = wrapper.getDOMNode();
domNode.innerHTML = html;
initialiseEmojiPicker(domNode, this._insertEmoji.bind(this));
this.getDialogue().centerDialogue();
domNode.querySelector(EMOJI_PICKER_SEARCH_INPUT).focus();
}.bind(this));
}.bind(this));
return wrapper;
}
}, {
ATTRS: {
disabled: {
value: true
}
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});

Some files were not shown because too many files have changed in this diff Show More