MDL-44224 atto_indent: converts blockquotes to div indents
authorSam Hemelryk <sam@moodle.com>
Mon, 5 May 2014 01:23:09 +0000 (13:23 +1200)
committerDamyon Wiese <damyon@moodle.com>
Mon, 5 May 2014 06:07:01 +0000 (14:07 +0800)
lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js
lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js
lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js
lib/editor/atto/plugins/indent/yui/src/button/js/button.js

index 9dc6ee5..b276974 100644 (file)
Binary files a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js and b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js differ
index 034421d..a7f3a82 100644 (file)
Binary files a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js and b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js differ
index 9dc6ee5..b276974 100644 (file)
Binary files a/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js and b/lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js differ
index fe7433c..f4d1291 100644 (file)
 
 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');
+        }
     }
 });