Merge branch 'MDL-46387-m29-master' of https://github.com/dthies/moodle into MOODLE_29_STABLE

This commit is contained in:
Dan Poltawski
2015-08-18 12:22:16 +01:00
20 changed files with 847 additions and 23 deletions
@@ -19,3 +19,20 @@ Feature: Atto subscript button
And I follow "Edit profile"
Then I should see "<sub>Submarine</sub>"
@javascript
Scenario: Subscript some text in enclosed in superscript
Given I log in as "admin"
And I follow "Profile" in the user menu
And I follow "Edit profile"
And I set the field "Description" to "<sup>Submarine</sup>"
And I select the text in the "Description" Atto editor
And I click on "Show more buttons" "button"
When I click on "Subscript" "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 follow "Edit profile"
Then I should see "<sub>Submarine</sub>"
@@ -34,13 +34,55 @@ YUI.add('moodle-atto_subscript-button', function (Y, NAME) {
*/
Y.namespace('M.atto_subscript').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
/**
* A rangy object to alter CSS classes.
*
* @property _subscriptApplier
* @type Object
* @private
*/
_subscriptApplier: null,
/**
* A rangy object to alter CSS classes.
*
* @property _superscriptApplier
* @type Object
* @private
*/
_superscriptApplier: null,
initializer: function() {
this.addBasicButton({
exec: 'subscript',
this.addButton({
buttonName: 'subscript',
callback: this.toggleSubscript,
icon: 'e/subscript',
inlineFormat: true,
// Watch the following tags and add/remove highlighting as appropriate:
tags: 'sub'
});
this._subscriptApplier = window.rangy.createCssClassApplier("editor-subscript");
this._superscriptApplier = window.rangy.createCssClassApplier("editor-superscript");
},
/**
* Toggle subscripts in selection
*
* @method toggleSubscript
*/
toggleSubscript: function() {
// Replace all the sub and sup tags.
this.get('host').changeToCSS('sup', 'editor-superscript');
this.get('host').changeToCSS('sub', 'editor-subscript');
// Remove all superscripts inselection and toggle subscript.
this._superscriptApplier.undoToSelection();
this._subscriptApplier.toggleSelection();
// Replace CSS classes with tags.
this.get('host').changeToTags('editor-subscript', 'sub');
this.get('host').changeToTags('editor-superscript', 'sup');
}
});
@@ -1 +1 @@
YUI.add("moodle-atto_subscript-button",function(e,t){e.namespace("M.atto_subscript").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{initializer:function(){this.addBasicButton({exec:"subscript",tags:"sub"})}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
YUI.add("moodle-atto_subscript-button",function(e,t){e.namespace("M.atto_subscript").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_subscriptApplier:null,_superscriptApplier:null,initializer:function(){this.addButton({buttonName:"subscript",callback:this.toggleSubscript,icon:"e/subscript",inlineFormat:!0,tags:"sub"}),this._subscriptApplier=window.rangy.createCssClassApplier("editor-subscript"),this._superscriptApplier=window.rangy.createCssClassApplier("editor-superscript")},toggleSubscript:function(){this.get("host").changeToCSS("sup","editor-superscript"),this.get("host").changeToCSS("sub","editor-subscript"),this._superscriptApplier.undoToSelection(),this._subscriptApplier.toggleSelection(),this.get("host").changeToTags("editor-subscript","sub"),this.get("host").changeToTags("editor-superscript","sup")}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
@@ -34,13 +34,55 @@ YUI.add('moodle-atto_subscript-button', function (Y, NAME) {
*/
Y.namespace('M.atto_subscript').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
/**
* A rangy object to alter CSS classes.
*
* @property _subscriptApplier
* @type Object
* @private
*/
_subscriptApplier: null,
/**
* A rangy object to alter CSS classes.
*
* @property _superscriptApplier
* @type Object
* @private
*/
_superscriptApplier: null,
initializer: function() {
this.addBasicButton({
exec: 'subscript',
this.addButton({
buttonName: 'subscript',
callback: this.toggleSubscript,
icon: 'e/subscript',
inlineFormat: true,
// Watch the following tags and add/remove highlighting as appropriate:
tags: 'sub'
});
this._subscriptApplier = window.rangy.createCssClassApplier("editor-subscript");
this._superscriptApplier = window.rangy.createCssClassApplier("editor-superscript");
},
/**
* Toggle subscripts in selection
*
* @method toggleSubscript
*/
toggleSubscript: function() {
// Replace all the sub and sup tags.
this.get('host').changeToCSS('sup', 'editor-superscript');
this.get('host').changeToCSS('sub', 'editor-subscript');
// Remove all superscripts inselection and toggle subscript.
this._superscriptApplier.undoToSelection();
this._subscriptApplier.toggleSelection();
// Replace CSS classes with tags.
this.get('host').changeToTags('editor-subscript', 'sub');
this.get('host').changeToTags('editor-superscript', 'sup');
}
});
@@ -32,12 +32,54 @@
*/
Y.namespace('M.atto_subscript').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
/**
* A rangy object to alter CSS classes.
*
* @property _subscriptApplier
* @type Object
* @private
*/
_subscriptApplier: null,
/**
* A rangy object to alter CSS classes.
*
* @property _superscriptApplier
* @type Object
* @private
*/
_superscriptApplier: null,
initializer: function() {
this.addBasicButton({
exec: 'subscript',
this.addButton({
buttonName: 'subscript',
callback: this.toggleSubscript,
icon: 'e/subscript',
inlineFormat: true,
// Watch the following tags and add/remove highlighting as appropriate:
tags: 'sub'
});
this._subscriptApplier = window.rangy.createCssClassApplier("editor-subscript");
this._superscriptApplier = window.rangy.createCssClassApplier("editor-superscript");
},
/**
* Toggle subscripts in selection
*
* @method toggleSubscript
*/
toggleSubscript: function() {
// Replace all the sub and sup tags.
this.get('host').changeToCSS('sup', 'editor-superscript');
this.get('host').changeToCSS('sub', 'editor-subscript');
// Remove all superscripts inselection and toggle subscript.
this._superscriptApplier.undoToSelection();
this._subscriptApplier.toggleSelection();
// Replace CSS classes with tags.
this.get('host').changeToTags('editor-subscript', 'sub');
this.get('host').changeToTags('editor-superscript', 'sup');
}
});
@@ -19,3 +19,20 @@ Feature: Atto superscript button
And I follow "Edit profile"
Then I should see "<sup>Helicopter</sup>"
@javascript
Scenario: Superscript some text that is enclosed in subscript
Given I log in as "admin"
And I follow "Profile" in the user menu
And I follow "Edit profile"
And I set the field "Description" to "<sub>Helicopter</sub>"
And I select the text in the "Description" Atto editor
And I click on "Show more buttons" "button"
When I click on "Superscript" "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 follow "Edit profile"
Then I should see "<sup>Helicopter</sup>"
@@ -34,13 +34,55 @@ YUI.add('moodle-atto_superscript-button', function (Y, NAME) {
*/
Y.namespace('M.atto_superscript').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
/**
* A rangy object to alter CSS classes.
*
* @property _subscriptApplier
* @type Object
* @private
*/
_subscriptApplier: null,
/**
* A rangy object to alter CSS classes.
*
* @property _superscriptApplier
* @type Object
* @private
*/
_superscriptApplier: null,
initializer: function() {
this.addBasicButton({
exec: 'superscript',
this.addButton({
buttonName: 'superscript',
inlineFormat: true,
callback: this.toggleSuperscript,
icon: 'e/superscript',
// Watch the following tags and add/remove highlighting as appropriate:
tags: 'sup'
});
this._subscriptApplier = window.rangy.createCssClassApplier("editor-subscript");
this._superscriptApplier = window.rangy.createCssClassApplier("editor-superscript");
},
/**
* Toggle superscripts in selection
*
* @method toggleSuperscript
*/
toggleSuperscript: function() {
// Replace all the sub and sup tags.
this.get('host').changeToCSS('sub', 'editor-subscript');
this.get('host').changeToCSS('sup', 'editor-superscript');
// Remove all subscripts inselection and toggle superscript.
this._superscriptApplier.toggleSelection();
this._subscriptApplier.undoToSelection();
// Replace CSS classes with tags.
this.get('host').changeToTags('editor-superscript', 'sup');
this.get('host').changeToTags('editor-subscript', 'sub');
}
});
@@ -1 +1 @@
YUI.add("moodle-atto_superscript-button",function(e,t){e.namespace("M.atto_superscript").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{initializer:function(){this.addBasicButton({exec:"superscript",tags:"sup"})}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
YUI.add("moodle-atto_superscript-button",function(e,t){e.namespace("M.atto_superscript").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_subscriptApplier:null,_superscriptApplier:null,initializer:function(){this.addButton({buttonName:"superscript",inlineFormat:!0,callback:this.toggleSuperscript,icon:"e/superscript",tags:"sup"}),this._subscriptApplier=window.rangy.createCssClassApplier("editor-subscript"),this._superscriptApplier=window.rangy.createCssClassApplier("editor-superscript")},toggleSuperscript:function(){this.get("host").changeToCSS("sub","editor-subscript"),this.get("host").changeToCSS("sup","editor-superscript"),this._superscriptApplier.toggleSelection(),this._subscriptApplier.undoToSelection(),this.get("host").changeToTags("editor-superscript","sup"),this.get("host").changeToTags("editor-subscript","sub")}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
@@ -34,13 +34,55 @@ YUI.add('moodle-atto_superscript-button', function (Y, NAME) {
*/
Y.namespace('M.atto_superscript').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
/**
* A rangy object to alter CSS classes.
*
* @property _subscriptApplier
* @type Object
* @private
*/
_subscriptApplier: null,
/**
* A rangy object to alter CSS classes.
*
* @property _superscriptApplier
* @type Object
* @private
*/
_superscriptApplier: null,
initializer: function() {
this.addBasicButton({
exec: 'superscript',
this.addButton({
buttonName: 'superscript',
inlineFormat: true,
callback: this.toggleSuperscript,
icon: 'e/superscript',
// Watch the following tags and add/remove highlighting as appropriate:
tags: 'sup'
});
this._subscriptApplier = window.rangy.createCssClassApplier("editor-subscript");
this._superscriptApplier = window.rangy.createCssClassApplier("editor-superscript");
},
/**
* Toggle superscripts in selection
*
* @method toggleSuperscript
*/
toggleSuperscript: function() {
// Replace all the sub and sup tags.
this.get('host').changeToCSS('sub', 'editor-subscript');
this.get('host').changeToCSS('sup', 'editor-superscript');
// Remove all subscripts inselection and toggle superscript.
this._superscriptApplier.toggleSelection();
this._subscriptApplier.undoToSelection();
// Replace CSS classes with tags.
this.get('host').changeToTags('editor-superscript', 'sup');
this.get('host').changeToTags('editor-subscript', 'sub');
}
});
@@ -32,12 +32,54 @@
*/
Y.namespace('M.atto_superscript').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
/**
* A rangy object to alter CSS classes.
*
* @property _subscriptApplier
* @type Object
* @private
*/
_subscriptApplier: null,
/**
* A rangy object to alter CSS classes.
*
* @property _superscriptApplier
* @type Object
* @private
*/
_superscriptApplier: null,
initializer: function() {
this.addBasicButton({
exec: 'superscript',
this.addButton({
buttonName: 'superscript',
callback: this.toggleSuperscript,
icon: 'e/superscript',
inlineFormat: true,
// Watch the following tags and add/remove highlighting as appropriate:
tags: 'sup'
});
this._subscriptApplier = window.rangy.createCssClassApplier("editor-subscript");
this._superscriptApplier = window.rangy.createCssClassApplier("editor-superscript");
},
/**
* Toggle superscripts in selection
*
* @method toggleSuperscript
*/
toggleSuperscript: function() {
// Replace all the sub and sup tags.
this.get('host').changeToCSS('sub', 'editor-subscript');
this.get('host').changeToCSS('sup', 'editor-superscript');
// Remove all subscripts inselection and toggle superscript.
this._superscriptApplier.toggleSelection();
this._subscriptApplier.undoToSelection();
// Replace CSS classes with tags.
this.get('host').changeToTags('editor-superscript', 'sup');
this.get('host').changeToTags('editor-subscript', 'sub');
}
});
@@ -1410,6 +1410,175 @@ Y.Base.mix(Y.M.editor_atto.Editor, [EditorClean]);
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* @module moodle-editor_atto-editor
* @submodule commands
*/
/**
* Selection functions for the Atto editor.
*
* See {{#crossLink "M.editor_atto.Editor"}}{{/crossLink}} for details.
*
* @namespace M.editor_atto
* @class EditorCommand
*/
function EditorCommand() {}
EditorCommand.ATTRS= {
};
EditorCommand.prototype = {
/**
* Applies a callback method to editor if selection is uncollapsed or waits for input to select first.
* @method applyFormat
* @param e EventTarget Event to be passed to callback if selection is uncollapsed
* @param method callback A callback method which changes editor when text is selected.
* @param object context Context to be used for callback method
* @param array args Array of arguments to pass to callback
*/
applyFormat: function(e, callback, context, args) {
function handleInsert(e, callback, context, args, anchorNode, anchorOffset) {
// After something is inputed, select it and apply the formating function.
Y.soon(Y.bind(function(e, callback, context, args, anchorNode, anchorOffset) {
var selection = window.rangy.getSelection();
// Set the start of the selection to where it was when the method was first called.
var range = selection.getRangeAt(0);
range.setStart(anchorNode, anchorOffset);
selection.setSingleRange(range);
// Now apply callback to the new text that is selected.
callback.apply(context, [e, args]);
// Collapse selection so cursor is at end of inserted material.
selection.collapseToEnd();
}, this, e, callback, context, args, anchorNode, anchorOffset));
}
// Set default context for the method.
context = context || this;
// Check whether range is collapsed.
var selection = window.rangy.getSelection();
if (selection.isCollapsed) {
// Selection is collapsed so listen for input into editor.
var handle = this.editor.once('input', handleInsert, this, callback, context, args,
selection.anchorNode, selection.anchorOffset);
// Cancel if selection changes before input.
this.editor.onceAfter(['click', 'selectstart'], handle.detach, handle);
return;
}
// The range is not collapsed; so apply callback method immediately.
callback.apply(context, [e, args]);
},
/**
* Replaces all the tags in a node list with new type.
* @method replaceTags
* @param NodeList nodelist
* @param String tag
*/
replaceTags: function(nodelist, tag) {
// We mark elements in the node list for iterations.
nodelist.setAttribute('data-iterate', true);
var node = this.editor.one('[data-iterate="true"]');
while (node) {
var clone = Y.Node.create('<' + tag + ' />')
.setAttrs(node.getAttrs())
.removeAttribute('data-iterate');
// Copy class and style if not blank.
if (node.getAttribute('style')) {
clone.setAttribute('style', node.getAttribute('style'));
}
if (node.getAttribute('class')) {
clone.setAttribute('class', node.getAttribute('class'));
}
// We use childNodes here because we are interested in both type 1 and 3 child nodes.
var children = node.getDOMNode().childNodes, child;
child = children[0];
while (typeof child !== "undefined") {
clone.append(child);
child = children[0];
}
node.replace(clone);
node = this.editor.one('[data-iterate="true"]');
}
},
/**
* Change all tags with given type to a span with CSS class attribute.
* @method changeToCSS
* @param String tag Tag type to be changed to span
* @param String markerClass CSS class that corresponds to desired tag
*/
changeToCSS: function(tag, markerClass) {
// Save the selection.
var selection = window.rangy.saveSelection();
// Remove display:none from rangy markers so browser doesn't delete them.
this.editor.all('.rangySelectionBoundary').setStyle('display', null);
// Replace tags with CSS classes.
this.editor.all(tag).addClass(markerClass);
this.replaceTags(this.editor.all('.' + markerClass), 'span');
// Restore selection and toggle class.
window.rangy.restoreSelection(selection);
},
/**
* Change spans with CSS classes in editor into elements with given tag.
* @method changeToCSS
* @param String markerClass CSS class that corresponds to desired tag
* @param String tag New tag type to be created
*/
changeToTags: function(markerClass, tag) {
// Save the selection.
var selection = window.rangy.saveSelection();
// Remove display:none from rangy markers so browser doesn't delete them.
this.editor.all('.rangySelectionBoundary').setStyle('display', null);
// Replace spans with given tag.
this.replaceTags(this.editor.all('span[class="' + markerClass + '"]'), tag);
this.editor.all(tag + '[class="' + markerClass + '"]').removeAttribute('class');
this.editor.all('.' + markerClass).each(function(n) {
n.wrap('<' + tag + '/>');
n.removeClass(markerClass);
});
// Remove CSS classes.
this.editor.all('[class="' + markerClass + '"]').removeAttribute('class');
this.editor.all(tag).removeClass(markerClass);
// Restore selection.
window.rangy.restoreSelection(selection);
}
};
Y.Base.mix(Y.M.editor_atto.Editor, [EditorCommand]);
// 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/>.
/**
* @module moodle-editor_atto-editor
* @submodule toolbar
@@ -2392,6 +2561,7 @@ Y.Base.mix(Y.M.editor_atto.Editor, [EditorFilepicker]);
"event",
"event-simulate",
"event-custom",
"node-event-html5",
"yui-throttle",
"moodle-core-notification-dialogue",
"moodle-core-notification-confirm",
File diff suppressed because one or more lines are too long
@@ -1399,6 +1399,175 @@ Y.Base.mix(Y.M.editor_atto.Editor, [EditorClean]);
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* @module moodle-editor_atto-editor
* @submodule commands
*/
/**
* Selection functions for the Atto editor.
*
* See {{#crossLink "M.editor_atto.Editor"}}{{/crossLink}} for details.
*
* @namespace M.editor_atto
* @class EditorCommand
*/
function EditorCommand() {}
EditorCommand.ATTRS= {
};
EditorCommand.prototype = {
/**
* Applies a callback method to editor if selection is uncollapsed or waits for input to select first.
* @method applyFormat
* @param e EventTarget Event to be passed to callback if selection is uncollapsed
* @param method callback A callback method which changes editor when text is selected.
* @param object context Context to be used for callback method
* @param array args Array of arguments to pass to callback
*/
applyFormat: function(e, callback, context, args) {
function handleInsert(e, callback, context, args, anchorNode, anchorOffset) {
// After something is inputed, select it and apply the formating function.
Y.soon(Y.bind(function(e, callback, context, args, anchorNode, anchorOffset) {
var selection = window.rangy.getSelection();
// Set the start of the selection to where it was when the method was first called.
var range = selection.getRangeAt(0);
range.setStart(anchorNode, anchorOffset);
selection.setSingleRange(range);
// Now apply callback to the new text that is selected.
callback.apply(context, [e, args]);
// Collapse selection so cursor is at end of inserted material.
selection.collapseToEnd();
}, this, e, callback, context, args, anchorNode, anchorOffset));
}
// Set default context for the method.
context = context || this;
// Check whether range is collapsed.
var selection = window.rangy.getSelection();
if (selection.isCollapsed) {
// Selection is collapsed so listen for input into editor.
var handle = this.editor.once('input', handleInsert, this, callback, context, args,
selection.anchorNode, selection.anchorOffset);
// Cancel if selection changes before input.
this.editor.onceAfter(['click', 'selectstart'], handle.detach, handle);
return;
}
// The range is not collapsed; so apply callback method immediately.
callback.apply(context, [e, args]);
},
/**
* Replaces all the tags in a node list with new type.
* @method replaceTags
* @param NodeList nodelist
* @param String tag
*/
replaceTags: function(nodelist, tag) {
// We mark elements in the node list for iterations.
nodelist.setAttribute('data-iterate', true);
var node = this.editor.one('[data-iterate="true"]');
while (node) {
var clone = Y.Node.create('<' + tag + ' />')
.setAttrs(node.getAttrs())
.removeAttribute('data-iterate');
// Copy class and style if not blank.
if (node.getAttribute('style')) {
clone.setAttribute('style', node.getAttribute('style'));
}
if (node.getAttribute('class')) {
clone.setAttribute('class', node.getAttribute('class'));
}
// We use childNodes here because we are interested in both type 1 and 3 child nodes.
var children = node.getDOMNode().childNodes, child;
child = children[0];
while (typeof child !== "undefined") {
clone.append(child);
child = children[0];
}
node.replace(clone);
node = this.editor.one('[data-iterate="true"]');
}
},
/**
* Change all tags with given type to a span with CSS class attribute.
* @method changeToCSS
* @param String tag Tag type to be changed to span
* @param String markerClass CSS class that corresponds to desired tag
*/
changeToCSS: function(tag, markerClass) {
// Save the selection.
var selection = window.rangy.saveSelection();
// Remove display:none from rangy markers so browser doesn't delete them.
this.editor.all('.rangySelectionBoundary').setStyle('display', null);
// Replace tags with CSS classes.
this.editor.all(tag).addClass(markerClass);
this.replaceTags(this.editor.all('.' + markerClass), 'span');
// Restore selection and toggle class.
window.rangy.restoreSelection(selection);
},
/**
* Change spans with CSS classes in editor into elements with given tag.
* @method changeToCSS
* @param String markerClass CSS class that corresponds to desired tag
* @param String tag New tag type to be created
*/
changeToTags: function(markerClass, tag) {
// Save the selection.
var selection = window.rangy.saveSelection();
// Remove display:none from rangy markers so browser doesn't delete them.
this.editor.all('.rangySelectionBoundary').setStyle('display', null);
// Replace spans with given tag.
this.replaceTags(this.editor.all('span[class="' + markerClass + '"]'), tag);
this.editor.all(tag + '[class="' + markerClass + '"]').removeAttribute('class');
this.editor.all('.' + markerClass).each(function(n) {
n.wrap('<' + tag + '/>');
n.removeClass(markerClass);
});
// Remove CSS classes.
this.editor.all('[class="' + markerClass + '"]').removeAttribute('class');
this.editor.all(tag).removeClass(markerClass);
// Restore selection.
window.rangy.restoreSelection(selection);
}
};
Y.Base.mix(Y.M.editor_atto.Editor, [EditorCommand]);
// 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/>.
/**
* @module moodle-editor_atto-editor
* @submodule toolbar
@@ -2379,6 +2548,7 @@ Y.Base.mix(Y.M.editor_atto.Editor, [EditorFilepicker]);
"event",
"event-simulate",
"event-custom",
"node-event-html5",
"yui-throttle",
"moodle-core-notification-dialogue",
"moodle-core-notification-confirm",
@@ -690,9 +690,18 @@ EditorPluginButtons.prototype = {
inheritFrom = {};
}
// First we wrap the callback in function to handle formating of text inserted into collapsed selection.
config.inlineFormat = config.inlineFormat || inheritFrom.inlineFormat;
config._inlineCallback = config.callback || inheritFrom.callback;
config._callback = config.callback || inheritFrom.callback;
if (config.inlineFormat && typeof config._inlineCallback === 'function') {
config._callback = function(e, args) {
this.get('host').applyFormat(e, config._inlineCallback, this, args);
};
}
// We wrap the callback in function to prevent the default action, check whether the editor is
// active and focus it, and then mark the field as updated.
config._callback = config.callback || inheritFrom.callback;
config.callback = Y.rbind(this._callbackWrapper, this, config._callback, config.callbackArgs);
config._callbackNormalized = true;
File diff suppressed because one or more lines are too long
@@ -688,9 +688,18 @@ EditorPluginButtons.prototype = {
inheritFrom = {};
}
// First we wrap the callback in function to handle formating of text inserted into collapsed selection.
config.inlineFormat = config.inlineFormat || inheritFrom.inlineFormat;
config._inlineCallback = config.callback || inheritFrom.callback;
config._callback = config.callback || inheritFrom.callback;
if (config.inlineFormat && typeof config._inlineCallback === 'function') {
config._callback = function(e, args) {
this.get('host').applyFormat(e, config._inlineCallback, this, args);
};
}
// We wrap the callback in function to prevent the default action, check whether the editor is
// active and focus it, and then mark the field as updated.
config._callback = config.callback || inheritFrom.callback;
config.callback = Y.rbind(this._callbackWrapper, this, config._callback, config.callbackArgs);
config._callbackNormalized = true;
@@ -8,6 +8,7 @@
"textarea.js",
"autosave.js",
"clean.js",
"commands.js",
"toolbar.js",
"toolbar-keyboardnav.js",
"selection.js",
+169
View File
@@ -0,0 +1,169 @@
// 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/>.
/**
* @module moodle-editor_atto-editor
* @submodule commands
*/
/**
* Selection functions for the Atto editor.
*
* See {{#crossLink "M.editor_atto.Editor"}}{{/crossLink}} for details.
*
* @namespace M.editor_atto
* @class EditorCommand
*/
function EditorCommand() {}
EditorCommand.ATTRS= {
};
EditorCommand.prototype = {
/**
* Applies a callback method to editor if selection is uncollapsed or waits for input to select first.
* @method applyFormat
* @param e EventTarget Event to be passed to callback if selection is uncollapsed
* @param method callback A callback method which changes editor when text is selected.
* @param object context Context to be used for callback method
* @param array args Array of arguments to pass to callback
*/
applyFormat: function(e, callback, context, args) {
function handleInsert(e, callback, context, args, anchorNode, anchorOffset) {
// After something is inputed, select it and apply the formating function.
Y.soon(Y.bind(function(e, callback, context, args, anchorNode, anchorOffset) {
var selection = window.rangy.getSelection();
// Set the start of the selection to where it was when the method was first called.
var range = selection.getRangeAt(0);
range.setStart(anchorNode, anchorOffset);
selection.setSingleRange(range);
// Now apply callback to the new text that is selected.
callback.apply(context, [e, args]);
// Collapse selection so cursor is at end of inserted material.
selection.collapseToEnd();
}, this, e, callback, context, args, anchorNode, anchorOffset));
}
// Set default context for the method.
context = context || this;
// Check whether range is collapsed.
var selection = window.rangy.getSelection();
if (selection.isCollapsed) {
// Selection is collapsed so listen for input into editor.
var handle = this.editor.once('input', handleInsert, this, callback, context, args,
selection.anchorNode, selection.anchorOffset);
// Cancel if selection changes before input.
this.editor.onceAfter(['click', 'selectstart'], handle.detach, handle);
return;
}
// The range is not collapsed; so apply callback method immediately.
callback.apply(context, [e, args]);
},
/**
* Replaces all the tags in a node list with new type.
* @method replaceTags
* @param NodeList nodelist
* @param String tag
*/
replaceTags: function(nodelist, tag) {
// We mark elements in the node list for iterations.
nodelist.setAttribute('data-iterate', true);
var node = this.editor.one('[data-iterate="true"]');
while (node) {
var clone = Y.Node.create('<' + tag + ' />')
.setAttrs(node.getAttrs())
.removeAttribute('data-iterate');
// Copy class and style if not blank.
if (node.getAttribute('style')) {
clone.setAttribute('style', node.getAttribute('style'));
}
if (node.getAttribute('class')) {
clone.setAttribute('class', node.getAttribute('class'));
}
// We use childNodes here because we are interested in both type 1 and 3 child nodes.
var children = node.getDOMNode().childNodes, child;
child = children[0];
while (typeof child !== "undefined") {
clone.append(child);
child = children[0];
}
node.replace(clone);
node = this.editor.one('[data-iterate="true"]');
}
},
/**
* Change all tags with given type to a span with CSS class attribute.
* @method changeToCSS
* @param String tag Tag type to be changed to span
* @param String markerClass CSS class that corresponds to desired tag
*/
changeToCSS: function(tag, markerClass) {
// Save the selection.
var selection = window.rangy.saveSelection();
// Remove display:none from rangy markers so browser doesn't delete them.
this.editor.all('.rangySelectionBoundary').setStyle('display', null);
// Replace tags with CSS classes.
this.editor.all(tag).addClass(markerClass);
this.replaceTags(this.editor.all('.' + markerClass), 'span');
// Restore selection and toggle class.
window.rangy.restoreSelection(selection);
},
/**
* Change spans with CSS classes in editor into elements with given tag.
* @method changeToCSS
* @param String markerClass CSS class that corresponds to desired tag
* @param String tag New tag type to be created
*/
changeToTags: function(markerClass, tag) {
// Save the selection.
var selection = window.rangy.saveSelection();
// Remove display:none from rangy markers so browser doesn't delete them.
this.editor.all('.rangySelectionBoundary').setStyle('display', null);
// Replace spans with given tag.
this.replaceTags(this.editor.all('span[class="' + markerClass + '"]'), tag);
this.editor.all(tag + '[class="' + markerClass + '"]').removeAttribute('class');
this.editor.all('.' + markerClass).each(function(n) {
n.wrap('<' + tag + '/>');
n.removeClass(markerClass);
});
// Remove CSS classes.
this.editor.all('[class="' + markerClass + '"]').removeAttribute('class');
this.editor.all(tag).removeClass(markerClass);
// Restore selection.
window.rangy.restoreSelection(selection);
}
};
Y.Base.mix(Y.M.editor_atto.Editor, [EditorCommand]);
+10 -1
View File
@@ -540,9 +540,18 @@ EditorPluginButtons.prototype = {
inheritFrom = {};
}
// First we wrap the callback in function to handle formating of text inserted into collapsed selection.
config.inlineFormat = config.inlineFormat || inheritFrom.inlineFormat;
config._inlineCallback = config.callback || inheritFrom.callback;
config._callback = config.callback || inheritFrom.callback;
if (config.inlineFormat && typeof config._inlineCallback === 'function') {
config._callback = function(e, args) {
this.get('host').applyFormat(e, config._inlineCallback, this, args);
};
}
// We wrap the callback in function to prevent the default action, check whether the editor is
// active and focus it, and then mark the field as updated.
config._callback = config.callback || inheritFrom.callback;
config.callback = Y.rbind(this._callbackWrapper, this, config._callback, config.callbackArgs);
config._callbackNormalized = true;
@@ -9,6 +9,7 @@
"event",
"event-simulate",
"event-custom",
"node-event-html5",
"yui-throttle",
"moodle-core-notification-dialogue",
"moodle-core-notification-confirm",