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/>.
/*
* @package atto_indent
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module moodle-atto_indent-button
*/
/**
* Atto text editor indent plugin.
*
* @namespace M.atto_indent
* @class button
* @extends M.editor_atto.EditorPlugin
*/
Y.namespace('M.atto_indent').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addButton({
icon: 'e/decrease_indent',
title: 'outdent',
buttonName: 'outdent',
callback: this.outdent
});
this.addButton({
icon: 'e/increase_indent',
title: 'indent',
buttonName: 'indent',
callback: this.indent
});
},
/**
* Indents the currently selected content.
*
* @method indent
*/
indent: function() {
// Save the current selection - we want to restore this.
var selection = window.rangy.saveSelection(),
blockquotes = this.editor.all('blockquote'),
count = blockquotes.size();
// Remove display:none from rangy markers so browser doesn't delete them.
this.editor.all('.rangySelectionBoundary').setStyle('display', null);
// Mark all existing block quotes in case the user has actually added some.
blockquotes.addClass('pre-existing');
// Run the indent command.
document.execCommand('indent', false, null);
// Fix indent list item.
this.fixupListItemsAfterIndent();
// Get all blockquotes, both existing and new.
blockquotes = this.editor.all('blockquote');
if (blockquotes.size() !== count) {
// There are new block quotes, the indent exec has wrapped some content in block quotes in order
// to indent the selected content.
// We don't want blockquotes, we're going to convert them to divs.
this.replaceBlockquote(this.editor);
// Finally restore the seelction. The content has changed - sometimes this works - but not always :(
window.rangy.restoreSelection(selection);
} else if (blockquotes.size() > 0) {
// There were no new blockquotes, this happens if the user is indenting/outdenting a list.
blockquotes.removeClass('pre-existing');
}
// Remove the selection markers - a clean up really.
window.rangy.removeMarkers(selection);
// Mark the text as having been updated.
this.markUpdated();
},
/**
* Outdents the currently selected content.
*
* @method outdent
*/
outdent: function() {
// Save the selection we will want to restore it.
var selection = window.rangy.saveSelection(),
blockquotes = this.editor.all('blockquote'),
count = blockquotes.size();
// Mark existing blockquotes so that we don't convert them later.
blockquotes.addClass('pre-existing');
// Replace all div indents with blockquote indents so that we can rely on the browser functionality.
this.replaceEditorIndents(this.editor);
// Restore the users selection - otherwise the next outdent operation won't work!
window.rangy.restoreSelection(selection);
// And save it once more.
selection = window.rangy.saveSelection();
// Outdent.
document.execCommand('outdent', false, null);
// Get all blockquotes so that we can work out what happened.
blockquotes = this.editor.all('blockquote');
if (blockquotes.size() !== count) {
// The number of blockquotes hasn't changed.
// This occurs when the user has outdented a list item.
this.replaceBlockquote(this.editor);
window.rangy.restoreSelection(selection);
} else if (blockquotes.size() > 0) {
// The number of blockquotes is the same and is more than 0 we just need to clean up the class
// we added to mark pre-existing blockquotes.
blockquotes.removeClass('pre-existing');
}
// Clean up any left over selection markers.
window.rangy.removeMarkers(selection);
// Mark the text as having been updated.
this.markUpdated();
},
/**
* Replaces all blockquotes within an editor with div indents.
* @method replaceBlockquote
* @param Editor editor
*/
replaceBlockquote: function(editor) {
editor.all('blockquote').setAttribute('data-iterate', true);
var blockquote = editor.one('blockquote'),
margindir = (Y.one('body.dir-ltr')) ? 'marginLeft' : 'marginRight';
while (blockquote) {
blockquote.removeAttribute('data-iterate');
if (blockquote.hasClass('pre-existing')) {
blockquote.removeClass('pre-existing');
} else {
var clone = Y.Node.create('<div></div>')
.setAttrs(blockquote.getAttrs())
.setStyle(margindir, '30px')
.addClass('editor-indent');
// We use childNodes here because we are interested in both type 1 and 3 child nodes.
var children = blockquote.getDOMNode().childNodes;
var child;
child = children[0];
while (typeof child !== "undefined") {
clone.append(child);
child = children[0];
}
blockquote.replace(clone);
}
blockquote = editor.one('blockquote[data-iterate]');
}
},
/**
* Replaces all div indents with blockquotes.
* @method replaceEditorIndents
* @param Editor editor
*/
replaceEditorIndents: function(editor) {
// We use the editor-indent class because it is preserved between saves.
var indent = editor.one('.editor-indent');
while (indent) {
var clone = Y.Node.create('<blockquote></blockquote>')
.setAttrs(indent
.getAttrs())
.removeClass('editor-indent');
// We use childNodes here because we are interested in both type 1 and 3 child nodes.
var children = indent.getDOMNode().childNodes;
var child;
child = children[0];
while (typeof child !== "undefined") {
clone.append(child);
child = children[0];
}
indent.replace(clone);
indent = editor.one('.editor-indent');
}
},
/**
* Fixup for list item after indent.
*
* @method fixupListItemsAfterIndent
*/
fixupListItemsAfterIndent: function() {
var selection = window.rangy.getSelection(),
rootelement = this.editor.getDOMNode(),
listelement = selection.anchorNode.parentElement;
listelement = listelement.closest('ol, ul');
if (!(listelement && rootelement.contains(listelement))) {
return;
}
// We will move the child list into previous list item of the parent.
var previous = listelement.previousElementSibling;
if (previous && previous.tagName === 'LI') {
previous.appendChild(listelement);
selection.collapseToEnd();
}
}
});