Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
 
16
/*
17
 * @package    atto_indent
18
 * @copyright  2013 Damyon Wiese  <damyon@moodle.com>
19
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
20
 */
21
 
22
/**
23
 * @module     moodle-atto_indent-button
24
 */
25
 
26
/**
27
 * Atto text editor indent plugin.
28
 *
29
 * @namespace M.atto_indent
30
 * @class button
31
 * @extends M.editor_atto.EditorPlugin
32
 */
33
 
34
Y.namespace('M.atto_indent').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
35
    initializer: function() {
36
 
37
        this.addButton({
38
            icon: 'e/decrease_indent',
39
            title: 'outdent',
40
            buttonName: 'outdent',
41
            callback: this.outdent
42
        });
43
 
44
        this.addButton({
45
            icon: 'e/increase_indent',
46
            title: 'indent',
47
            buttonName: 'indent',
48
            callback: this.indent
49
        });
50
    },
51
 
52
    /**
53
     * Indents the currently selected content.
54
     *
55
     * @method indent
56
     */
57
    indent: function() {
58
        // Save the current selection - we want to restore this.
59
        var selection = window.rangy.saveSelection(),
60
            blockquotes = this.editor.all('blockquote'),
61
            count = blockquotes.size();
62
 
63
        // Remove display:none from rangy markers so browser doesn't delete them.
64
        this.editor.all('.rangySelectionBoundary').setStyle('display', null);
65
 
66
        // Mark all existing block quotes in case the user has actually added some.
67
        blockquotes.addClass('pre-existing');
68
 
69
        // Run the indent command.
70
        document.execCommand('indent', false, null);
71
 
72
        // Fix indent list item.
73
        this.fixupListItemsAfterIndent();
74
 
75
        // Get all blockquotes, both existing and new.
76
        blockquotes = this.editor.all('blockquote');
77
 
78
        if (blockquotes.size() !== count) {
79
            // There are new block quotes, the indent exec has wrapped some content in block quotes in order
80
            // to indent the selected content.
81
            // We don't want blockquotes, we're going to convert them to divs.
82
            this.replaceBlockquote(this.editor);
83
            // Finally restore the seelction. The content has changed - sometimes this works - but not always :(
84
            window.rangy.restoreSelection(selection);
85
        } else if (blockquotes.size() > 0) {
86
            // There were no new blockquotes, this happens if the user is indenting/outdenting a list.
87
            blockquotes.removeClass('pre-existing');
88
        }
89
 
90
        // Remove the selection markers - a clean up really.
91
        window.rangy.removeMarkers(selection);
92
 
93
        // Mark the text as having been updated.
94
        this.markUpdated();
95
    },
96
 
97
    /**
98
     * Outdents the currently selected content.
99
     *
100
     * @method outdent
101
     */
102
    outdent: function() {
103
        // Save the selection we will want to restore it.
104
        var selection = window.rangy.saveSelection(),
105
            blockquotes = this.editor.all('blockquote'),
106
            count = blockquotes.size();
107
 
108
        // Mark existing blockquotes so that we don't convert them later.
109
        blockquotes.addClass('pre-existing');
110
 
111
        // Replace all div indents with blockquote indents so that we can rely on the browser functionality.
112
        this.replaceEditorIndents(this.editor);
113
 
114
        // Restore the users selection - otherwise the next outdent operation won't work!
115
        window.rangy.restoreSelection(selection);
116
        // And save it once more.
117
        selection = window.rangy.saveSelection();
118
 
119
        // Outdent.
120
        document.execCommand('outdent', false, null);
121
 
122
        // Get all blockquotes so that we can work out what happened.
123
        blockquotes = this.editor.all('blockquote');
124
 
125
        if (blockquotes.size() !== count) {
126
            // The number of blockquotes hasn't changed.
127
            // This occurs when the user has outdented a list item.
128
            this.replaceBlockquote(this.editor);
129
            window.rangy.restoreSelection(selection);
130
        } else if (blockquotes.size() > 0) {
131
            // The number of blockquotes is the same and is more than 0 we just need to clean up the class
132
            // we added to mark pre-existing blockquotes.
133
            blockquotes.removeClass('pre-existing');
134
        }
135
 
136
        // Clean up any left over selection markers.
137
        window.rangy.removeMarkers(selection);
138
 
139
        // Mark the text as having been updated.
140
        this.markUpdated();
141
    },
142
 
143
    /**
144
     * Replaces all blockquotes within an editor with div indents.
145
     * @method replaceBlockquote
146
     * @param Editor editor
147
     */
148
    replaceBlockquote: function(editor) {
149
        editor.all('blockquote').setAttribute('data-iterate', true);
150
        var blockquote = editor.one('blockquote'),
151
            margindir = (Y.one('body.dir-ltr')) ? 'marginLeft' : 'marginRight';
152
        while (blockquote) {
153
            blockquote.removeAttribute('data-iterate');
154
            if (blockquote.hasClass('pre-existing')) {
155
                blockquote.removeClass('pre-existing');
156
            } else {
157
                var clone = Y.Node.create('<div></div>')
158
                        .setAttrs(blockquote.getAttrs())
159
                        .setStyle(margindir, '30px')
160
                        .addClass('editor-indent');
161
                // We use childNodes here because we are interested in both type 1 and 3 child nodes.
162
                var children = blockquote.getDOMNode().childNodes;
163
                var child;
164
                child = children[0];
165
                while (typeof child !== "undefined") {
166
                    clone.append(child);
167
                    child = children[0];
168
                }
169
                blockquote.replace(clone);
170
            }
171
            blockquote = editor.one('blockquote[data-iterate]');
172
        }
173
    },
174
 
175
    /**
176
     * Replaces all div indents with blockquotes.
177
     * @method replaceEditorIndents
178
     * @param Editor editor
179
     */
180
    replaceEditorIndents: function(editor) {
181
        // We use the editor-indent class because it is preserved between saves.
182
        var indent = editor.one('.editor-indent');
183
        while (indent) {
184
            var clone = Y.Node.create('<blockquote></blockquote>')
185
                    .setAttrs(indent
186
                    .getAttrs())
187
                    .removeClass('editor-indent');
188
            // We use childNodes here because we are interested in both type 1 and 3 child nodes.
189
            var children = indent.getDOMNode().childNodes;
190
            var child;
191
            child = children[0];
192
            while (typeof child !== "undefined") {
193
                clone.append(child);
194
                child = children[0];
195
            }
196
            indent.replace(clone);
197
            indent = editor.one('.editor-indent');
198
        }
199
    },
200
    /**
201
     * Fixup for list item after indent.
202
     *
203
     * @method fixupListItemsAfterIndent
204
     */
205
    fixupListItemsAfterIndent: function() {
206
        var selection = window.rangy.getSelection(),
207
            rootelement = this.editor.getDOMNode(),
208
            listelement = selection.anchorNode.parentElement;
209
 
210
        listelement = listelement.closest('ol, ul');
211
        if (!(listelement && rootelement.contains(listelement))) {
212
            return;
213
        }
214
 
215
        // We will move the child list into previous list item of the parent.
216
        var previous = listelement.previousElementSibling;
217
        if (previous && previous.tagName === 'LI') {
218
            previous.appendChild(listelement);
219
            selection.collapseToEnd();
220
        }
221
    }
222
});