AutorÃa | Ultima modificación | Ver Log |
// 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();
// Save save selection and editor contents.
this.saveSelection();
this.updateOriginal();
}, 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]);
// Save save selection and editor contents.
this.saveSelection();
this.updateOriginal();
},
/**
* 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;
var 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]);