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
 * @module moodle-editor_atto-editor
17
 * @submodule styling
18
 */
19
 
20
/**
21
 * Editor styling functions for the Atto editor.
22
 *
23
 * See {{#crossLink "M.editor_atto.Editor"}}{{/crossLink}} for details.
24
 *
25
 * @namespace M.editor_atto
26
 * @class EditorStyling
27
 */
28
 
29
function EditorStyling() {}
30
 
31
EditorStyling.ATTRS = {
32
};
33
 
34
EditorStyling.prototype = {
35
    /**
36
     * Disable CSS styling.
37
     *
38
     * @method disableCssStyling
39
     */
40
    disableCssStyling: function() {
41
        try {
42
            document.execCommand("styleWithCSS", 0, false);
43
        } catch (e1) {
44
            try {
45
                document.execCommand("useCSS", 0, true);
46
            } catch (e2) {
47
                try {
48
                    document.execCommand('styleWithCSS', false, false);
49
                } catch (e3) {
50
                    // We did our best.
51
                }
52
            }
53
        }
54
    },
55
 
56
    /**
57
     * Enable CSS styling.
58
     *
59
     * @method enableCssStyling
60
     */
61
    enableCssStyling: function() {
62
        try {
63
            document.execCommand("styleWithCSS", 0, true);
64
        } catch (e1) {
65
            try {
66
                document.execCommand("useCSS", 0, false);
67
            } catch (e2) {
68
                try {
69
                    document.execCommand('styleWithCSS', false, true);
70
                } catch (e3) {
71
                    // We did our best.
72
                }
73
            }
74
        }
75
    },
76
 
77
    /**
78
     * Change the formatting for the current selection.
79
     *
80
     * This will wrap the selection in span tags, adding the provided classes.
81
     *
82
     * If the selection covers multiple block elements, multiple spans will be inserted to preserve the original structure.
83
     *
84
     * @method toggleInlineSelectionClass
85
     * @param {Array} toggleclasses - Class names to be toggled on or off.
86
     */
87
    toggleInlineSelectionClass: function(toggleclasses) {
88
        var classname = toggleclasses.join(" ");
89
        var cssApplier = rangy.createClassApplier(classname, {normalize: true});
90
 
91
        cssApplier.toggleSelection();
92
    },
93
 
94
    /**
95
     * Change the formatting for the current selection.
96
     *
97
     * This will set inline styles on the current selection.
98
     *
99
     * @method formatSelectionInlineStyle
100
     * @param {Array} styles - Style attributes to set on the nodes.
101
     */
102
    formatSelectionInlineStyle: function(styles) {
103
        var classname = this.PLACEHOLDER_CLASS;
104
        var cssApplier = rangy.createClassApplier(classname, {normalize: true});
105
 
106
        cssApplier.applyToSelection();
107
 
108
        this.editor.all('.' + classname).each(function(node) {
109
            node.removeClass(classname).setStyles(styles);
110
        }, this);
111
 
112
    },
113
 
114
    /**
115
     * Change the formatting for the current selection.
116
     *
117
     * Also changes the selection to the newly formatted block (allows applying multiple styles to a block).
118
     *
119
     * @method formatSelectionBlock
120
     * @param {String} [blocktag] Change the block level tag to this. Empty string, means do not change the tag.
121
     * @param {Object} [attributes] The keys and values for attributes to be added/changed in the block tag.
122
     * @return {Node|boolean} The Node that was formatted if a change was made, otherwise false.
123
     */
124
    formatSelectionBlock: function(blocktag, attributes) {
125
        // First find the nearest ancestor of the selection that is a block level element.
126
        var selectionparentnode = this.getSelectionParentNode(),
127
            boundary,
128
            cell,
129
            nearestblock,
130
            newcontent,
131
            match,
132
            replacement;
133
 
134
        if (!selectionparentnode) {
135
            // No selection, nothing to format.
136
            return false;
137
        }
138
 
139
        boundary = this.editor;
140
 
141
        selectionparentnode = Y.one(selectionparentnode);
142
 
143
        // If there is a table cell in between the selectionparentnode and the boundary,
144
        // move the boundary to the table cell.
145
        // This is because we might have a table in a div, and we select some text in a cell,
146
        // want to limit the change in style to the table cell, not the entire table (via the outer div).
147
        cell = selectionparentnode.ancestor(function(node) {
148
            var tagname = node.get('tagName');
149
            if (tagname) {
150
                tagname = tagname.toLowerCase();
151
            }
152
            return (node === boundary) ||
153
                   (tagname === 'td') ||
154
                   (tagname === 'th');
155
        }, true);
156
 
157
        if (cell) {
158
            // Limit the scope to the table cell.
159
            boundary = cell;
160
        }
161
 
162
        nearestblock = selectionparentnode.ancestor(this.BLOCK_TAGS.join(', '), true);
163
        if (nearestblock) {
164
            // Check that the block is contained by the boundary.
165
            match = nearestblock.ancestor(function(node) {
166
                return node === boundary;
167
            }, false);
168
 
169
            if (!match) {
170
                nearestblock = false;
171
            }
172
        }
173
 
174
        // No valid block element - make one.
175
        if (!nearestblock) {
176
            var alignment;
177
            if (this.coreDirection === 'rtl') {
178
                alignment = 'style="text-align: right;"';
179
            } else {
180
                alignment = 'style="text-align: left;"';
181
            }
182
            // There is no block node in the content, wrap the content in a p and use that.
183
            newcontent = Y.Node.create('<p dir="' + this.coreDirection + '" ' + alignment + '></p>');
184
            boundary.get('childNodes').each(function(child) {
185
                newcontent.append(child.remove());
186
            });
187
            boundary.append(newcontent);
188
            nearestblock = newcontent;
189
        }
190
 
191
        // Guaranteed to have a valid block level element contained in the contenteditable region.
192
        // Change the tag to the new block level tag.
193
        if (blocktag && blocktag !== '') {
194
            // Change the block level node for a new one.
195
            replacement = Y.Node.create('<' + blocktag + '></' + blocktag + '>');
196
            // Copy all attributes.
197
            replacement.setAttrs(nearestblock.getAttrs());
198
            // Copy all children.
199
            nearestblock.get('childNodes').each(function(child) {
200
                child.remove();
201
                replacement.append(child);
202
            });
203
 
204
            nearestblock.replace(replacement);
205
            nearestblock = replacement;
206
        }
207
 
208
        // Set the attributes on the block level tag.
209
        if (attributes) {
210
            nearestblock.setAttrs(attributes);
211
        }
212
 
213
        // Change the selection to the modified block. This makes sense when we might apply multiple styles
214
        // to the block.
215
        var selection = this.getSelectionFromNode(nearestblock);
216
        this.setSelection(selection);
217
 
218
        return nearestblock;
219
    }
220
 
221
};
222
 
223
Y.Base.mix(Y.M.editor_atto.Editor, [EditorStyling]);