Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('editor-para', function (Y, NAME) {
2
 
3
 
4
    /**
5
     * Plugin for Editor to paragraph auto wrapping and correction.
6
     * @class Plugin.EditorPara
7
     * @extends Plugin.EditorParaBase
8
     * @constructor
9
     * @module editor
10
     * @submodule editor-para
11
     */
12
 
13
 
14
    var EditorPara = function() {
15
        EditorPara.superclass.constructor.apply(this, arguments);
16
    }, HOST = 'host', NODE_CHANGE = 'nodeChange', PARENT_NODE = 'parentNode',
17
    FIRST_P = '> p', P = 'p', BR = '<br>', FC = 'firstChild', LI = 'li';
18
 
19
 
20
    Y.extend(EditorPara, Y.Plugin.EditorParaBase, {
21
        /**
22
        * Resolves the ROOT editor element.
23
        * @method _getRoot
24
        * @private
25
        */
26
        _getRoot: function() {
27
            return this.get(HOST).getInstance().EditorSelection.ROOT;
28
        },
29
 
30
        /**
31
        * nodeChange handler to handle fixing an empty document.
32
        * @private
33
        * @method _onNodeChange
34
        */
35
        _onNodeChange: function(e) {
36
            var host = this.get(HOST), inst = host.getInstance(),
37
                html, txt, par , d, sel, btag = inst.EditorSelection.DEFAULT_BLOCK_TAG,
38
                inHTML, txt2, childs, aNode, node2, top, n, sib, para2, prev,
39
                ps, br, item, p, imgs, t, LAST_CHILD = ':last-child', para, b, dir,
40
                lc, lc2, found = false, root = this._getRoot(), start;
41
 
42
            switch (e.changedType) {
43
                case 'enter-up':
44
                    para = ((this._lastPara) ? this._lastPara : e.changedNode);
45
                    b = para.one('br.yui-cursor');
46
 
47
                    if (this._lastPara) {
48
                        delete this._lastPara;
49
                    }
50
 
51
                    if (b) {
52
                        if (b.previous() || b.next()) {
53
                            if (b.ancestor(P)) {
54
                                b.remove();
55
                            }
56
                        }
57
                    }
58
                    if (!para.test(btag)) {
59
                        para2 = para.ancestor(btag);
60
                        if (para2) {
61
                            para = para2;
62
                            para2 = null;
63
                        }
64
                    }
65
                    if (para.test(btag)) {
66
                        prev = para.previous();
67
                        if (prev) {
68
                            lc = prev.one(LAST_CHILD);
69
                            while (!found) {
70
                                if (lc) {
71
                                    lc2 = lc.one(LAST_CHILD);
72
                                    if (lc2) {
73
                                        lc = lc2;
74
                                    } else {
75
                                        found = true;
76
                                    }
77
                                } else {
78
                                    found = true;
79
                                }
80
                            }
81
                            if (lc) {
82
                                host.copyStyles(lc, para);
83
                            }
84
                        }
85
                    }
86
                    break;
87
                case 'enter':
88
                    if (Y.UA.webkit) {
89
                        //Webkit doesn't support shift+enter as a BR, this fixes that.
90
                        if (e.changedEvent.shiftKey) {
91
                            host.execCommand('insertbr');
92
                            e.changedEvent.preventDefault();
93
                        }
94
                    }
95
                    if (e.changedNode.test('li') && !Y.UA.ie) {
96
                        html = inst.EditorSelection.getText(e.changedNode);
97
                        if (html === '') {
98
                            par = e.changedNode.ancestor('ol,ul');
99
                            dir = par.getAttribute('dir');
100
                            if (dir !== '') {
101
                                dir = ' dir = "' + dir + '"';
102
                            }
103
                            par = e.changedNode.ancestor(inst.EditorSelection.BLOCKS);
104
                            d = inst.Node.create('<p' + dir + '>' + inst.EditorSelection.CURSOR + '</p>');
105
                            par.insert(d, 'after');
106
                            e.changedNode.remove();
107
                            e.changedEvent.halt();
108
 
109
                            sel = new inst.EditorSelection();
110
                            sel.selectNode(d, true, false);
111
                        }
112
                    }
113
                    //TODO Move this to a GECKO MODULE - Can't for the moment, requires no change to metadata (YMAIL)
114
                    if (Y.UA.gecko && host.get('defaultblock') !== 'p') {
115
                        par = e.changedNode;
116
 
117
                        if (!par.test(LI) && !par.ancestor(LI)) {
118
                            if (!par.test(btag)) {
119
                                par = par.ancestor(btag);
120
                            }
121
                            d = inst.Node.create('<' + btag + '></' + btag + '>');
122
                            par.insert(d, 'after');
123
                            sel = new inst.EditorSelection();
124
                            if (sel.anchorOffset) {
125
                                inHTML = sel.anchorNode.get('textContent');
126
 
127
                                txt = inst.one(inst.config.doc.createTextNode(inHTML.substr(0, sel.anchorOffset)));
128
                                txt2 = inst.one(inst.config.doc.createTextNode(inHTML.substr(sel.anchorOffset)));
129
 
130
                                aNode = sel.anchorNode;
131
                                aNode.setContent(''); //I
132
                                node2 = aNode.cloneNode(); //I
133
                                node2.append(txt2); //text
134
                                top = false;
135
                                sib = aNode; //I
136
                                while (!top) {
137
                                    sib = sib.get(PARENT_NODE); //B
138
                                    if (sib && !sib.test(btag)) {
139
                                        n = sib.cloneNode();
140
                                        n.set('innerHTML', '');
141
                                        n.append(node2);
142
 
143
                                        //Get children..
144
                                        childs = sib.get('childNodes');
145
                                        start = false;
146
                                        /*jshint loopfunc: true */
147
                                        childs.each(function(c) {
148
                                            if (start) {
149
                                                n.append(c);
150
                                            }
151
                                            if (c === aNode) {
152
                                                start = true;
153
                                            }
154
                                        });
155
 
156
                                        aNode = sib; //Top sibling
157
                                        node2 = n;
158
                                    } else {
159
                                        top = true;
160
                                    }
161
                                }
162
                                txt2 = node2;
163
                                sel.anchorNode.append(txt);
164
 
165
                                if (txt2) {
166
                                    d.append(txt2);
167
                                }
168
                            }
169
                            if (d.get(FC)) {
170
                                d = d.get(FC);
171
                            }
172
                            d.prepend(inst.EditorSelection.CURSOR);
173
                            sel.focusCursor(true, true);
174
                            html = inst.EditorSelection.getText(d);
175
                            if (html !== '') {
176
                                inst.EditorSelection.cleanCursor();
177
                            }
178
                            e.changedEvent.preventDefault();
179
                        }
180
                    }
181
                    break;
182
                case 'keyup':
183
                    if (Y.UA.gecko) {
184
                        if (root && root.getHTML().length < 20) {
185
                            if (!root.one(FIRST_P)) {
186
                                this._fixFirstPara();
187
                            }
188
                        }
189
                    }
190
                    break;
191
                case 'backspace-up':
192
                case 'backspace-down':
193
                case 'delete-up':
194
                    if (!Y.UA.ie) {
195
                        ps = root.all(FIRST_P);
196
                        item = root;
197
                        if (ps.item(0)) {
198
                            item = ps.item(0);
199
                        }
200
                        br = item.one('br');
201
                        if (br) {
202
                            br.removeAttribute('id');
203
                            br.removeAttribute('class');
204
                        }
205
 
206
                        txt = inst.EditorSelection.getText(item);
207
                        txt = txt.replace(/ /g, '').replace(/\n/g, '');
208
                        imgs = item.all('img');
209
 
210
                        if (txt.length === 0 && !imgs.size()) {
211
                            //God this is horrible..
212
                            if (!item.test(P)) {
213
                                this._fixFirstPara();
214
                            }
215
                            p = null;
216
                            if (e.changedNode && e.changedNode.test(P)) {
217
                                p = e.changedNode;
218
                            }
219
                            if (!p && host._lastPara && host._lastPara.inDoc()) {
220
                                p = host._lastPara;
221
                            }
222
                            if (p && !p.test(P)) {
223
                                p = p.ancestor(P);
224
                            }
225
                            if (p) {
226
                                if (!p.previous() && p.get(PARENT_NODE) && p.get(PARENT_NODE).compareTo(root)) {
227
                                    e.changedEvent.frameEvent.halt();
228
                                    e.preventDefault();
229
                                }
230
                            }
231
                        }
232
                        if (Y.UA.webkit) {
233
                            if (e.changedNode) {
234
                                //All backspace calls in Webkit need a preventDefault to
235
                                //stop history navigation #2531299
236
                                e.preventDefault();
237
                                item = e.changedNode;
238
                                if (item.test('li') && (!item.previous() && !item.next())) {
239
                                    html = item.get('innerHTML').replace(BR, '');
240
                                    if (html === '') {
241
                                        if (item.get(PARENT_NODE)) {
242
                                            item.get(PARENT_NODE).replace(inst.Node.create(BR));
243
                                            e.changedEvent.frameEvent.halt();
244
                                            inst.EditorSelection.filterBlocks();
245
                                        }
246
                                    }
247
                                }
248
                            }
249
                        }
250
                    }
251
 
252
                    if (Y.UA.gecko) {
253
                       /*
254
                        * This forced FF to redraw the content on backspace.
255
                        * On some occasions FF will leave a cursor residue after content has been deleted.
256
                        * Dropping in the empty textnode and then removing it causes FF to redraw and
257
                        * remove the "ghost cursors"
258
                        */
259
                        // d = e.changedNode;
260
                        // t = inst.config.doc.createTextNode(' ');
261
                        // d.appendChild(t);
262
                        // d.removeChild(t);
263
 
264
                        this._fixGeckoOnBackspace(inst);
265
                    }
266
                    break;
267
            }
268
            if (Y.UA.gecko) {
269
                if (e.changedNode && !e.changedNode.test(btag)) {
270
                    p = e.changedNode.ancestor(btag);
271
                    if (p) {
272
                        this._lastPara = p;
273
                    }
274
                }
275
            }
276
 
277
        },
278
 
279
        //If we just backspaced into a P on FF, we have to put the cursor
280
        //before the BR that FF (usually) had injected when we used <ENTER> to
281
        //leave the P.
282
        _fixGeckoOnBackspace: function (inst) {
283
            var sel = new inst.EditorSelection(),
284
                node,
285
                childNodes;
286
 
287
            //not a cursor, not in a paragraph, or anchored at paragraph start.
288
            if (!sel.isCollapsed || sel.anchorNode.get('nodeName') !== 'P' ||
289
                sel.anchorOffset === 0) {
290
                return;
291
            }
292
 
293
            //cursor not on the injected final BR
294
            childNodes = sel.anchorNode.get('childNodes');
295
            node = sel.anchorNode.get('lastChild');
296
            if (sel.anchorOffset !== childNodes.size() || node.get('nodeName') !== 'BR') {
297
                return;
298
            }
299
 
300
            //empty P (only contains BR)
301
            if (sel.anchorOffset === 1) {
302
                sel.selectNode(sel.anchorNode, true);
303
                return;
304
            }
305
 
306
            //We only expect injected BR behavior when last Node is text
307
            node = node.get('previousSibling');
308
            if (node.get('nodeType') !== Node.TEXT_NODE) {
309
                return;
310
            }
311
 
312
            offset = node.get('length');
313
 
314
            // the cursor's position is strictly
315
            // at the offset when this bug occurs
316
            if (sel.getEditorOffset() === offset) {
317
                sel.selectNode(node, true, offset);
318
            }
319
        },
320
 
321
        initializer: function() {
322
            var host = this.get(HOST);
323
            if (host.editorBR) {
324
                Y.error('Can not plug EditorPara and EditorBR at the same time.');
325
                return;
326
            }
327
 
328
            host.on(NODE_CHANGE, Y.bind(this._onNodeChange, this));
329
        }
330
    }, {
331
        /**
332
        * editorPara
333
        * @static
334
        * @property NAME
335
        */
336
        NAME: 'editorPara',
337
        /**
338
        * editorPara
339
        * @static
340
        * @property NS
341
        */
342
        NS: 'editorPara',
343
        ATTRS: {
344
            host: {
345
                value: false
346
            }
347
        }
348
    });
349
 
350
    Y.namespace('Plugin');
351
 
352
    Y.Plugin.EditorPara = EditorPara;
353
 
354
 
355
}, '3.18.1', {"requires": ["editor-para-base"]});