');e.one("body").prepend(r),r.focus();try{document.execCommand("insertText",!1,"b"),r.getHTML()==="ba"&&(document.execCommand("undo",!1),r.getHTML()==="a"&&(document.execCommand("redo",!1),r.getHTML()==="ba"&&(n=!0)))}catch(i){return!1}return e.one("body").removeChild(r),t.focus(),n},init:function(e){M.atto_undo.browsersupportsundo===null&&(M.atto_undo.browsersupportsundo=M.atto_undo.test_undo_support());if(M.atto_undo.browsersupportsundo){var t=M.util.image_url("e/undo","core");M.editor_atto.add_toolbar_button(e.elementid,"undo",t,e.group,M.atto_undo.undo_handler,"undo",M.util.get_string("undo","atto_undo")),t=M.util.image_url("e/redo","core"),M.editor_atto.add_toolbar_button(e.elementid,"undo",t,e.group,M.atto_undo.redo_handler,"redo",M.util.get_string("redo","atto_undo")),M.editor_atto.add_button_shortcut({action:"redo",keys:89})}}}},"@VERSION@",{requires:["node","moodle-editor_atto-editor-shortcut"]});
diff --git a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js
index 6aa450b5a99..6143f87a008 100644
--- a/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js
+++ b/lib/editor/atto/plugins/undo/yui/build/moodle-atto_undo-button/moodle-atto_undo-button.js
@@ -138,9 +138,10 @@ M.atto_undo = M.atto_undo || {
// Redo button.
iconurl = M.util.image_url('e/redo', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'undo', iconurl, params.group, M.atto_undo.redo_handler, 'redo', M.util.get_string('redo', 'atto_undo'));
+ M.editor_atto.add_button_shortcut({action: 'redo', keys: 89});
}
}
};
-}, '@VERSION@', {"requires": ["node"]});
+}, '@VERSION@', {"requires": ["node", "moodle-editor_atto-editor-shortcut"]});
diff --git a/lib/editor/atto/plugins/undo/yui/src/button/js/button.js b/lib/editor/atto/plugins/undo/yui/src/button/js/button.js
index 3b0be134eae..98d2f6cbc5c 100755
--- a/lib/editor/atto/plugins/undo/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/undo/yui/src/button/js/button.js
@@ -136,6 +136,7 @@ M.atto_undo = M.atto_undo || {
// Redo button.
iconurl = M.util.image_url('e/redo', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'undo', iconurl, params.group, M.atto_undo.redo_handler, 'redo', M.util.get_string('redo', 'atto_undo'));
+ M.editor_atto.add_button_shortcut({action: 'redo', keys: 89});
}
}
};
diff --git a/lib/editor/atto/plugins/undo/yui/src/button/meta/button.json b/lib/editor/atto/plugins/undo/yui/src/button/meta/button.json
index 59737f492c8..1e574b9dbaf 100755
--- a/lib/editor/atto/plugins/undo/yui/src/button/meta/button.json
+++ b/lib/editor/atto/plugins/undo/yui/src/button/meta/button.json
@@ -1,5 +1,5 @@
{
"moodle-atto_undo-button": {
- "requires": ["node"]
+ "requires": ["node", "moodle-editor_atto-editor-shortcut"]
}
}
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor-shortcut/moodle-editor_atto-editor-shortcut-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor-shortcut/moodle-editor_atto-editor-shortcut-debug.js
new file mode 100644
index 00000000000..76a4c809646
--- /dev/null
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor-shortcut/moodle-editor_atto-editor-shortcut-debug.js
@@ -0,0 +1,263 @@
+YUI.add('moodle-editor_atto-editor-shortcut', 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 .
+
+var SHORTCUT,
+ SHORTCUTNAME = 'AttoButtonShortcut',
+ EVENTS = {
+ press: 'press'
+ },
+ ATTRS = {
+ action: 'action',
+ eventtype: 'eventtype',
+ keys: 'keys'
+ },
+ CSS = {
+ editorid: 'data-editor',
+ contenteditable: '.editor_atto_content'
+ },
+ NS = 'moodle-editor_atto-editor-shortcut';
+
+/**
+ * Atto editor shortcut class
+ *
+ * @namespace M.editor_atto
+ * @class Shortcut
+ * @constructor
+ * @extends Base
+ * @copyright 2014 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+SHORTCUT = function() {
+ SHORTCUT.superclass.constructor.apply(this, arguments);
+};
+SHORTCUT.prototype = {
+ /**
+ * Initialises a new shortcut.
+ * @method initializer
+ */
+ initializer: function() {
+ /**
+ * Shortcut Event: press.
+ *
+ * This event is fired when the user triggers the shortcut object by pressing the required keys.
+ * The event has a default function {@see execActionDefault()} that calls the browser to execute the action used
+ * when the shortcut is created.
+ * If you wish to provide your own functionality you need to add a listener to the shortcut you create for this
+ * event, and then when it is fired call e.preventDefault() on the event facade it provides.
+ *
+ * The event facade contains two custom properties:
+ * * elementid: The ID of the editor that is being acted upon.
+ * * origevent: The original event facade, should you need it for any reason (I hope not)
+ *
+ * @event press
+ */
+ this.publish(EVENTS.press, {
+ emitFacade: true,
+ defaultFn: this.execActionDefault
+ });
+ },
+ /**
+ * Gets called when the user has triggered this shortcut.
+ * @method trigger
+ * @param {EventFacade} e
+ */
+ trigger: function(e) {
+ e.preventDefault();
+ var elementid = e.target.getAttribute(CSS.editorid);
+ this.fire(EVENTS.press, {
+ elementid: elementid,
+ origevent: e
+ });
+ },
+ /**
+ * Binds this shortcut to the editors being shown on the page.
+ * @method bind
+ * @chainable
+ * @param {Node} node
+ * @param {String} container CSS to select the container element to bind to. Usually the contenteditable element.
+ * @return {SHORTCUT}
+ */
+ bind: function(node, container) {
+ var eventtype = this.get(ATTRS.eventtype),
+ keys = this.get(ATTRS.keys);
+ Y.one('body').delegate(
+ eventtype, // Event.
+ this.trigger, // Callback.
+ keys, // Keys.
+ container, // Delegated container.
+ this // Context.
+ );
+ return this;
+ },
+ /**
+ * The default action performed when this shortcut (or any) is triggered.
+ *
+ * This can be cancelled by attaching your own event listener to the press event published
+ * by this shortcut and then calling e.preventDefault() on the EventFacade it triggers.
+ *
+ * @method execActionDefault
+ * @param {EventFacade} e
+ */
+ execActionDefault: function(e) {
+ var elementid = e.elementid;
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand(this.get(ATTRS.action), false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ },
+
+ /**
+ * Returns the default meta key to use with a shortcut.
+ * @method getDefaultMeta
+ * @returns {string}
+ */
+ getDefaultMeta: function() {
+ return (Y.UA.os === 'macintosh') ? '+meta' : '+ctrl';
+ },
+
+ /**
+ * Returns the key event to use for this shortcut.
+ * @returns {string}
+ */
+ getKeyEvent: function() {
+ return 'down:';
+ }
+};
+Y.extend(SHORTCUT, Y.Base, SHORTCUT.prototype, {
+ NAME: SHORTCUTNAME,
+ ATTRS: {
+ /**
+ * The action this shortcut is performing.
+ * If using the default functionality this should be the browser command to execute.
+ * @attribute action
+ * @type String
+ * @writeOnce
+ */
+ action: {
+ writeOnce: 'init',
+ validator: function(val) {
+ return Y.Lang.isString(val);
+ }
+ },
+ /**
+ * The key code(s) used to trigger the shortcut, should be something like `85` for u (underline).
+ *
+ * For a single char all you need to do is set the keys property to the char you want to map to a shortcut.
+ * If you need to do something more advanced (special combinations etc) you can specify a complete key set and
+ * then set the simplekeys property to false.
+ *
+ * Please note that if you do provide an complete char set the browser defaults can only be overridden on the key down
+ * event. A keypress is unfortunately good enough.
+ *
+ * @attribute keys
+ * @default false
+ * @type String|Bool
+ * @writeOnce
+ */
+ keys: {
+ writeOnce: 'init',
+ value: false,
+ validator: function(val) {
+ return Y.Lang.isString(val) || Y.Lang.isNumber(val) || Y.Lang.isBoolean(val);
+ },
+ getter: function(val) {
+ if (this.get('simplekeys')) {
+ return this.getKeyEvent() + val + this.getDefaultMeta();
+ }
+ }
+ },
+ /**
+ * The event type to trigger on.
+ * I can't imagine any good reason to override this, if you find one please let me know.
+ * @attribute eventtype
+ * @type String
+ * @writeOnce
+ * @default key
+ */
+ eventtype: {
+ writeOnce: 'init',
+ value: 'key',
+ validator: function(val) {
+ return Y.Lang.isString(val);
+ }
+ },
+ /**
+ * When set to true a simple key combination is being used and we'll have to append the correct type and control for it.
+ *
+ * Set this too off if you want to define the complete key combination for the shortcut yourself (advanced).
+ */
+ simplekeys: {
+ value: true,
+ validator: function(val) {
+ return Y.Lang.isBoolean(val);
+ }
+ }
+ }
+});
+
+M.editor_atto = M.editor_atto || {};
+Y.mix(M.editor_atto, {
+ /**
+ * An associative collection of shortcut objects that have been bound to the editors on the page.
+ * @protected
+ * @namespace M.editor_atto
+ * @property shortcutdelegations
+ * @type Object
+ */
+ shortcutdelegations: {},
+
+ /**
+ * Adds a button shortcut given a configuration object containing properties for it.
+ *
+ * The config object must contain at least action and keys.
+ * For more details see {@link SHORTCUT()}
+ *
+ * @static
+ * @namespace M.editor_atto
+ * @method add_button_shortcut
+ * @param {Object} config A configuration object containing at least action and keys.
+ * @return SHORTCUT
+ */
+ add_button_shortcut: function(config) {
+ var shortcut = new SHORTCUT(config);
+ return this.register_button_shortcut(shortcut);
+ },
+
+ /**
+ * Registers a shortcut object and binds it to the editors being displayed on the current page.
+ *
+ * @static
+ * @namespace M.editor_atto
+ * @method register_button_shortcut
+ * @param {SHORTCUT} shortcut The shortcut object to add.
+ * @return SHORTCUT
+ */
+ register_button_shortcut: function(shortcut) {
+ var action = shortcut.get(ATTRS.action),
+ keys = shortcut.get(ATTRS.keys);
+ if (!M.editor_atto.shortcutdelegations[action] && keys) {
+ Y.log('Atto shortcut registered: ' + keys + ' now triggers ' + action, 'debug', NS);
+ M.editor_atto.shortcutdelegations[action] = shortcut.bind(Y.one('body'), CSS.contenteditable);
+ }
+ return M.editor_atto.shortcutdelegations[action];
+ }
+});
+
+
+}, '@VERSION@', {"requires": ["node", "event", "event-custom", "moodle-editor_atto-editor"]});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor-shortcut/moodle-editor_atto-editor-shortcut-min.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor-shortcut/moodle-editor_atto-editor-shortcut-min.js
new file mode 100644
index 00000000000..679b5f4ba69
--- /dev/null
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor-shortcut/moodle-editor_atto-editor-shortcut-min.js
@@ -0,0 +1 @@
+YUI.add("moodle-editor_atto-editor-shortcut",function(e,t){var n,r="AttoButtonShortcut",i={press:"press"},s={action:"action",eventtype:"eventtype",keys:"keys"},o={editorid:"data-editor",contenteditable:".editor_atto_content"},u="moodle-editor_atto-editor-shortcut";n=function(){n.superclass.constructor.apply(this,arguments)},n.prototype={initializer:function(){this.publish(i.press,{emitFacade:!0,defaultFn:this.execActionDefault})},trigger:function(e){e.preventDefault();var t=e.target.getAttribute(o.editorid);this.fire(i.press,{elementid:t,origevent:e})},bind:function(t,n){var r=this.get(s.eventtype),i=this.get(s.keys);return e.one("body").delegate(r,this.trigger,i,n,this),this},execActionDefault:function(e){var t=e.elementid;M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand(this.get(s.action),!1,null),M.editor_atto.text_updated(t)},getDefaultMeta:function(){return e.UA.os==="macintosh"?"+meta":"+ctrl"},getKeyEvent:function(){return"down:"}},e.extend(n,e.Base,n.prototype,{NAME:r,ATTRS:{action:{writeOnce:"init",validator:function(t){return e.Lang.isString(t)}},keys:{writeOnce:"init",value:!1,validator:function(t){return e.Lang.isString(t)||e.Lang.isNumber(t)||e.Lang.isBoolean(t)},getter:function(e){if(this.get("simplekeys"))return this.getKeyEvent()+e+this.getDefaultMeta()}},eventtype:{writeOnce:"init",value:"key",validator:function(t){return e.Lang.isString(t)}},simplekeys:{value:!0,validator:function(t){return e.Lang.isBoolean(t)}}}}),M.editor_atto=M.editor_atto||{},e.mix(M.editor_atto,{shortcutdelegations:{},add_button_shortcut:function(e){var t=new n(e);return this.register_button_shortcut(t)},register_button_shortcut:function(t){var n=t.get(s.action),r=t.get(s.keys);return!M.editor_atto.shortcutdelegations[n]&&r&&(M.editor_atto.shortcutdelegations[n]=t.bind(e.one("body"),o.contenteditable)),M.editor_atto.shortcutdelegations[n]}})},"@VERSION@",{requires:["node","event","event-custom","moodle-editor_atto-editor"]});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor-shortcut/moodle-editor_atto-editor-shortcut.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor-shortcut/moodle-editor_atto-editor-shortcut.js
new file mode 100644
index 00000000000..1ab16eb317f
--- /dev/null
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor-shortcut/moodle-editor_atto-editor-shortcut.js
@@ -0,0 +1,262 @@
+YUI.add('moodle-editor_atto-editor-shortcut', 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 .
+
+var SHORTCUT,
+ SHORTCUTNAME = 'AttoButtonShortcut',
+ EVENTS = {
+ press: 'press'
+ },
+ ATTRS = {
+ action: 'action',
+ eventtype: 'eventtype',
+ keys: 'keys'
+ },
+ CSS = {
+ editorid: 'data-editor',
+ contenteditable: '.editor_atto_content'
+ },
+ NS = 'moodle-editor_atto-editor-shortcut';
+
+/**
+ * Atto editor shortcut class
+ *
+ * @namespace M.editor_atto
+ * @class Shortcut
+ * @constructor
+ * @extends Base
+ * @copyright 2014 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+SHORTCUT = function() {
+ SHORTCUT.superclass.constructor.apply(this, arguments);
+};
+SHORTCUT.prototype = {
+ /**
+ * Initialises a new shortcut.
+ * @method initializer
+ */
+ initializer: function() {
+ /**
+ * Shortcut Event: press.
+ *
+ * This event is fired when the user triggers the shortcut object by pressing the required keys.
+ * The event has a default function {@see execActionDefault()} that calls the browser to execute the action used
+ * when the shortcut is created.
+ * If you wish to provide your own functionality you need to add a listener to the shortcut you create for this
+ * event, and then when it is fired call e.preventDefault() on the event facade it provides.
+ *
+ * The event facade contains two custom properties:
+ * * elementid: The ID of the editor that is being acted upon.
+ * * origevent: The original event facade, should you need it for any reason (I hope not)
+ *
+ * @event press
+ */
+ this.publish(EVENTS.press, {
+ emitFacade: true,
+ defaultFn: this.execActionDefault
+ });
+ },
+ /**
+ * Gets called when the user has triggered this shortcut.
+ * @method trigger
+ * @param {EventFacade} e
+ */
+ trigger: function(e) {
+ e.preventDefault();
+ var elementid = e.target.getAttribute(CSS.editorid);
+ this.fire(EVENTS.press, {
+ elementid: elementid,
+ origevent: e
+ });
+ },
+ /**
+ * Binds this shortcut to the editors being shown on the page.
+ * @method bind
+ * @chainable
+ * @param {Node} node
+ * @param {String} container CSS to select the container element to bind to. Usually the contenteditable element.
+ * @return {SHORTCUT}
+ */
+ bind: function(node, container) {
+ var eventtype = this.get(ATTRS.eventtype),
+ keys = this.get(ATTRS.keys);
+ Y.one('body').delegate(
+ eventtype, // Event.
+ this.trigger, // Callback.
+ keys, // Keys.
+ container, // Delegated container.
+ this // Context.
+ );
+ return this;
+ },
+ /**
+ * The default action performed when this shortcut (or any) is triggered.
+ *
+ * This can be cancelled by attaching your own event listener to the press event published
+ * by this shortcut and then calling e.preventDefault() on the EventFacade it triggers.
+ *
+ * @method execActionDefault
+ * @param {EventFacade} e
+ */
+ execActionDefault: function(e) {
+ var elementid = e.elementid;
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand(this.get(ATTRS.action), false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ },
+
+ /**
+ * Returns the default meta key to use with a shortcut.
+ * @method getDefaultMeta
+ * @returns {string}
+ */
+ getDefaultMeta: function() {
+ return (Y.UA.os === 'macintosh') ? '+meta' : '+ctrl';
+ },
+
+ /**
+ * Returns the key event to use for this shortcut.
+ * @returns {string}
+ */
+ getKeyEvent: function() {
+ return 'down:';
+ }
+};
+Y.extend(SHORTCUT, Y.Base, SHORTCUT.prototype, {
+ NAME: SHORTCUTNAME,
+ ATTRS: {
+ /**
+ * The action this shortcut is performing.
+ * If using the default functionality this should be the browser command to execute.
+ * @attribute action
+ * @type String
+ * @writeOnce
+ */
+ action: {
+ writeOnce: 'init',
+ validator: function(val) {
+ return Y.Lang.isString(val);
+ }
+ },
+ /**
+ * The key code(s) used to trigger the shortcut, should be something like `85` for u (underline).
+ *
+ * For a single char all you need to do is set the keys property to the char you want to map to a shortcut.
+ * If you need to do something more advanced (special combinations etc) you can specify a complete key set and
+ * then set the simplekeys property to false.
+ *
+ * Please note that if you do provide an complete char set the browser defaults can only be overridden on the key down
+ * event. A keypress is unfortunately good enough.
+ *
+ * @attribute keys
+ * @default false
+ * @type String|Bool
+ * @writeOnce
+ */
+ keys: {
+ writeOnce: 'init',
+ value: false,
+ validator: function(val) {
+ return Y.Lang.isString(val) || Y.Lang.isNumber(val) || Y.Lang.isBoolean(val);
+ },
+ getter: function(val) {
+ if (this.get('simplekeys')) {
+ return this.getKeyEvent() + val + this.getDefaultMeta();
+ }
+ }
+ },
+ /**
+ * The event type to trigger on.
+ * I can't imagine any good reason to override this, if you find one please let me know.
+ * @attribute eventtype
+ * @type String
+ * @writeOnce
+ * @default key
+ */
+ eventtype: {
+ writeOnce: 'init',
+ value: 'key',
+ validator: function(val) {
+ return Y.Lang.isString(val);
+ }
+ },
+ /**
+ * When set to true a simple key combination is being used and we'll have to append the correct type and control for it.
+ *
+ * Set this too off if you want to define the complete key combination for the shortcut yourself (advanced).
+ */
+ simplekeys: {
+ value: true,
+ validator: function(val) {
+ return Y.Lang.isBoolean(val);
+ }
+ }
+ }
+});
+
+M.editor_atto = M.editor_atto || {};
+Y.mix(M.editor_atto, {
+ /**
+ * An associative collection of shortcut objects that have been bound to the editors on the page.
+ * @protected
+ * @namespace M.editor_atto
+ * @property shortcutdelegations
+ * @type Object
+ */
+ shortcutdelegations: {},
+
+ /**
+ * Adds a button shortcut given a configuration object containing properties for it.
+ *
+ * The config object must contain at least action and keys.
+ * For more details see {@link SHORTCUT()}
+ *
+ * @static
+ * @namespace M.editor_atto
+ * @method add_button_shortcut
+ * @param {Object} config A configuration object containing at least action and keys.
+ * @return SHORTCUT
+ */
+ add_button_shortcut: function(config) {
+ var shortcut = new SHORTCUT(config);
+ return this.register_button_shortcut(shortcut);
+ },
+
+ /**
+ * Registers a shortcut object and binds it to the editors being displayed on the current page.
+ *
+ * @static
+ * @namespace M.editor_atto
+ * @method register_button_shortcut
+ * @param {SHORTCUT} shortcut The shortcut object to add.
+ * @return SHORTCUT
+ */
+ register_button_shortcut: function(shortcut) {
+ var action = shortcut.get(ATTRS.action),
+ keys = shortcut.get(ATTRS.keys);
+ if (!M.editor_atto.shortcutdelegations[action] && keys) {
+ M.editor_atto.shortcutdelegations[action] = shortcut.bind(Y.one('body'), CSS.contenteditable);
+ }
+ return M.editor_atto.shortcutdelegations[action];
+ }
+});
+
+
+}, '@VERSION@', {"requires": ["node", "event", "event-custom", "moodle-editor_atto-editor"]});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
index 35d31ea5ad6..0522d98dd19 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
@@ -572,7 +572,8 @@ M.editor_atto = M.editor_atto || {
'role="textbox" ' +
'spellcheck="true" ' +
'aria-live="off" ' +
- 'class="' + CSS.CONTENT + '" />');
+ 'class="' + CSS.CONTENT + '" '+
+ 'data-editor="' + params.elementid + '" />');
var toolbar = Y.Node.create('');
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
index 900318f5a81..57c0cfa0412 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
@@ -1,3 +1,3 @@
YUI.add("moodle-editor_atto-editor",function(e,t){function i(){var e=this.getHTML(),t=[{regex://gi,replace:""},{regex:/<\\?\?xml[^>]*>/gi,replace:""},{regex:/<\/?\w+:[^>]*>/gi,replace:""},{regex:/\s*MSO[-:][^;"']*;?/gi,replace:""},{regex:/]*>( |\s)*<\/span>/gi,replace:""},{regex:/class="Mso[^"]*"/gi,replace:""},{regex:/<(\/?title|\/?meta|\/?style|\/?st\d|\/?head|\/?font|\/?html|\/?body|!\[)[^>]*?>/gi,replace:""},{regex:new RegExp(String.fromCharCode(8220),"gi"),replace:'"'},{regex:new RegExp(String.fromCharCode(8216),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8217),"gi"),replace:"'"},{regex:new RegExp(String.fromCharCode(8211),"gi"),replace:"-"},{regex:new RegExp(String.fromCharCode(8212),"gi"),replace:"--"},{regex:new RegExp(String.fromCharCode(189),"gi"),replace:"1/2"},{regex:new RegExp(String.fromCharCode(188),"gi"),replace:"1/4"},{regex:new RegExp(String.fromCharCode(190),"gi"),replace:"3/4"},{regex:new RegExp(String.fromCharCode(169),"gi"),replace:"(c)"},{regex:new RegExp(String.fromCharCode(174),"gi"),replace:"(r)"},{regex:new RegExp(String.fromCharCode(8230),"gi"),replace:"..."}],n=0,r;for(n=0;n'),l.append(c)),v=M.util.image_url("t/expanded","moodle"),p=e.Node.create('"),c.append(p),h=l.getAttribute("aria-activedescendant"),h||(p.setAttribute("tabindex","0"),l.setAttribute("aria-activedescendant",p.generateID())),M.editor_atto.widgets[d]=d;var m=e.Node.create(''),g=0,y={};for(g=0;g'+y.text+""+"")),M.editor_atto.buttonhandlers[n+"_action_"+
-g]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+d+"_action_"+g),e.one("body").delegate("key",M.editor_atto.buttonclicked_handler,"32,enter",".atto_"+d+"_action_"+g),M.editor_atto.buttonhandlers[d+"_action_"+g]=y.handler);M.editor_atto.buttonhandlers[d]||(e.one("body").delegate("click",M.editor_atto.showhide_menu_handler,".atto_"+d+"_button"),M.editor_atto.buttonhandlers[d]=!0);var b=new M.core.dialogue({bodyContent:m,visible:!1,width:a+"em",lightbox:!1,closeButton:!1,center:!1});M.editor_atto.menus[d+"_"+t]=b,b.align(p,[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL]),b.hide(),b.headerNode.hide(),b.render()},add_toolbar_button:function(t,n,r,i,s,o,u){var a=M.editor_atto.get_toolbar_node(t),f=a.one(".atto_group."+i+"_group"),l,c,h;o?c=n+"_"+o:(o="",c=n),u||(u=M.util.get_string("pluginname","atto_"+n)),f||(f=e.Node.create(''),a.append(f)),l=e.Node.create('"),f.append(l),h=a.getAttribute("aria-activedescendant"),h||(l.setAttribute("tabindex","0"),a.setAttribute("aria-activedescendant",l.generateID())),M.editor_atto.buttonhandlers[c]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+c+"_button"),M.editor_atto.buttonhandlers[c]=s),M.editor_atto.widgets[c]=c},is_active:function(t){var n=M.editor_atto.get_selection();n.length&&(n=n.pop());var r=null;n.parentElement?r=e.one(n.parentElement()):r=e.one(n.startContainer);var i=M.editor_atto.get_editable_node(t);return r&&i.contains(r)},focus:function(e){M.editor_atto.get_editable_node(e).focus()},init:function(t){var n=e.Node.create(''),r=e.Node.create(''),i=e.Node.create(''),s=e.Node.create(''),o=M.editor_atto.get_textarea_node(t.elementid),u=e.one('[for="'+t.elementid+'"]');u&&(u.generateID(),r.setAttribute("aria-labelledby",u.get("id")),i.setAttribute("aria-labelledby",u.get("id"))),s.appendChild(r),n.appendChild(i),n.appendChild(s),r.setStyle("minHeight",1.2*o.getAttribute("rows")+"em"),r.append(o.get("value")),r.cleanHTML(),o.get("parentNode").insert(n,o),M.editor_atto.disable_css_styling(),o.hide(),this.publish_events(),r.on("atto:selectionchanged",this.save_selection,this,t.elementid),r.on("focus",this.restore_selection,this,t.elementid),r.on("mousedown",function(){this.focusfromclick=!0},this),r.on("blur",function(){this.focusfromclick=!1,this.text_updated(t.elementid)},this),e.one(e.config.doc.body).delegate("key",this.keyboard_navigation,"down:37,39","#"+t.elementid+"_toolbar",this,t.elementid),M.editor_atto.filepickeroptions[t.elementid]=t.filepickeroptions;var a,f,l,c;for(a=0;a=r.size()&&(o=0)),i=r.item(o),i.setAttribute("tabindex","0"),i.focus(),u.setAttribute("aria-activedescendant",i.generateID())},can_show_filepicker:function(e,t){var n=M.editor_atto.filepickeroptions[e];return typeof n[t]!="undefined"},show_filepicker:function(t,n,r){e.use("core_filepicker",function(
-e){var i=M.editor_atto.filepickeroptions[t][n];i.formcallback=r,M.core_filepicker.show(e,i)})},get_selection_from_node:function(e){var t;return window.getSelection?(t=document.createRange(),t.setStartBefore(e.getDOMNode()),t.setEndAfter(e.getDOMNode()),[t]):document.selection?(t=document.body.createTextRange(),t.moveToElementText(e.getDOMNode()),t):!1},save_selection:function(e,t){if(this.is_active(t)){var n=this.get_selection();n.length>0&&(this.selections[t]=n)}},restore_selection:function(e,t){e.preventDefault(),this.focusfromclick||typeof this.selections[t]!="undefined"&&this.set_selection(this.selections[t]),this.focusfromclick=!1},get_selection:function(){if(window.getSelection){var e=window.getSelection(),t=[],n=0;for(n=0;n0&&e[0].cloneContents)return e[0].cloneContents()},set_selection:function(e){var t,n;if(window.getSelection){t=window.getSelection(),t.removeAllRanges();for(n=0;n
"),s.get("childNodes").each(function(e){a.append(e.remove())}),s.append(a),u=a),n&&n!==""&&(l=e.Node.create("<"+n+">"+n+">"),l.setAttrs(u.getAttrs()),u.get("childNodes").each(function(e){e.remove(),l.append(e)}),u.replace(l),u=l),r&&u.setAttrs(r);var c=M.editor_atto.get_selection_from_node(u);return M.editor_atto.set_selection(c),u},disable_css_styling:function(){try{document.execCommand("styleWithCSS",0,!1)}catch(e){try{document.execCommand("useCSS",0,!0)}catch(t){try{document.execCommand("styleWithCSS",!1,!1)}catch(n){}}}},enable_css_styling:function(){try{document.execCommand("styleWithCSS",0,!0)}catch(e){try{document.execCommand("useCSS",0,!1)}catch(t){try{document.execCommand("styleWithCSS",!1,!0)}catch(n){}}}},insert_html_at_focus_point:function(e){if(document.selection&&document.selection.createRange){var t=document.selection.createRange();t.pasteHTML&&t.pasteHTML(e)}else document.execCommand("insertHTML",!1,e)}};var n="Controlmenu",r;r=function(e){e.draggable=!1,e.center=!1,e.width="auto",e.lightbox=!1,e.footerContent="",e.hideOn=[{eventName:"clickoutside"}],r.superclass.constructor.apply(this,[e])},e.extend(r,M.core.dialogue,{initializer:function(t){var n,i,s;r.superclass.initializer.call(this,t),s=this.get("boundingBox"),s.addClass("editor_atto_controlmenu"),n=this.bodyNode,i=e.Node.create(""),i.addClass("accesshide"),i.setHTML(this.get("headerText")),n.prepend(i)}},{NAME:n,ATTRS:{headerText:{value:""}}}),M.editor_atto=M.editor_atto||{},M.editor_atto.controlmenu=r,e.Node.addMethod("cleanHTML",i),e.NodeList.importMethod(e.Node.prototype,"cleanHTML")},"@VERSION@",{requires:["node","io","overlay","escape","event","event-simulate","event-custom","yui-throttle","moodle-core-notification"]});
+g]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+d+"_action_"+g),e.one("body").delegate("key",M.editor_atto.buttonclicked_handler,"32,enter",".atto_"+d+"_action_"+g),M.editor_atto.buttonhandlers[d+"_action_"+g]=y.handler);M.editor_atto.buttonhandlers[d]||(e.one("body").delegate("click",M.editor_atto.showhide_menu_handler,".atto_"+d+"_button"),M.editor_atto.buttonhandlers[d]=!0);var b=new M.core.dialogue({bodyContent:m,visible:!1,width:a+"em",lightbox:!1,closeButton:!1,center:!1});M.editor_atto.menus[d+"_"+t]=b,b.align(p,[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL]),b.hide(),b.headerNode.hide(),b.render()},add_toolbar_button:function(t,n,r,i,s,o,u){var a=M.editor_atto.get_toolbar_node(t),f=a.one(".atto_group."+i+"_group"),l,c,h;o?c=n+"_"+o:(o="",c=n),u||(u=M.util.get_string("pluginname","atto_"+n)),f||(f=e.Node.create(''),a.append(f)),l=e.Node.create('"),f.append(l),h=a.getAttribute("aria-activedescendant"),h||(l.setAttribute("tabindex","0"),a.setAttribute("aria-activedescendant",l.generateID())),M.editor_atto.buttonhandlers[c]||(e.one("body").delegate("click",M.editor_atto.buttonclicked_handler,".atto_"+c+"_button"),M.editor_atto.buttonhandlers[c]=s),M.editor_atto.widgets[c]=c},is_active:function(t){var n=M.editor_atto.get_selection();n.length&&(n=n.pop());var r=null;n.parentElement?r=e.one(n.parentElement()):r=e.one(n.startContainer);var i=M.editor_atto.get_editable_node(t);return r&&i.contains(r)},focus:function(e){M.editor_atto.get_editable_node(e).focus()},init:function(t){var n=e.Node.create(''),r=e.Node.create(''),i=e.Node.create(''),s=e.Node.create(''),o=M.editor_atto.get_textarea_node(t.elementid),u=e.one('[for="'+t.elementid+'"]');u&&(u.generateID(),r.setAttribute("aria-labelledby",u.get("id")),i.setAttribute("aria-labelledby",u.get("id"))),s.appendChild(r),n.appendChild(i),n.appendChild(s),r.setStyle("minHeight",1.2*o.getAttribute("rows")+"em"),r.append(o.get("value")),r.cleanHTML(),o.get("parentNode").insert(n,o),M.editor_atto.disable_css_styling(),o.hide(),this.publish_events(),r.on("atto:selectionchanged",this.save_selection,this,t.elementid),r.on("focus",this.restore_selection,this,t.elementid),r.on("mousedown",function(){this.focusfromclick=!0},this),r.on("blur",function(){this.focusfromclick=!1,this.text_updated(t.elementid)},this),e.one(e.config.doc.body).delegate("key",this.keyboard_navigation,"down:37,39","#"+t.elementid+"_toolbar",this,t.elementid),M.editor_atto.filepickeroptions[t.elementid]=t.filepickeroptions;var a,f,l,c;for(a=0;a=r.size()&&(o=0)),i=r.item(o),i.setAttribute("tabindex","0"),i.focus(),u.setAttribute("aria-activedescendant",i.generateID())},can_show_filepicker:function(e,t){var n=M.editor_atto.filepickeroptions[e];return typeof n[t]!="undefined"},show_filepicker:function(t
+,n,r){e.use("core_filepicker",function(e){var i=M.editor_atto.filepickeroptions[t][n];i.formcallback=r,M.core_filepicker.show(e,i)})},get_selection_from_node:function(e){var t;return window.getSelection?(t=document.createRange(),t.setStartBefore(e.getDOMNode()),t.setEndAfter(e.getDOMNode()),[t]):document.selection?(t=document.body.createTextRange(),t.moveToElementText(e.getDOMNode()),t):!1},save_selection:function(e,t){if(this.is_active(t)){var n=this.get_selection();n.length>0&&(this.selections[t]=n)}},restore_selection:function(e,t){e.preventDefault(),this.focusfromclick||typeof this.selections[t]!="undefined"&&this.set_selection(this.selections[t]),this.focusfromclick=!1},get_selection:function(){if(window.getSelection){var e=window.getSelection(),t=[],n=0;for(n=0;n0&&e[0].cloneContents)return e[0].cloneContents()},set_selection:function(e){var t,n;if(window.getSelection){t=window.getSelection(),t.removeAllRanges();for(n=0;n"),s.get("childNodes").each(function(e){a.append(e.remove())}),s.append(a),u=a),n&&n!==""&&(l=e.Node.create("<"+n+">"+n+">"),l.setAttrs(u.getAttrs()),u.get("childNodes").each(function(e){e.remove(),l.append(e)}),u.replace(l),u=l),r&&u.setAttrs(r);var c=M.editor_atto.get_selection_from_node(u);return M.editor_atto.set_selection(c),u},disable_css_styling:function(){try{document.execCommand("styleWithCSS",0,!1)}catch(e){try{document.execCommand("useCSS",0,!0)}catch(t){try{document.execCommand("styleWithCSS",!1,!1)}catch(n){}}}},enable_css_styling:function(){try{document.execCommand("styleWithCSS",0,!0)}catch(e){try{document.execCommand("useCSS",0,!1)}catch(t){try{document.execCommand("styleWithCSS",!1,!0)}catch(n){}}}},insert_html_at_focus_point:function(e){if(document.selection&&document.selection.createRange){var t=document.selection.createRange();t.pasteHTML&&t.pasteHTML(e)}else document.execCommand("insertHTML",!1,e)}};var n="Controlmenu",r;r=function(e){e.draggable=!1,e.center=!1,e.width="auto",e.lightbox=!1,e.footerContent="",e.hideOn=[{eventName:"clickoutside"}],r.superclass.constructor.apply(this,[e])},e.extend(r,M.core.dialogue,{initializer:function(t){var n,i,s;r.superclass.initializer.call(this,t),s=this.get("boundingBox"),s.addClass("editor_atto_controlmenu"),n=this.bodyNode,i=e.Node.create(""),i.addClass("accesshide"),i.setHTML(this.get("headerText")),n.prepend(i)}},{NAME:n,ATTRS:{headerText:{value:""}}}),M.editor_atto=M.editor_atto||{},M.editor_atto.controlmenu=r,e.Node.addMethod("cleanHTML",i),e.NodeList.importMethod(e.Node.prototype,"cleanHTML")},"@VERSION@",{requires:["node","io","overlay","escape","event","event-simulate","event-custom","yui-throttle","moodle-core-notification"]});
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
index 35d31ea5ad6..0522d98dd19 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
@@ -572,7 +572,8 @@ M.editor_atto = M.editor_atto || {
'role="textbox" ' +
'spellcheck="true" ' +
'aria-live="off" ' +
- 'class="' + CSS.CONTENT + '" />');
+ 'class="' + CSS.CONTENT + '" '+
+ 'data-editor="' + params.elementid + '" />');
var toolbar = Y.Node.create('');
diff --git a/lib/editor/atto/yui/src/editor/build.json b/lib/editor/atto/yui/src/editor/build.json
index 30fdf49942f..718fef019c3 100644
--- a/lib/editor/atto/yui/src/editor/build.json
+++ b/lib/editor/atto/yui/src/editor/build.json
@@ -7,6 +7,11 @@
"controlmenu.js",
"clean.js"
]
+ },
+ "moodle-editor_atto-editor-shortcut": {
+ "jsfiles": [
+ "shortcut.js"
+ ]
}
}
}
diff --git a/lib/editor/atto/yui/src/editor/js/editor.js b/lib/editor/atto/yui/src/editor/js/editor.js
index bb7bc26eadc..071e2b5ddf5 100755
--- a/lib/editor/atto/yui/src/editor/js/editor.js
+++ b/lib/editor/atto/yui/src/editor/js/editor.js
@@ -570,7 +570,8 @@ M.editor_atto = M.editor_atto || {
'role="textbox" ' +
'spellcheck="true" ' +
'aria-live="off" ' +
- 'class="' + CSS.CONTENT + '" />');
+ 'class="' + CSS.CONTENT + '" '+
+ 'data-editor="' + params.elementid + '" />');
var toolbar = Y.Node.create('');
diff --git a/lib/editor/atto/yui/src/editor/js/shortcut.js b/lib/editor/atto/yui/src/editor/js/shortcut.js
new file mode 100644
index 00000000000..94534e8c307
--- /dev/null
+++ b/lib/editor/atto/yui/src/editor/js/shortcut.js
@@ -0,0 +1,258 @@
+// 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 .
+
+var SHORTCUT,
+ SHORTCUTNAME = 'AttoButtonShortcut',
+ EVENTS = {
+ press: 'press'
+ },
+ ATTRS = {
+ action: 'action',
+ eventtype: 'eventtype',
+ keys: 'keys'
+ },
+ CSS = {
+ editorid: 'data-editor',
+ contenteditable: '.editor_atto_content'
+ },
+ NS = 'moodle-editor_atto-editor-shortcut';
+
+/**
+ * Atto editor shortcut class
+ *
+ * @namespace M.editor_atto
+ * @class Shortcut
+ * @constructor
+ * @extends Base
+ * @copyright 2014 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+SHORTCUT = function() {
+ SHORTCUT.superclass.constructor.apply(this, arguments);
+};
+SHORTCUT.prototype = {
+ /**
+ * Initialises a new shortcut.
+ * @method initializer
+ */
+ initializer: function() {
+ /**
+ * Shortcut Event: press.
+ *
+ * This event is fired when the user triggers the shortcut object by pressing the required keys.
+ * The event has a default function {@see execActionDefault()} that calls the browser to execute the action used
+ * when the shortcut is created.
+ * If you wish to provide your own functionality you need to add a listener to the shortcut you create for this
+ * event, and then when it is fired call e.preventDefault() on the event facade it provides.
+ *
+ * The event facade contains two custom properties:
+ * * elementid: The ID of the editor that is being acted upon.
+ * * origevent: The original event facade, should you need it for any reason (I hope not)
+ *
+ * @event press
+ */
+ this.publish(EVENTS.press, {
+ emitFacade: true,
+ defaultFn: this.execActionDefault
+ });
+ },
+ /**
+ * Gets called when the user has triggered this shortcut.
+ * @method trigger
+ * @param {EventFacade} e
+ */
+ trigger: function(e) {
+ e.preventDefault();
+ var elementid = e.target.getAttribute(CSS.editorid);
+ this.fire(EVENTS.press, {
+ elementid: elementid,
+ origevent: e
+ });
+ },
+ /**
+ * Binds this shortcut to the editors being shown on the page.
+ * @method bind
+ * @chainable
+ * @param {Node} node
+ * @param {String} container CSS to select the container element to bind to. Usually the contenteditable element.
+ * @return {SHORTCUT}
+ */
+ bind: function(node, container) {
+ var eventtype = this.get(ATTRS.eventtype),
+ keys = this.get(ATTRS.keys);
+ Y.one('body').delegate(
+ eventtype, // Event.
+ this.trigger, // Callback.
+ keys, // Keys.
+ container, // Delegated container.
+ this // Context.
+ );
+ return this;
+ },
+ /**
+ * The default action performed when this shortcut (or any) is triggered.
+ *
+ * This can be cancelled by attaching your own event listener to the press event published
+ * by this shortcut and then calling e.preventDefault() on the EventFacade it triggers.
+ *
+ * @method execActionDefault
+ * @param {EventFacade} e
+ */
+ execActionDefault: function(e) {
+ var elementid = e.elementid;
+ if (!M.editor_atto.is_active(elementid)) {
+ M.editor_atto.focus(elementid);
+ }
+ document.execCommand(this.get(ATTRS.action), false, null);
+ // Clean the YUI ids from the HTML.
+ M.editor_atto.text_updated(elementid);
+ },
+
+ /**
+ * Returns the default meta key to use with a shortcut.
+ * @method getDefaultMeta
+ * @returns {string}
+ */
+ getDefaultMeta: function() {
+ return (Y.UA.os === 'macintosh') ? '+meta' : '+ctrl';
+ },
+
+ /**
+ * Returns the key event to use for this shortcut.
+ * @returns {string}
+ */
+ getKeyEvent: function() {
+ return 'down:';
+ }
+};
+Y.extend(SHORTCUT, Y.Base, SHORTCUT.prototype, {
+ NAME: SHORTCUTNAME,
+ ATTRS: {
+ /**
+ * The action this shortcut is performing.
+ * If using the default functionality this should be the browser command to execute.
+ * @attribute action
+ * @type String
+ * @writeOnce
+ */
+ action: {
+ writeOnce: 'init',
+ validator: function(val) {
+ return Y.Lang.isString(val);
+ }
+ },
+ /**
+ * The key code(s) used to trigger the shortcut, should be something like `85` for u (underline).
+ *
+ * For a single char all you need to do is set the keys property to the char you want to map to a shortcut.
+ * If you need to do something more advanced (special combinations etc) you can specify a complete key set and
+ * then set the simplekeys property to false.
+ *
+ * Please note that if you do provide an complete char set the browser defaults can only be overridden on the key down
+ * event. A keypress is unfortunately good enough.
+ *
+ * @attribute keys
+ * @default false
+ * @type String|Bool
+ * @writeOnce
+ */
+ keys: {
+ writeOnce: 'init',
+ value: false,
+ validator: function(val) {
+ return Y.Lang.isString(val) || Y.Lang.isNumber(val) || Y.Lang.isBoolean(val);
+ },
+ getter: function(val) {
+ if (this.get('simplekeys')) {
+ return this.getKeyEvent() + val + this.getDefaultMeta();
+ }
+ }
+ },
+ /**
+ * The event type to trigger on.
+ * I can't imagine any good reason to override this, if you find one please let me know.
+ * @attribute eventtype
+ * @type String
+ * @writeOnce
+ * @default key
+ */
+ eventtype: {
+ writeOnce: 'init',
+ value: 'key',
+ validator: function(val) {
+ return Y.Lang.isString(val);
+ }
+ },
+ /**
+ * When set to true a simple key combination is being used and we'll have to append the correct type and control for it.
+ *
+ * Set this too off if you want to define the complete key combination for the shortcut yourself (advanced).
+ */
+ simplekeys: {
+ value: true,
+ validator: function(val) {
+ return Y.Lang.isBoolean(val);
+ }
+ }
+ }
+});
+
+M.editor_atto = M.editor_atto || {};
+Y.mix(M.editor_atto, {
+ /**
+ * An associative collection of shortcut objects that have been bound to the editors on the page.
+ * @protected
+ * @namespace M.editor_atto
+ * @property shortcutdelegations
+ * @type Object
+ */
+ shortcutdelegations: {},
+
+ /**
+ * Adds a button shortcut given a configuration object containing properties for it.
+ *
+ * The config object must contain at least action and keys.
+ * For more details see {@link SHORTCUT()}
+ *
+ * @static
+ * @namespace M.editor_atto
+ * @method add_button_shortcut
+ * @param {Object} config A configuration object containing at least action and keys.
+ * @return SHORTCUT
+ */
+ add_button_shortcut: function(config) {
+ var shortcut = new SHORTCUT(config);
+ return this.register_button_shortcut(shortcut);
+ },
+
+ /**
+ * Registers a shortcut object and binds it to the editors being displayed on the current page.
+ *
+ * @static
+ * @namespace M.editor_atto
+ * @method register_button_shortcut
+ * @param {SHORTCUT} shortcut The shortcut object to add.
+ * @return SHORTCUT
+ */
+ register_button_shortcut: function(shortcut) {
+ var action = shortcut.get(ATTRS.action),
+ keys = shortcut.get(ATTRS.keys);
+ if (!M.editor_atto.shortcutdelegations[action] && keys) {
+ Y.log('Atto shortcut registered: ' + keys + ' now triggers ' + action, 'debug', NS);
+ M.editor_atto.shortcutdelegations[action] = shortcut.bind(Y.one('body'), CSS.contenteditable);
+ }
+ return M.editor_atto.shortcutdelegations[action];
+ }
+});
diff --git a/lib/editor/atto/yui/src/editor/meta/editor.json b/lib/editor/atto/yui/src/editor/meta/editor.json
index 7f887402add..699906d4dd0 100644
--- a/lib/editor/atto/yui/src/editor/meta/editor.json
+++ b/lib/editor/atto/yui/src/editor/meta/editor.json
@@ -11,5 +11,13 @@
"yui-throttle",
"moodle-core-notification"
]
+ },
+ "moodle-editor_atto-editor-shortcut": {
+ "requires": [
+ "node",
+ "event",
+ "event-custom",
+ "moodle-editor_atto-editor"
+ ]
}
}