MDL-44224 atto_indent: converts blockquotes to div indents

This commit is contained in:
Sam Hemelryk 2014-05-05 13:23:09 +12:00 committed by Damyon Wiese
parent 3917871e90
commit b16c2ec5c8
4 changed files with 412 additions and 67 deletions

View File

@ -35,36 +35,151 @@ YUI.add('moodle-atto_indent-button', function (Y, NAME) {
Y.namespace('M.atto_indent').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addButton({
// This is adding a <blockquote> which is not ideal but that is the easiest to put in place
// for now. When disabling the styleWithCSS, some browser will use <blockquote> so we cannot
// rely on it for <div>s, and that would not work when indenting lists either....
// Handling it ourselves is even worse as it would require to get a parent and wrap
// a div with a margin around it. Considering that multiple <p> should end up in the
// same <div>, that table cells should not be wrapped, and that lists work differently too.
icon: 'e/increase_indent',
title: 'indent',
buttonName: 'indent',
callback: function() {
document.execCommand('indent', false, null);
// Some browsers add style attributes to the blockquote, let's get rid of them.
// It is really tricky to figure out what blockquote was just added, so removing
// the styles on all of them seems OK.
// Eg. Chrome changes the selection after adding the blockquote, so we cannot target it.
// IE adds a dir attribute to the blockquote too, but it's probably OK to leave it...
this.editor.all('blockquote').removeAttribute('style');
// Mark the text as having been updated.
this.markUpdated();
}
callback: this.indent
});
this.addBasicButton({
exec: 'outdent',
this.addButton({
icon: 'e/decrease_indent',
title: 'outdent'
title: 'outdent',
buttonName: 'outdent',
callback: this.outdent
});
},
/**
* Indents the currently selected content.
*
* @method indent
*/
indent: function() {
// Save the current selection - we want to restore this.
var selection = rangy.saveSelection(),
blockquotes = this.editor.all('blockquote'),
count = blockquotes.size();
// 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);
// 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 :(
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.
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 = 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!
rangy.restoreSelection(selection);
// And save it once more.
selection = 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);
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.
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, child;
while (child = children[0]) {
clone.append(child);
}
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('div.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, child;
while (child = children[0]) {
clone.append(child);
}
indent.replace(clone);
indent = editor.one('div.editor-indent');
}
}
});

View File

@ -1 +1 @@
YUI.add("moodle-atto_indent-button",function(e,t){e.namespace("M.atto_indent").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{initializer:function(){this.addButton({icon:"e/increase_indent",title:"indent",buttonName:"indent",callback:function(){document.execCommand("indent",!1,null),this.editor.all("blockquote").removeAttribute("style"),this.markUpdated()}}),this.addBasicButton({exec:"outdent",icon:"e/decrease_indent",title:"outdent"})}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
YUI.add("moodle-atto_indent-button",function(e,t){e.namespace("M.atto_indent").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{initializer:function(){this.addButton({icon:"e/increase_indent",title:"indent",buttonName:"indent",callback:this.indent}),this.addButton({icon:"e/decrease_indent",title:"outdent",buttonName:"outdent",callback:this.outdent})},indent:function(){var e=rangy.saveSelection(),t=this.editor.all("blockquote"),n=t.size();t.addClass("pre-existing"),document.execCommand("indent",!1,null),t=this.editor.all("blockquote"),t.size()!==n?(this.replaceBlockquote(this.editor),rangy.restoreSelection(e)):t.size()>0&&t.removeClass("pre-existing"),rangy.removeMarkers(e),this.markUpdated()},outdent:function(){var e=rangy.saveSelection(),t=this.editor.all("blockquote"),n=t.size();t.addClass("pre-existing"),this.replaceEditorIndents(this.editor),rangy.restoreSelection(e),e=rangy.saveSelection(),document.execCommand("outdent",!1,null),t=this.editor.all("blockquote"),t.size()!==n?(this.replaceBlockquote(this.editor),rangy.restoreSelection(e)):t.size()>0&&t.removeClass("pre-existing"),rangy.removeMarkers(e),this.markUpdated()},replaceBlockquote:function(t){t.all("blockquote").setAttribute("data-iterate",!0);var n=t.one("blockquote"),r=e.one("body.dir-ltr")?"marginLeft":"marginRight";while(n){n.removeAttribute("data-iterate");if(n.hasClass("pre-existing"))n.removeClass("pre-existing");else{var i=e.Node.create("<div></div>").setAttrs(n.getAttrs()).setStyle(r,"30px").addClass("editor-indent"),s=n.getDOMNode().childNodes,o;while(o=s[0])i.append(o);n.replace(i)}n=t.one("blockquote[data-iterate]")}},replaceEditorIndents:function(t){var n=t.one("div.editor-indent");while(n){var r=e.Node.create("<blockquote></blockquote>").setAttrs(n.getAttrs()).removeClass("editor-indent"),i=n.getDOMNode().childNodes,s;while(s=i[0])r.append(s);n.replace(r),n=t.one("div.editor-indent")}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});

View File

@ -35,36 +35,151 @@ YUI.add('moodle-atto_indent-button', function (Y, NAME) {
Y.namespace('M.atto_indent').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addButton({
// This is adding a <blockquote> which is not ideal but that is the easiest to put in place
// for now. When disabling the styleWithCSS, some browser will use <blockquote> so we cannot
// rely on it for <div>s, and that would not work when indenting lists either....
// Handling it ourselves is even worse as it would require to get a parent and wrap
// a div with a margin around it. Considering that multiple <p> should end up in the
// same <div>, that table cells should not be wrapped, and that lists work differently too.
icon: 'e/increase_indent',
title: 'indent',
buttonName: 'indent',
callback: function() {
document.execCommand('indent', false, null);
// Some browsers add style attributes to the blockquote, let's get rid of them.
// It is really tricky to figure out what blockquote was just added, so removing
// the styles on all of them seems OK.
// Eg. Chrome changes the selection after adding the blockquote, so we cannot target it.
// IE adds a dir attribute to the blockquote too, but it's probably OK to leave it...
this.editor.all('blockquote').removeAttribute('style');
// Mark the text as having been updated.
this.markUpdated();
}
callback: this.indent
});
this.addBasicButton({
exec: 'outdent',
this.addButton({
icon: 'e/decrease_indent',
title: 'outdent'
title: 'outdent',
buttonName: 'outdent',
callback: this.outdent
});
},
/**
* Indents the currently selected content.
*
* @method indent
*/
indent: function() {
// Save the current selection - we want to restore this.
var selection = rangy.saveSelection(),
blockquotes = this.editor.all('blockquote'),
count = blockquotes.size();
// 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);
// 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 :(
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.
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 = 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!
rangy.restoreSelection(selection);
// And save it once more.
selection = 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);
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.
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, child;
while (child = children[0]) {
clone.append(child);
}
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('div.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, child;
while (child = children[0]) {
clone.append(child);
}
indent.replace(clone);
indent = editor.one('div.editor-indent');
}
}
});

View File

@ -33,35 +33,150 @@
Y.namespace('M.atto_indent').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
initializer: function() {
this.addButton({
// This is adding a <blockquote> which is not ideal but that is the easiest to put in place
// for now. When disabling the styleWithCSS, some browser will use <blockquote> so we cannot
// rely on it for <div>s, and that would not work when indenting lists either....
// Handling it ourselves is even worse as it would require to get a parent and wrap
// a div with a margin around it. Considering that multiple <p> should end up in the
// same <div>, that table cells should not be wrapped, and that lists work differently too.
icon: 'e/increase_indent',
title: 'indent',
buttonName: 'indent',
callback: function() {
document.execCommand('indent', false, null);
// Some browsers add style attributes to the blockquote, let's get rid of them.
// It is really tricky to figure out what blockquote was just added, so removing
// the styles on all of them seems OK.
// Eg. Chrome changes the selection after adding the blockquote, so we cannot target it.
// IE adds a dir attribute to the blockquote too, but it's probably OK to leave it...
this.editor.all('blockquote').removeAttribute('style');
// Mark the text as having been updated.
this.markUpdated();
}
callback: this.indent
});
this.addBasicButton({
exec: 'outdent',
this.addButton({
icon: 'e/decrease_indent',
title: 'outdent'
title: 'outdent',
buttonName: 'outdent',
callback: this.outdent
});
},
/**
* Indents the currently selected content.
*
* @method indent
*/
indent: function() {
// Save the current selection - we want to restore this.
var selection = rangy.saveSelection(),
blockquotes = this.editor.all('blockquote'),
count = blockquotes.size();
// 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);
// 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 :(
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.
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 = 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!
rangy.restoreSelection(selection);
// And save it once more.
selection = 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);
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.
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, child;
while (child = children[0]) {
clone.append(child);
}
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('div.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, child;
while (child = children[0]) {
clone.append(child);
}
indent.replace(clone);
indent = editor.one('div.editor-indent');
}
}
});