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
+11
View File
@@ -0,0 +1,11 @@
define("core_editor/events",["exports","core/event_dispatcher","jquery","core/yui"],(function(_exports,_event_dispatcher,_jquery,_yui){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Javascript events for the `core_editor` subsystem.
*
* @module core_editor/events
* @copyright 2021 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 4.0
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.notifyEditorContentRestored=_exports.eventTypes=void 0,_jquery=_interopRequireDefault(_jquery),_yui=_interopRequireDefault(_yui);const eventTypes={editorContentRestored:"core_editor/contentRestored"};_exports.eventTypes=eventTypes;_exports.notifyEditorContentRestored=editor=>(editor||window.console.warn("The HTMLElement representing the editor that was modified should be provided to notifyEditorContentRestored."),(0,_event_dispatcher.dispatchEvent)(eventTypes.editorContentRestored,{},editor||document));let legacyEventsRegistered=!1;legacyEventsRegistered||(_yui.default.use("event","moodle-core-event",(()=>{document.addEventListener(eventTypes.editorContentRestored,(()=>{(0,_jquery.default)(document).trigger(M.core.event.EDITOR_CONTENT_RESTORED),_yui.default.fire(M.core.event.EDITOR_CONTENT_RESTORED)}))})),legacyEventsRegistered=!0)}));
//# sourceMappingURL=events.min.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"events.min.js","sources":["../src/events.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/ //\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript events for the `core_editor` subsystem.\n *\n * @module core_editor/events\n * @copyright 2021 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport jQuery from 'jquery';\nimport Y from 'core/yui';\n\n/**\n * Events for the `core_editor` subsystem.\n *\n * @constant\n * @property {String} editorContentRestored See {@link event:editorContentRestored}\n */\nexport const eventTypes = {\n /**\n * An event triggered when an editor restores auto-saved content.\n *\n * @event editorContentRestored\n */\n editorContentRestored: 'core_editor/contentRestored',\n};\n\n/**\n * Trigger an event to indicate that editor content was restored.\n *\n * @method notifyEditorContentRestored\n * @param {HTMLElement|null} editor The element that was modified\n * @returns {CustomEvent}\n * @fires editorContentRestored\n */\nexport const notifyEditorContentRestored = editor => {\n if (!editor) {\n window.console.warn(\n `The HTMLElement representing the editor that was modified should be provided to notifyEditorContentRestored.`\n );\n }\n return dispatchEvent(\n eventTypes.editorContentRestored,\n {},\n editor || document\n );\n};\n\nlet legacyEventsRegistered = false;\nif (!legacyEventsRegistered) {\n // The following event triggers are legacy and will be removed in the future.\n // The following approach provides a backwards-compatability layer for the new events.\n // Code should be updated to make use of native events.\n\n Y.use('event', 'moodle-core-event', () => {\n // Provide a backwards-compatability layer for YUI Events.\n document.addEventListener(eventTypes.editorContentRestored, () => {\n // Trigger a legacy AMD event.\n jQuery(document).trigger(M.core.event.EDITOR_CONTENT_RESTORED);\n\n // Trigger a legacy YUI event.\n Y.fire(M.core.event.EDITOR_CONTENT_RESTORED);\n });\n });\n\n legacyEventsRegistered = true;\n}\n"],"names":["eventTypes","editorContentRestored","editor","window","console","warn","document","legacyEventsRegistered","use","addEventListener","trigger","M","core","event","EDITOR_CONTENT_RESTORED","fire"],"mappings":";;;;;;;;6MAiCaA,WAAa,CAMtBC,sBAAuB,mGAWgBC,SAClCA,QACDC,OAAOC,QAAQC,sHAIZ,mCACHL,WAAWC,sBACX,GACAC,QAAUI,eAIdC,wBAAyB,EACxBA,sCAKCC,IAAI,QAAS,qBAAqB,KAEhCF,SAASG,iBAAiBT,WAAWC,uBAAuB,yBAEjDK,UAAUI,QAAQC,EAAEC,KAAKC,MAAMC,sCAGpCC,KAAKJ,EAAEC,KAAKC,MAAMC,+BAI5BP,wBAAyB"}
+82
View File
@@ -0,0 +1,82 @@
// 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/>.
/**
* Javascript events for the `core_editor` subsystem.
*
* @module core_editor/events
* @copyright 2021 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 4.0
*/
import {dispatchEvent} from 'core/event_dispatcher';
import jQuery from 'jquery';
import Y from 'core/yui';
/**
* Events for the `core_editor` subsystem.
*
* @constant
* @property {String} editorContentRestored See {@link event:editorContentRestored}
*/
export const eventTypes = {
/**
* An event triggered when an editor restores auto-saved content.
*
* @event editorContentRestored
*/
editorContentRestored: 'core_editor/contentRestored',
};
/**
* Trigger an event to indicate that editor content was restored.
*
* @method notifyEditorContentRestored
* @param {HTMLElement|null} editor The element that was modified
* @returns {CustomEvent}
* @fires editorContentRestored
*/
export const notifyEditorContentRestored = editor => {
if (!editor) {
window.console.warn(
`The HTMLElement representing the editor that was modified should be provided to notifyEditorContentRestored.`
);
}
return dispatchEvent(
eventTypes.editorContentRestored,
{},
editor || document
);
};
let legacyEventsRegistered = false;
if (!legacyEventsRegistered) {
// The following event triggers are legacy and will be removed in the future.
// The following approach provides a backwards-compatability layer for the new events.
// Code should be updated to make use of native events.
Y.use('event', 'moodle-core-event', () => {
// Provide a backwards-compatability layer for YUI Events.
document.addEventListener(eventTypes.editorContentRestored, () => {
// Trigger a legacy AMD event.
jQuery(document).trigger(M.core.event.EDITOR_CONTENT_RESTORED);
// Trigger a legacy YUI event.
Y.fire(M.core.event.EDITOR_CONTENT_RESTORED);
});
});
legacyEventsRegistered = true;
}
+252
View File
@@ -0,0 +1,252 @@
<?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 admin setting stuff.
*
* @package editor_atto
* @copyright 2014 Jerome Mouneyrac
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Admin setting for toolbar.
*
* @package editor_atto
* @copyright 2014 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class editor_atto_toolbar_setting extends admin_setting_configtextarea {
/**
* Validate data.
*
* This ensures that:
* - Plugins are only used once,
* - Group names are unique,
* - Lines match: group = plugin[, plugin[, plugin ...]],
* - There are some groups and plugins defined,
* - The plugins used are installed.
*
* @param string $data
* @return mixed True on success, else error message.
*/
public function validate($data) {
$result = parent::validate($data);
if ($result !== true) {
return $result;
}
$lines = explode("\n", $data);
$groups = array();
$plugins = array();
foreach ($lines as $line) {
if (!trim($line)) {
continue;
}
$matches = array();
if (!preg_match('/^\s*([a-z0-9]+)\s*=\s*([a-z0-9]+(\s*,\s*[a-z0-9]+)*)+\s*$/', $line, $matches)) {
$result = get_string('errorcannotparseline', 'editor_atto', $line);
break;
}
$group = $matches[1];
if (isset($groups[$group])) {
$result = get_string('errorgroupisusedtwice', 'editor_atto', $group);
break;
}
$groups[$group] = true;
$lineplugins = array_map('trim', explode(',', $matches[2]));
foreach ($lineplugins as $plugin) {
if (isset($plugins[$plugin])) {
$result = get_string('errorpluginisusedtwice', 'editor_atto', $plugin);
break 2;
} else if (!core_component::get_component_directory('atto_' . $plugin)) {
$result = get_string('errorpluginnotfound', 'editor_atto', $plugin);
break 2;
}
$plugins[$plugin] = true;
}
}
// We did not find any groups or plugins.
if (empty($groups) || empty($plugins)) {
$result = get_string('errornopluginsorgroupsfound', 'editor_atto');
}
return $result;
}
}
/**
* Special class for Atto plugins administration.
*
* @package editor_atto
* @copyright 2014 Jerome Mouneyrac
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class editor_atto_subplugins_setting extends admin_setting {
/**
* Constructor.
*/
public function __construct() {
$this->nosave = true;
parent::__construct('attosubplugins', get_string('subplugintype_atto_plural', 'editor_atto'), '', '');
}
/**
* Returns current value of this setting.
* Always returns true, does nothing.
*
* @return true
*/
public function get_setting() {
return true;
}
/**
* Returns default setting if exists.
* Always returns true, does nothing.
*
* @return true
*/
public function get_defaultsetting() {
return true;
}
/**
* Store new setting.
* Always returns '', does not write anything.
*
* @param string $data string or array, must not be NULL.
* @return string Always returns ''.
*/
public function write_setting($data) {
// Do not write any setting.
return '';
}
/**
* Checks if $query is one of the available subplugins.
*
* @param string $query The string to search for.
* @return bool Returns true if found, false if not.
*/
public function is_related($query) {
if (parent::is_related($query)) {
return true;
}
$subplugins = core_component::get_plugin_list('atto');
foreach ($subplugins as $name => $dir) {
if (stripos($name, $query) !== false) {
return true;
}
$namestr = get_string('pluginname', 'atto_' . $name);
if (strpos(core_text::strtolower($namestr), core_text::strtolower($query)) !== false) {
return true;
}
}
return false;
}
/**
* Builds the XHTML to display the control.
*
* @param mixed $data Unused.
* @param string $query
* @return string highlight.
*/
public function output_html($data, $query = '') {
global $CFG, $OUTPUT, $PAGE;
require_once($CFG->libdir . "/editorlib.php");
require_once(__DIR__ . '/lib.php');
$pluginmanager = core_plugin_manager::instance();
// Display strings.
$strtoolbarconfig = get_string('toolbarconfig', 'editor_atto');
$strname = get_string('name');
$strsettings = get_string('settings');
$struninstall = get_string('uninstallplugin', 'core_admin');
$strversion = get_string('version');
$subplugins = core_component::get_plugin_list('atto');
$return = $OUTPUT->heading(get_string('subplugintype_atto_plural', 'editor_atto'), 3, 'main', true);
$return .= $OUTPUT->box_start('generalbox attosubplugins');
$table = new html_table();
$table->head = array($strname, $strversion, $strtoolbarconfig, $strsettings, $struninstall);
$table->align = array('left', 'left', 'center', 'center', 'center', 'center');
$table->data = array();
$table->attributes['class'] = 'admintable generaltable';
// Iterate through subplugins.
foreach ($subplugins as $name => $dir) {
$namestr = get_string('pluginname', 'atto_' . $name);
$version = get_config('atto_' . $name, 'version');
if ($version === false) {
$version = '';
}
$plugininfo = $pluginmanager->get_plugin_info('atto_' . $name);
$toolbarconfig = $name;
$displayname = $namestr;
// Check if there is an icon in the atto plugin pix/ folder.
if ($PAGE->theme->resolve_image_location('icon', 'atto_' . $name, false)) {
$icon = $OUTPUT->pix_icon('icon', '', 'atto_' . $name, array('class' => 'icon pluginicon'));
} else {
// No icon found.
$icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
}
$displayname = $icon . $displayname;
// Add settings link.
if (!$version) {
$settings = '';
} else if ($url = $plugininfo->get_settings_url()) {
$settings = html_writer::link($url, $strsettings);
} else {
$settings = '';
}
// Add uninstall info.
$uninstall = '';
if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('atto_' . $name, 'manage')) {
$uninstall = html_writer::link($uninstallurl, $struninstall);
}
// Add a row to the table.
$row = new html_table_row(array($displayname, $version, $toolbarconfig, $settings, $uninstall));
$table->data[] = $row;
}
$return .= html_writer::table($table);
$return .= html_writer::tag('p', get_string('tablenosave', 'admin'));
$return .= $OUTPUT->box_end();
return highlight($query, $return);
}
}
+12
View File
@@ -0,0 +1,12 @@
define("editor_atto/events",["exports","core/event_dispatcher"],(function(_exports,_event_dispatcher){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.notifyButtonHighlightToggled=_exports.eventTypes=void 0;
/**
* Javascript events for the `editor_atto` plugin.
*
* @module editor_atto/events
* @copyright 2021 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.10.5
*/
const eventTypes={attoButtonHighlightToggled:"editor_atto/attoButtonHighlightToggled"};_exports.eventTypes=eventTypes;_exports.notifyButtonHighlightToggled=(attoButton,buttonName,highlight)=>(0,_event_dispatcher.dispatchEvent)(eventTypes.attoButtonHighlightToggled,{buttonName:buttonName,highlight:highlight},attoButton)}));
//# sourceMappingURL=events.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"events.min.js","sources":["../src/events.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/ //\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript events for the `editor_atto` plugin.\n *\n * @module editor_atto/events\n * @copyright 2021 Jun Pataleta <jun@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.10.5\n */\n\nimport {dispatchEvent} from 'core/event_dispatcher';\n\n/**\n * Events for the `editor_atto` plugin.\n *\n * @constant\n * @property {String} attoButtonHighlightToggled See {@link event:attoButtonHighlightToggled}\n */\nexport const eventTypes = {\n /**\n * An event triggered when a toolbar button's highlight gets toggled.\n *\n * @event attoButtonHighlightToggled\n * @type {CustomEvent}\n * @property {HTMLElement} target The button which had its highlight toggled.\n * @property {object} detail\n * @property {String} detail.buttonName The name of the Atto button that has had its highlight toggled.\n * @property {Boolean} detail.highlight True when the button was highlighted. False, otherwise.\n */\n attoButtonHighlightToggled: 'editor_atto/attoButtonHighlightToggled',\n};\n\n/**\n * Trigger an event to indicate that a button's highlight was toggled.\n *\n * @method notifyButtonHighlightToggled\n * @returns {CustomEvent}\n * @fires attoButtonHighlightToggled\n * @param {HTMLElement} attoButton The button object.\n * @param {String} buttonName The button name.\n * @param {Boolean} highlight True when the button was highlighted. False, otherwise.\n */\nexport const notifyButtonHighlightToggled = (attoButton, buttonName, highlight) => {\n return dispatchEvent(\n eventTypes.attoButtonHighlightToggled,\n {\n buttonName,\n highlight,\n },\n attoButton\n );\n};\n"],"names":["eventTypes","attoButtonHighlightToggled","attoButton","buttonName","highlight"],"mappings":";;;;;;;;;MA+BaA,WAAa,CAWtBC,2BAA4B,+GAaY,CAACC,WAAYC,WAAYC,aAC1D,mCACHJ,WAAWC,2BACX,CACIE,WAAAA,WACAC,UAAAA,WAEJF"}
+65
View File
@@ -0,0 +1,65 @@
// 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/>.
/**
* Javascript events for the `editor_atto` plugin.
*
* @module editor_atto/events
* @copyright 2021 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.10.5
*/
import {dispatchEvent} from 'core/event_dispatcher';
/**
* Events for the `editor_atto` plugin.
*
* @constant
* @property {String} attoButtonHighlightToggled See {@link event:attoButtonHighlightToggled}
*/
export const eventTypes = {
/**
* An event triggered when a toolbar button's highlight gets toggled.
*
* @event attoButtonHighlightToggled
* @type {CustomEvent}
* @property {HTMLElement} target The button which had its highlight toggled.
* @property {object} detail
* @property {String} detail.buttonName The name of the Atto button that has had its highlight toggled.
* @property {Boolean} detail.highlight True when the button was highlighted. False, otherwise.
*/
attoButtonHighlightToggled: 'editor_atto/attoButtonHighlightToggled',
};
/**
* Trigger an event to indicate that a button's highlight was toggled.
*
* @method notifyButtonHighlightToggled
* @returns {CustomEvent}
* @fires attoButtonHighlightToggled
* @param {HTMLElement} attoButton The button object.
* @param {String} buttonName The button name.
* @param {Boolean} highlight True when the button was highlighted. False, otherwise.
*/
export const notifyButtonHighlightToggled = (attoButton, buttonName, highlight) => {
return dispatchEvent(
eventTypes.attoButtonHighlightToggled,
{
buttonName,
highlight,
},
attoButton
);
};
+221
View File
@@ -0,0 +1,221 @@
<?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/>.
/**
* Save and load draft text while a user is still editing a form.
*
* @package editor_atto
* @copyright 2014 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('AJAX_SCRIPT', true);
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir . '/filestorage/file_storage.php');
// Clean up actions.
$actions = array_map(function($actionparams) {
$action = isset($actionparams['action']) ? $actionparams['action'] : null;
$params = [];
$keys = [
'action' => PARAM_ALPHA,
'contextid' => PARAM_INT,
'elementid' => PARAM_ALPHANUMEXT,
'pagehash' => PARAM_ALPHANUMEXT,
'pageinstance' => PARAM_ALPHANUMEXT
];
if ($action == 'save') {
$keys['drafttext'] = PARAM_RAW;
} else if ($action == 'resume') {
$keys['draftid'] = PARAM_INT;
}
foreach ($keys as $key => $type) {
// Replicate required_param().
if (!isset($actionparams[$key])) {
throw new \moodle_exception('missingparam', '', '', $key);
}
$params[$key] = clean_param($actionparams[$key], $type);
}
return $params;
}, isset($_REQUEST['actions']) ? $_REQUEST['actions'] : []);
$now = time();
// This is the oldest time any autosave text will be recovered from.
// This is so that there is a good chance the draft files will still exist (there are many variables so
// this is impossible to guarantee).
$before = $now - 60*60*24*4;
$context = context_system::instance();
$PAGE->set_url('/lib/editor/atto/autosave-ajax.php');
$PAGE->set_context($context);
require_login();
if (isguestuser()) {
throw new \moodle_exception('accessdenied', 'admin');
}
require_sesskey();
if (!in_array('atto', explode(',', get_config('core', 'texteditors')))) {
throw new \moodle_exception('accessdenied', 'admin');
}
$responses = array();
foreach ($actions as $actionparams) {
$action = $actionparams['action'];
$contextid = $actionparams['contextid'];
$elementid = $actionparams['elementid'];
$pagehash = $actionparams['pagehash'];
$pageinstance = $actionparams['pageinstance'];
if ($action === 'save') {
$drafttext = $actionparams['drafttext'];
$params = array('elementid' => $elementid,
'userid' => $USER->id,
'pagehash' => $pagehash,
'contextid' => $contextid);
$record = $DB->get_record('editor_atto_autosave', $params);
if ($record && $record->pageinstance != $pageinstance) {
throw new \moodle_exception('concurrent access from the same user is not supported');
die();
}
if (!$record) {
$record = new stdClass();
$record->elementid = $elementid;
$record->userid = $USER->id;
$record->pagehash = $pagehash;
$record->contextid = $contextid;
$record->drafttext = $drafttext;
$record->pageinstance = $pageinstance;
$record->timemodified = $now;
$DB->insert_record('editor_atto_autosave', $record);
// No response means no error.
$responses[] = null;
continue;
} else {
$record->drafttext = $drafttext;
$record->timemodified = time();
$DB->update_record('editor_atto_autosave', $record);
// No response means no error.
$responses[] = null;
continue;
}
} else if ($action == 'resume') {
$params = array('elementid' => $elementid,
'userid' => $USER->id,
'pagehash' => $pagehash,
'contextid' => $contextid);
$newdraftid = $actionparams['draftid'];
$record = $DB->get_record('editor_atto_autosave', $params);
if (!$record) {
$record = new stdClass();
$record->elementid = $elementid;
$record->userid = $USER->id;
$record->pagehash = $pagehash;
$record->contextid = $contextid;
$record->pageinstance = $pageinstance;
$record->pagehash = $pagehash;
$record->draftid = $newdraftid;
$record->timemodified = time();
$record->drafttext = '';
$DB->insert_record('editor_atto_autosave', $record);
// No response means no error.
$responses[] = null;
continue;
} else {
// Copy all draft files from the old draft area.
$usercontext = context_user::instance($USER->id);
$stale = $record->timemodified < $before;
require_once($CFG->libdir . '/filelib.php');
$fs = get_file_storage();
$files = $fs->get_directory_files($usercontext->id, 'user', 'draft', $newdraftid, '/', true, true);
$lastfilemodified = 0;
foreach ($files as $file) {
$lastfilemodified = max($lastfilemodified, $file->get_timemodified());
}
if ($record->timemodified < $lastfilemodified) {
$stale = true;
}
if (!$stale) {
// This function copies all the files in one draft area, to another area (in this case it's
// another draft area). It also rewrites the text to @@PLUGINFILE@@ links.
$newdrafttext = file_save_draft_area_files($record->draftid,
$usercontext->id,
'user',
'draft',
$newdraftid,
array(),
$record->drafttext);
// Final rewrite to the new draft area (convert the @@PLUGINFILES@@ again).
$newdrafttext = file_rewrite_pluginfile_urls($newdrafttext,
'draftfile.php',
$usercontext->id,
'user',
'draft',
$newdraftid);
$record->drafttext = $newdrafttext;
$record->pageinstance = $pageinstance;
$record->draftid = $newdraftid;
$record->timemodified = time();
$DB->update_record('editor_atto_autosave', $record);
// A response means the draft has been restored and here is the auto-saved text.
$response = ['result' => $record->drafttext];
$responses[] = $response;
} else {
$DB->delete_records('editor_atto_autosave', array('id' => $record->id));
// No response means no error.
$responses[] = null;
}
continue;
}
} else if ($action == 'reset') {
$params = array('elementid' => $elementid,
'userid' => $USER->id,
'pagehash' => $pagehash,
'contextid' => $contextid);
$DB->delete_records('editor_atto_autosave', $params);
$responses[] = null;
continue;
}
}
echo json_encode($responses);
@@ -0,0 +1,87 @@
<?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/>.
/**
* Subplugin info class.
*
* @package editor_atto
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace editor_atto\plugininfo;
use core\plugininfo\base;
defined('MOODLE_INTERNAL') || die();
class atto extends base {
/**
* Yes you can uninstall these plugins if you want.
* @return \moodle_url
*/
public function is_uninstall_allowed() {
return true;
}
/**
* Return URL used for management of plugins of this type.
* @return \moodle_url
*/
public static function get_manage_url() {
return new \moodle_url('/admin/settings.php', array('section'=>'editorsettingsatto'));
}
/**
* Include the settings.php file from sub plugins if they provide it.
* This is a copy of very similar implementations from various other subplugin areas.
*
* @return \moodle_url
*/
public function load_settings(\part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
global $CFG, $USER, $DB, $OUTPUT, $PAGE; // In case settings.php wants to refer to them.
/** @var \admin_root $ADMIN */
$ADMIN = $adminroot; // May be used in settings.php.
$plugininfo = $this; // Also can be used inside settings.php.
if (!$this->is_installed_and_upgraded()) {
return;
}
if (!$hassiteconfig or !file_exists($this->full_path('settings.php'))) {
return;
}
$section = $this->get_settings_section_name();
$settings = new \admin_settingpage($section, $this->displayname, 'moodle/site:config', $this->is_enabled() === false);
include($this->full_path('settings.php')); // This may also set $settings to null.
if ($settings) {
$ADMIN->add($parentnodename, $settings);
}
}
/**
* Get the settings section name.
* It's used to get the setting links in the Atto sub-plugins table.
*
* @return null|string the settings section name.
*/
public function get_settings_section_name() {
return 'atto_' . $this->name . '_settings';
}
}
@@ -0,0 +1,244 @@
<?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 editor_atto.
*
* @package editor_atto
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace editor_atto\privacy;
defined('MOODLE_INTERNAL') || die();
use \core_privacy\local\request\approved_contextlist;
use \core_privacy\local\request\writer;
use \core_privacy\local\request\helper;
use \core_privacy\local\request\deletion_criteria;
use \core_privacy\local\metadata\collection;
use \core_privacy\local\request\userlist;
use \core_privacy\local\request\approved_userlist;
/**
* Privacy Subsystem implementation for editor_atto.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
// The Atto editor stores user provided data.
\core_privacy\local\metadata\provider,
// The Atto editor provides data directly to core.
\core_privacy\local\request\plugin\provider,
// The Atto editor is capable of determining which users have data within it.
\core_privacy\local\request\core_userlist_provider {
/**
* Returns information about how editor_atto stores its data.
*
* @param collection $collection The initialised collection to add items to.
* @return collection A listing of user data stored through this system.
*/
public static function get_metadata(collection $collection): collection {
// There isn't much point giving details about the pageid, etc.
$collection->add_database_table('editor_atto_autosave', [
'userid' => 'privacy:metadata:database:atto_autosave:userid',
'drafttext' => 'privacy:metadata:database:atto_autosave:drafttext',
'timemodified' => 'privacy:metadata:database:atto_autosave:timemodified',
], 'privacy:metadata:database:atto_autosave');
return $collection;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid The user to search.
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
*/
public static function get_contexts_for_userid(int $userid): \core_privacy\local\request\contextlist {
// This block doesn't know who information is stored against unless it
// is at the user context.
$contextlist = new \core_privacy\local\request\contextlist();
$sql = "SELECT
c.id
FROM {editor_atto_autosave} eas
JOIN {context} c ON c.id = eas.contextid
WHERE contextlevel = :contextuser AND c.instanceid = :userid";
$contextlist->add_from_sql($sql, ['contextuser' => CONTEXT_USER, 'userid' => $userid]);
$sql = "SELECT contextid FROM {editor_atto_autosave} WHERE userid = :userid";
$contextlist->add_from_sql($sql, ['userid' => $userid]);
return $contextlist;
}
/**
* Get the list of users within a specific context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
$params = [
'contextid' => $context->id
];
$sql = "SELECT userid
FROM {editor_atto_autosave}
WHERE contextid = :contextid";
$userlist->add_from_sql('userid', $sql, $params);
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
$user = $contextlist->get_user();
// Firstly export all autosave records from all contexts in the list owned by the given user.
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
$contextparams['userid'] = $user->id;
$sql = "SELECT *
FROM {editor_atto_autosave}
WHERE userid = :userid AND contextid {$contextsql}";
$autosaves = $DB->get_recordset_sql($sql, $contextparams);
self::export_autosaves($user, $autosaves);
// Additionally export all eventual records in the given user's context regardless the actual owner.
// We still consider them to be the user's personal data even when edited by someone else.
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
$contextparams['userid'] = $user->id;
$contextparams['contextuser'] = CONTEXT_USER;
$sql = "SELECT eas.*
FROM {editor_atto_autosave} eas
JOIN {context} c ON c.id = eas.contextid
WHERE c.id {$contextsql} AND c.contextlevel = :contextuser AND c.instanceid = :userid";
$autosaves = $DB->get_recordset_sql($sql, $contextparams);
self::export_autosaves($user, $autosaves);
}
/**
* Export all autosave records in the recordset, and close the recordset when finished.
*
* @param \stdClass $user The user whose data is to be exported
* @param \moodle_recordset $autosaves The recordset containing the data to export
*/
protected static function export_autosaves(\stdClass $user, \moodle_recordset $autosaves) {
foreach ($autosaves as $autosave) {
$context = \context::instance_by_id($autosave->contextid);
$subcontext = [
get_string('autosaves', 'editor_atto'),
$autosave->id,
];
$html = writer::with_context($context)
->rewrite_pluginfile_urls($subcontext, 'user', 'draft', $autosave->draftid, $autosave->drafttext);
$data = (object) [
'drafttext' => format_text($html, FORMAT_HTML, static::get_filter_options()),
'timemodified' => \core_privacy\local\request\transform::datetime($autosave->timemodified),
];
if ($autosave->userid != $user->id) {
$data->author = \core_privacy\local\request\transform::user($autosave->userid);
}
writer::with_context($context)
->export_data($subcontext, $data)
->export_area_files($subcontext, 'user', 'draft', $autosave->draftid);
}
$autosaves->close();
}
/**
* Delete all data for all users in the specified context.
*
* @param \context $context The specific context to delete data for.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
global $DB;
$DB->delete_records('editor_atto_autosave', [
'contextid' => $context->id,
]);
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
global $DB;
$context = $userlist->get_context();
$userids = $userlist->get_userids();
list($useridsql, $useridsqlparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$params = ['contextid' => $context->id] + $useridsqlparams;
$DB->delete_records_select('editor_atto_autosave', "contextid = :contextid AND userid {$useridsql}",
$params);
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
global $DB;
$user = $contextlist->get_user();
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
$contextparams['userid'] = $user->id;
$sql = "SELECT * FROM {editor_atto_autosave} WHERE contextid {$contextsql}";
$autosaves = $DB->delete_records_select('editor_atto_autosave', "userid = :userid AND contextid {$contextsql}",
$contextparams);
}
/**
* Get the filter options.
*
* This is shared to allow unit testing too.
*
* @return \stdClass
*/
public static function get_filter_options() {
return (object) [
'overflowdiv' => true,
'noclean' => true,
];
}
}
@@ -0,0 +1,58 @@
<?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/>.
/**
* A scheduled task.
*
* @package editor_atto
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace editor_atto\task;
use \core\task\scheduled_task;
/**
* Simple task to run the autosave cleanup task.
*/
class autosave_cleanup_task extends scheduled_task {
/**
* Get a descriptive name for this task (shown to admins).
*
* @return string
*/
public function get_name() {
return get_string('taskautosavecleanup', 'editor_atto');
}
/**
* Do the job.
* Throw exceptions on errors (the job will be retried).
*/
public function execute() {
global $DB;
$now = time();
// This is the oldest time any autosave text will be recovered from.
// This is so that there is a good chance the draft files will still exist (there are many variables so
// this is impossible to guarantee).
$before = $now - 60*60*24*4;
$DB->delete_records_select('editor_atto_autosave', 'timemodified < :before', array('before' => $before));
}
}
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/editor/atto/db" VERSION="20140819" COMMENT="XMLDB file for Moodle lib/editor/atto"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="editor_atto_autosave" COMMENT="Draft text that is auto-saved every 5 seconds while an editor is open.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="elementid" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The unique id for the text editor in the form."/>
<FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The contextid that the form was loaded with."/>
<FIELD NAME="pagehash" TYPE="char" LENGTH="64" NOTNULL="true" SEQUENCE="false" COMMENT="The HTML DOM id of the page that loaded the form."/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The id of the user that loaded the form."/>
<FIELD NAME="drafttext" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The draft text"/>
<FIELD NAME="draftid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Optional draft area id containing draft files."/>
<FIELD NAME="pageinstance" TYPE="char" LENGTH="64" NOTNULL="true" SEQUENCE="false" COMMENT="The browser tab instance that last saved the draft text. This is to prevent multiple tabs from the same user saving different text alternately."/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Store the last modified time for the auto save text."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="autosave_uniq_key" TYPE="unique" FIELDS="elementid, contextid, userid, pagehash" COMMENT="Unique key for the user in the form in the page."/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
+5
View File
@@ -0,0 +1,5 @@
{
"plugintypes": {
"atto": "lib\/editor\/atto\/plugins"
}
}
+44
View File
@@ -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/>.
/**
* Definition of core scheduled tasks.
*
* The handlers defined on this file are processed and registered into
* the Moodle DB after any install or upgrade operation. All plugins
* support this.
*
* @package core
* @category task
* @copyright 2013 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/* List of handlers */
$tasks = array(
array(
'classname' => 'editor_atto\task\autosave_cleanup_task',
'blocking' => 0,
'minute' => 'R',
'hour' => 'R',
'day' => '*',
'dayofweek' => 'R',
'month' => '*'
)
);
+44
View File
@@ -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 upgrade script.
*
* @package editor_atto
* @copyright 2014 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Run all Atto upgrade steps between the current DB version and the current version on disk.
* @param int $oldversion The old version of atto in the DB.
* @return bool
*/
function xmldb_editor_atto_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+54
View File
@@ -0,0 +1,54 @@
<?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 'editor_atto', language 'en'.
*
* @package editor_atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['autosavefailed'] = 'Could not connect to the server. If you submit this page now, your changes may be lost.';
$string['autosavefrequency'] = 'Autosave frequency';
$string['autosavefrequency_desc'] = 'This is the number of seconds between auto save attempts. Atto will automatically save the text in the editor according to this setting, so that text can be automatically restored when the same user returns to the same form.';
$string['autosavesucceeded'] = 'Draft saved.';
$string['errorcannotparseline'] = 'The line \'{$a}\' is not in the correct format.';
$string['errorgroupisusedtwice'] = 'The group \'{$a}\' is defined twice; group names must be unique.';
$string['errornopluginsorgroupsfound'] = 'No plugins or groups found; please add some groups and plugins.';
$string['errorpluginnotfound'] = 'The plugin \'{$a}\' cannot be used; it does not appear to be installed.';
$string['errorpluginisusedtwice'] = 'The plugin \'{$a}\' is used twice; plugins can only be defined once.';
$string['errortextrecovery'] = 'Unfortunately the draft version could not be restored.';
$string['pluginname'] = 'Atto HTML editor';
$string['richtexteditor'] = 'Rich text editor';
$string['subplugintype_atto'] = 'Atto plugin';
$string['subplugintype_atto_plural'] = 'Atto plugins';
$string['settings'] = 'Atto toolbar settings';
$string['taskautosavecleanup'] = 'Delete expired autosave drafts';
$string['textrecovered'] = 'A draft version of this text was automatically restored.';
$string['toolbarconfig'] = 'Toolbar config';
$string['toolbarconfig_desc'] = 'The list of plugins and the order they are displayed can be configured here. The configuration consists of groups (one per line) followed by the ordered list of plugins for that group. The group is separated from the plugins with an equals sign and the plugins are separated with commas. The group names must be unique and should indicate what the buttons have in common. Button and group names should not be repeated and may only contain alphanumeric characters.';
$string['editor_command_keycode'] = 'Cmd + {$a}';
$string['editor_control_keycode'] = 'Ctrl + {$a}';
$string['plugin_title_shortcut'] = '{$a->title} [{$a->shortcut}]';
$string['recover'] = 'Recover';
$string['infostatus'] = 'Information';
$string['warningstatus'] = 'Warning';
$string['autosaves'] = 'Editor autosave information';
$string['privacy:metadata:database:atto_autosave'] = 'Automatically saved text editor drafts.';
$string['privacy:metadata:database:atto_autosave:userid'] = 'The ID of the user whose data was saved.';
$string['privacy:metadata:database:atto_autosave:drafttext'] = 'The text which was saved.';
$string['privacy:metadata:database:atto_autosave:timemodified'] = 'The time that content was modified.';
+207
View File
@@ -0,0 +1,207 @@
<?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/>.
/**
* YUI text editor integration.
*
* @package editor_atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* This is the texteditor implementation.
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class atto_texteditor extends texteditor {
/**
* Is the current browser supported by this editor?
*
* Of course!
* @return bool
*/
public function supported_by_browser() {
return true;
}
/**
* Returns array of supported text formats.
* @return array
*/
public function get_supported_formats() {
// FORMAT_MOODLE is not supported here, sorry.
return array(FORMAT_HTML => FORMAT_HTML);
}
/**
* Returns text format preferred by this editor.
* @return int
*/
public function get_preferred_format() {
return FORMAT_HTML;
}
/**
* Does this editor support picking from repositories?
* @return bool
*/
public function supports_repositories() {
return true;
}
/**
* Use this editor for given element.
*
* Available Atto-specific options:
* atto:toolbar - set to a string to override the system config editor_atto/toolbar
*
* Available general options:
* context - set to the current context object
* enable_filemanagement - set false to get rid of the managefiles plugin
* autosave - true/false to control autosave
*
* Options are also passed through to the plugins.
*
* @param string $elementid
* @param array $options
* @param null $fpoptions
*/
public function use_editor($elementid, array $options=null, $fpoptions=null) {
global $PAGE;
if (array_key_exists('atto:toolbar', $options)) {
$configstr = $options['atto:toolbar'];
} else {
$configstr = get_config('editor_atto', 'toolbar');
}
$grouplines = explode("\n", $configstr);
$groups = array();
foreach ($grouplines as $groupline) {
$line = explode('=', $groupline);
if (count($line) > 1) {
$group = trim(array_shift($line));
$plugins = array_map('trim', explode(',', array_shift($line)));
$groups[$group] = $plugins;
}
}
$modules = array('moodle-editor_atto-editor');
$options['context'] = empty($options['context']) ? context_system::instance() : $options['context'];
$jsplugins = array();
foreach ($groups as $group => $plugins) {
$groupplugins = array();
foreach ($plugins as $plugin) {
// Do not die on missing plugin.
if (!core_component::get_component_directory('atto_' . $plugin)) {
continue;
}
// Remove manage files if requested.
if ($plugin == 'managefiles' && isset($options['enable_filemanagement']) && !$options['enable_filemanagement']) {
continue;
}
$jsplugin = array();
$jsplugin['name'] = $plugin;
$jsplugin['params'] = array();
$modules[] = 'moodle-atto_' . $plugin . '-button';
component_callback('atto_' . $plugin, 'strings_for_js');
$extra = component_callback('atto_' . $plugin, 'params_for_js', array($elementid, $options, $fpoptions));
if ($extra) {
$jsplugin = array_merge($jsplugin, $extra);
}
// We always need the plugin name.
$PAGE->requires->string_for_js('pluginname', 'atto_' . $plugin);
$groupplugins[] = $jsplugin;
}
$jsplugins[] = array('group'=>$group, 'plugins'=>$groupplugins);
}
$PAGE->requires->strings_for_js([
'editor_command_keycode',
'editor_control_keycode',
'plugin_title_shortcut',
'textrecovered',
'autosavefailed',
'autosavesucceeded',
'errortextrecovery',
'richtexteditor',
], 'editor_atto');
$PAGE->requires->strings_for_js(array(
'warning',
'info'
), 'moodle');
$PAGE->requires->yui_module($modules,
'Y.M.editor_atto.Editor.init',
array($this->get_init_params($elementid, $options, $fpoptions, $jsplugins)));
}
/**
* Create a params array to init the editor.
*
* @param string $elementid
* @param array $options
* @param array $fpoptions
*/
protected function get_init_params($elementid, array $options = null, array $fpoptions = null, $plugins = null) {
global $PAGE;
$directionality = get_string('thisdirection', 'langconfig');
$strtime = get_string('strftimetime');
$strdate = get_string('strftimedaydate');
$lang = current_language();
$autosave = true;
$autosavefrequency = get_config('editor_atto', 'autosavefrequency');
if (isset($options['autosave'])) {
$autosave = $options['autosave'];
}
$contentcss = $PAGE->theme->editor_css_url()->out(false);
// Autosave disabled for guests and not logged in users.
if (isguestuser() OR !isloggedin()) {
$autosave = false;
}
// Note <> is a safe separator, because it will not appear in the output of s().
$pagehash = sha1($PAGE->url . '<>' . s($this->get_text()));
$params = array(
'elementid' => $elementid,
'content_css' => $contentcss,
'contextid' => $options['context']->id,
'autosaveEnabled' => $autosave,
'autosaveFrequency' => $autosavefrequency,
'language' => $lang,
'directionality' => $directionality,
'filepickeroptions' => array(),
'plugins' => $plugins,
'pageHash' => $pagehash,
);
if ($fpoptions) {
$params['filepickeroptions'] = $fpoptions;
}
return $params;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

+91
View File
@@ -0,0 +1,91 @@
<?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"
width="128"
height="128"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="New document 1" preserveAspectRatio="xMinYMid meet">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="155.41976"
inkscape:cy="37.330502"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1920"
inkscape:window-height="1004"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid2993"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="4px"
spacingy="4px" />
</sodipodi:namedview>
<metadata
id="metadata7">
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-924.36218)">
<rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect2991"
width="96"
height="96"
x="16"
y="16"
transform="translate(0,924.36218)"
ry="16" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
x="20"
y="1008.3622"
id="text2985"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan2987"
x="20"
y="1008.3622"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#ffffff;fill-opacity:1;font-family:FreeSans;-inkscape-font-specification:FreeSans"><tspan
style="font-size:40px"
id="tspan3763">10</tspan><tspan
style="font-size:28px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;baseline-shift:super;fill:#ffffff;fill-opacity:1;font-family:FreeSans;-inkscape-font-specification:FreeSans"
id="tspan2989">-18</tspan></tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -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

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