MDL-44040 editor: implemented shortcuts for bold, italic, underline and undo

This commit is contained in:
Sam Hemelryk 2014-02-20 14:53:50 +13:00 committed by Damyon Wiese
parent f331980308
commit bdfbdeeb64
30 changed files with 836 additions and 21 deletions

View File

@ -45,6 +45,7 @@ M.atto_bold = M.atto_bold || {
var iconurl = M.util.image_url('e/bold', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'bold', iconurl, params.group, click);
M.editor_atto.add_button_shortcut({action: 'bold', keys: 66});
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
@ -61,4 +62,4 @@ M.atto_bold = M.atto_bold || {
};
}, '@VERSION@', {"requires": ["node"]});
}, '@VERSION@', {"requires": ["node", "moodle-editor_atto-editor-shortcut"]});

View File

@ -1 +1 @@
YUI.add("moodle-atto_bold-button",function(e,t){var n={TAGS:"b,strong"};M.atto_bold=M.atto_bold||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("bold",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/bold","core");M.editor_atto.add_toolbar_button(e.elementid,"bold",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"bold"):M.editor_atto.remove_widget_highlight(e.elementid,"bold")})}}},"@VERSION@",{requires:["node"]});
YUI.add("moodle-atto_bold-button",function(e,t){var n={TAGS:"b,strong"};M.atto_bold=M.atto_bold||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("bold",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/bold","core");M.editor_atto.add_toolbar_button(e.elementid,"bold",r,e.group,t),M.editor_atto.add_button_shortcut({action:"bold",keys:66});var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"bold"):M.editor_atto.remove_widget_highlight(e.elementid,"bold")})}}},"@VERSION@",{requires:["node","moodle-editor_atto-editor-shortcut"]});

View File

@ -45,6 +45,7 @@ M.atto_bold = M.atto_bold || {
var iconurl = M.util.image_url('e/bold', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'bold', iconurl, params.group, click);
M.editor_atto.add_button_shortcut({action: 'bold', keys: 66});
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
@ -61,4 +62,4 @@ M.atto_bold = M.atto_bold || {
};
}, '@VERSION@', {"requires": ["node"]});
}, '@VERSION@', {"requires": ["node", "moodle-editor_atto-editor-shortcut"]});

View File

@ -43,6 +43,7 @@ M.atto_bold = M.atto_bold || {
var iconurl = M.util.image_url('e/bold', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'bold', iconurl, params.group, click);
M.editor_atto.add_button_shortcut({action: 'bold', keys: 66});
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based

View File

@ -1,5 +1,8 @@
{
"moodle-atto_bold-button": {
"requires": ["node"]
"requires": [
"node",
"moodle-editor_atto-editor-shortcut"
]
}
}

View File

@ -45,6 +45,7 @@ M.atto_italic = M.atto_italic || {
var iconurl = M.util.image_url('e/italic', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'italic', iconurl, params.group, click);
M.editor_atto.add_button_shortcut({action: 'italic', keys: 73});
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
@ -61,4 +62,4 @@ M.atto_italic = M.atto_italic || {
};
}, '@VERSION@', {"requires": ["node"]});
}, '@VERSION@', {"requires": ["node", "moodle-editor_atto-editor-shortcut"]});

View File

@ -1 +1 @@
YUI.add("moodle-atto_italic-button",function(e,t){var n={TAGS:"i"};M.atto_italic=M.atto_italic||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("italic",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/italic","core");M.editor_atto.add_toolbar_button(e.elementid,"italic",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"italic"):M.editor_atto.remove_widget_highlight(e.elementid,"italic")})}}},"@VERSION@",{requires:["node"]});
YUI.add("moodle-atto_italic-button",function(e,t){var n={TAGS:"i"};M.atto_italic=M.atto_italic||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("italic",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/italic","core");M.editor_atto.add_toolbar_button(e.elementid,"italic",r,e.group,t),M.editor_atto.add_button_shortcut({action:"italic",keys:73});var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"italic"):M.editor_atto.remove_widget_highlight(e.elementid,"italic")})}}},"@VERSION@",{requires:["node","moodle-editor_atto-editor-shortcut"]});

View File

@ -45,6 +45,7 @@ M.atto_italic = M.atto_italic || {
var iconurl = M.util.image_url('e/italic', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'italic', iconurl, params.group, click);
M.editor_atto.add_button_shortcut({action: 'italic', keys: 73});
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
@ -61,4 +62,4 @@ M.atto_italic = M.atto_italic || {
};
}, '@VERSION@', {"requires": ["node"]});
}, '@VERSION@', {"requires": ["node", "moodle-editor_atto-editor-shortcut"]});

View File

@ -43,6 +43,7 @@ M.atto_italic = M.atto_italic || {
var iconurl = M.util.image_url('e/italic', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'italic', iconurl, params.group, click);
M.editor_atto.add_button_shortcut({action: 'italic', keys: 73});
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based

View File

@ -1,5 +1,5 @@
{
"moodle-atto_italic-button": {
"requires": ["node"]
"requires": ["node", "moodle-editor_atto-editor-shortcut"]
}
}

View File

@ -45,6 +45,7 @@ M.atto_underline = M.atto_underline || {
var iconurl = M.util.image_url('e/underline', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'underline', iconurl, params.group, click);
M.editor_atto.add_button_shortcut({action: 'underline', keys: 85});
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
@ -61,4 +62,4 @@ M.atto_underline = M.atto_underline || {
};
}, '@VERSION@', {"requires": ["node"]});
}, '@VERSION@', {"requires": ["node", "moodle-editor_atto-editor-shortcut"]});

View File

@ -1 +1 @@
YUI.add("moodle-atto_underline-button",function(e,t){var n={TAGS:"u"};M.atto_underline=M.atto_underline||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("underline",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/underline","core");M.editor_atto.add_toolbar_button(e.elementid,"underline",r,e.group,t);var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"underline"):M.editor_atto.remove_widget_highlight(e.elementid,"underline")})}}},"@VERSION@",{requires:["node"]});
YUI.add("moodle-atto_underline-button",function(e,t){var n={TAGS:"u"};M.atto_underline=M.atto_underline||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("underline",!1,null),M.editor_atto.text_updated(t)},r=M.util.image_url("e/underline","core");M.editor_atto.add_toolbar_button(e.elementid,"underline",r,e.group,t),M.editor_atto.add_button_shortcut({action:"underline",keys:85});var i=M.editor_atto.get_editable_node(e.elementid);i.on("atto:selectionchanged",function(e){M.editor_atto.selection_filter_matches(e.elementid,n.TAGS,e.selectedNodes)?M.editor_atto.add_widget_highlight(e.elementid,"underline"):M.editor_atto.remove_widget_highlight(e.elementid,"underline")})}}},"@VERSION@",{requires:["node","moodle-editor_atto-editor-shortcut"]});

View File

@ -45,6 +45,7 @@ M.atto_underline = M.atto_underline || {
var iconurl = M.util.image_url('e/underline', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'underline', iconurl, params.group, click);
M.editor_atto.add_button_shortcut({action: 'underline', keys: 85});
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based
@ -61,4 +62,4 @@ M.atto_underline = M.atto_underline || {
};
}, '@VERSION@', {"requires": ["node"]});
}, '@VERSION@', {"requires": ["node", "moodle-editor_atto-editor-shortcut"]});

View File

@ -43,6 +43,7 @@ M.atto_underline = M.atto_underline || {
var iconurl = M.util.image_url('e/underline', 'core');
M.editor_atto.add_toolbar_button(params.elementid, 'underline', iconurl, params.group, click);
M.editor_atto.add_button_shortcut({action: 'underline', keys: 85});
// Attach an event listner to watch for "changes" in the contenteditable.
// This includes cursor changes, we check if the button should be active or not, based

View File

@ -1,5 +1,5 @@
{
"moodle-atto_underline-button": {
"requires": ["node"]
"requires": ["node", "moodle-editor_atto-editor-shortcut"]
}
}

View File

@ -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"]});

View File

@ -1 +1 @@
YUI.add("moodle-atto_undo-button",function(e,t){M.atto_undo=M.atto_undo||{browsersupportsundo:null,click_handler:function(e,t,n){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand(n,!1,null),M.editor_atto.text_updated(t)},undo_handler:function(e,t){M.atto_undo.click_handler(e,t,"undo")},redo_handler:function(e,t){M.atto_undo.click_handler(e,t,"redo")},test_undo_support:function(){var t=document.activeElement,n=!1,r=e.Node.create('<div id="attoundotesting" contenteditable="true"style="position: fixed; top: 0px; height:0px">a</div>');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"))}}}},"@VERSION@",{requires:["node"]});
YUI.add("moodle-atto_undo-button",function(e,t){M.atto_undo=M.atto_undo||{browsersupportsundo:null,click_handler:function(e,t,n){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand(n,!1,null),M.editor_atto.text_updated(t)},undo_handler:function(e,t){M.atto_undo.click_handler(e,t,"undo")},redo_handler:function(e,t){M.atto_undo.click_handler(e,t,"redo")},test_undo_support:function(){var t=document.activeElement,n=!1,r=e.Node.create('<div id="attoundotesting" contenteditable="true"style="position: fixed; top: 0px; height:0px">a</div>');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"]});

View File

@ -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"]});

View File

@ -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});
}
}
};

View File

@ -1,5 +1,5 @@
{
"moodle-atto_undo-button": {
"requires": ["node"]
"requires": ["node", "moodle-editor_atto-editor-shortcut"]
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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"]});

View File

@ -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"]});

View File

@ -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 <http://www.gnu.org/licenses/>.
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"]});

View File

@ -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('<div class="' + CSS.TOOLBAR + '" id="' + params.elementid + '_toolbar" role="toolbar" aria-live="off"/>');

File diff suppressed because one or more lines are too long

View File

@ -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('<div class="' + CSS.TOOLBAR + '" id="' + params.elementid + '_toolbar" role="toolbar" aria-live="off"/>');

View File

@ -7,6 +7,11 @@
"controlmenu.js",
"clean.js"
]
},
"moodle-editor_atto-editor-shortcut": {
"jsfiles": [
"shortcut.js"
]
}
}
}

View File

@ -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('<div class="' + CSS.TOOLBAR + '" id="' + params.elementid + '_toolbar" role="toolbar" aria-live="off"/>');

View File

@ -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 <http://www.gnu.org/licenses/>.
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];
}
});

View File

@ -11,5 +11,13 @@
"yui-throttle",
"moodle-core-notification"
]
},
"moodle-editor_atto-editor-shortcut": {
"requires": [
"node",
"event",
"event-custom",
"moodle-editor_atto-editor"
]
}
}