MDL-32843 import YUI 3.5.1
[moodle.git] / lib / yui / 3.5.1 / build / editor-para / editor-para-debug.js
blob9c0ddeef0f23bb836794d53709b774aadee87eac
1 /*
2 YUI 3.5.1 (build 22)
3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
7 YUI.add('editor-para', function(Y) {
10     /**
11      * Plugin for Editor to paragraph auto wrapping and correction.
12      * @class Plugin.EditorPara
13      * @extends Plugin.EditorParaBase
14      * @constructor
15      * @module editor
16      * @submodule editor-para
17      */
20     var EditorPara = function() {
21         EditorPara.superclass.constructor.apply(this, arguments);
22     }, HOST = 'host', BODY = 'body', NODE_CHANGE = 'nodeChange', PARENT_NODE = 'parentNode',
23     FIRST_P = BODY + ' > p', P = 'p', BR = '<br>', FC = 'firstChild', LI = 'li';
26     Y.extend(EditorPara, Y.Plugin.EditorParaBase, {
27         /**
28         * nodeChange handler to handle fixing an empty document.
29         * @private
30         * @method _onNodeChange
31         */
32         _onNodeChange: function(e) {
33             var host = this.get(HOST), inst = host.getInstance(),
34                 html, txt, par , d, sel, btag = inst.EditorSelection.DEFAULT_BLOCK_TAG,
35                 inHTML, txt2, childs, aNode, index, node2, top, n, sib,
36                 ps, br, item, p, imgs, t, LAST_CHILD = ':last-child';
38             switch (e.changedType) {
39                 case 'enter-up':
40                     var para = ((this._lastPara) ? this._lastPara : e.changedNode),
41                         b = para.one('br.yui-cursor');
43                     if (this._lastPara) {
44                         delete this._lastPara;
45                     }
47                     if (b) {
48                         if (b.previous() || b.next()) {
49                             if (b.ancestor(P)) {
50                                 b.remove();
51                             }
52                         }
53                     }
54                     if (!para.test(btag)) {
55                         var para2 = para.ancestor(btag);
56                         if (para2) {
57                             para = para2;
58                             para2 = null;
59                         }
60                     }
61                     if (para.test(btag)) {
62                         var prev = para.previous(), lc, lc2, found = false;
63                         if (prev) {
64                             lc = prev.one(LAST_CHILD);
65                             while (!found) {
66                                 if (lc) {
67                                     lc2 = lc.one(LAST_CHILD);
68                                     if (lc2) {
69                                         lc = lc2;
70                                     } else {
71                                         found = true;
72                                     }
73                                 } else {
74                                     found = true;
75                                 }
76                             }
77                             if (lc) {
78                                 host.copyStyles(lc, para);
79                             }
80                         }
81                     }
82                     break;
83                 case 'enter':
84                     if (Y.UA.webkit) {
85                         //Webkit doesn't support shift+enter as a BR, this fixes that.
86                         if (e.changedEvent.shiftKey) {
87                             host.execCommand('insertbr');
88                             e.changedEvent.preventDefault();
89                         }
90                     }
91                     if (e.changedNode.test('li') && !Y.UA.ie) {
92                         html = inst.EditorSelection.getText(e.changedNode);
93                         if (html === '') {
94                             par = e.changedNode.ancestor('ol,ul');
95                             var dir = par.getAttribute('dir');
96                             if (dir !== '') {
97                                 dir = ' dir = "' + dir + '"';
98                             }
99                             par = e.changedNode.ancestor(inst.EditorSelection.BLOCKS);
100                             d = inst.Node.create('<p' + dir + '>' + inst.EditorSelection.CURSOR + '</p>');
101                             par.insert(d, 'after');
102                             e.changedNode.remove();
103                             e.changedEvent.halt();
105                             sel = new inst.EditorSelection();
106                             sel.selectNode(d, true, false);
107                         }
108                     }
109                     //TODO Move this to a GECKO MODULE - Can't for the moment, requires no change to metadata (YMAIL)
110                     if (Y.UA.gecko && host.get('defaultblock') !== 'p') {
111                         par = e.changedNode;
113                         if (!par.test(LI) && !par.ancestor(LI)) {
114                             if (!par.test(btag)) {
115                                 par = par.ancestor(btag);
116                             }
117                             d = inst.Node.create('<' + btag + '></' + btag + '>');
118                             par.insert(d, 'after');
119                             sel = new inst.EditorSelection();
120                             if (sel.anchorOffset) {
121                                 inHTML = sel.anchorNode.get('textContent');
123                                 txt = inst.one(inst.config.doc.createTextNode(inHTML.substr(0, sel.anchorOffset)));
124                                 txt2 = inst.one(inst.config.doc.createTextNode(inHTML.substr(sel.anchorOffset)));
126                                 aNode = sel.anchorNode;
127                                 aNode.setContent(''); //I
128                                 node2 = aNode.cloneNode(); //I
129                                 node2.append(txt2); //text
130                                 top = false;
131                                 sib = aNode; //I
132                                 while (!top) {
133                                     sib = sib.get(PARENT_NODE); //B
134                                     if (sib && !sib.test(btag)) {
135                                         n = sib.cloneNode();
136                                         n.set('innerHTML', '');
137                                         n.append(node2);
138                                         
139                                         //Get children..
140                                         childs = sib.get('childNodes');
141                                         var start = false;
142                                         childs.each(function(c) {
143                                             if (start) {
144                                                 n.append(c);
145                                             }
146                                             if (c === aNode) {
147                                                 start = true;
148                                             }
149                                         });
151                                         aNode = sib; //Top sibling
152                                         node2 = n;
153                                     } else {
154                                         top = true;
155                                     }
156                                 }
157                                 txt2 = node2;
158                                 sel.anchorNode.append(txt);
160                                 if (txt2) {
161                                     d.append(txt2);
162                                 }
163                             }
164                             if (d.get(FC)) {
165                                 d = d.get(FC);
166                             }
167                             d.prepend(inst.EditorSelection.CURSOR);
168                             sel.focusCursor(true, true);
169                             html = inst.EditorSelection.getText(d);
170                             if (html !== '') {
171                                 inst.EditorSelection.cleanCursor();
172                             }
173                             e.changedEvent.preventDefault();
174                         }
175                     }
176                     break;
177                 case 'keyup':
178                     if (Y.UA.gecko) {
179                         if (inst.config.doc && inst.config.doc.body && inst.config.doc.body.innerHTML.length < 20) {
180                             if (!inst.one(FIRST_P)) {
181                                 this._fixFirstPara();
182                             }
183                         }
184                     }
185                     break;
186                 case 'backspace-up':
187                 case 'backspace-down':
188                 case 'delete-up':
189                     if (!Y.UA.ie) {
190                         ps = inst.all(FIRST_P);
191                         item = inst.one(BODY);
192                         if (ps.item(0)) {
193                             item = ps.item(0);
194                         }
195                         br = item.one('br');
196                         if (br) {
197                             br.removeAttribute('id');
198                             br.removeAttribute('class');
199                         }
201                         txt = inst.EditorSelection.getText(item);
202                         txt = txt.replace(/ /g, '').replace(/\n/g, '');
203                         imgs = item.all('img');
204                         
205                         if (txt.length === 0 && !imgs.size()) {
206                             //God this is horrible..
207                             if (!item.test(P)) {
208                                 this._fixFirstPara();
209                             }
210                             p = null;
211                             if (e.changedNode && e.changedNode.test(P)) {
212                                 p = e.changedNode;
213                             }
214                             if (!p && host._lastPara && host._lastPara.inDoc()) {
215                                 p = host._lastPara;
216                             }
217                             if (p && !p.test(P)) {
218                                 p = p.ancestor(P);
219                             }
220                             if (p) {
221                                 if (!p.previous() && p.get(PARENT_NODE) && p.get(PARENT_NODE).test(BODY)) {
222                                     Y.log('Stopping the backspace event', 'warn', 'editor-para');
223                                     e.changedEvent.frameEvent.halt();
224                                     e.preventDefault();
225                                 }
226                             }
227                         }
228                         if (Y.UA.webkit) {
229                             if (e.changedNode) {
230                                 //All backspace calls in Webkit need a preventDefault to
231                                 //stop history navigation #2531299
232                                 e.preventDefault();
233                                 item = e.changedNode;
234                                 if (item.test('li') && (!item.previous() && !item.next())) {
235                                     html = item.get('innerHTML').replace(BR, '');
236                                     if (html === '') {
237                                         if (item.get(PARENT_NODE)) {
238                                             item.get(PARENT_NODE).replace(inst.Node.create(BR));
239                                             e.changedEvent.frameEvent.halt();
240                                             inst.EditorSelection.filterBlocks();
241                                         }
242                                     }
243                                 }
244                             }
245                         }
246                     }
247                     if (Y.UA.gecko) {
248                         /*
249                         * This forced FF to redraw the content on backspace.
250                         * On some occasions FF will leave a cursor residue after content has been deleted.
251                         * Dropping in the empty textnode and then removing it causes FF to redraw and
252                         * remove the "ghost cursors"
253                         */
254                         d = e.changedNode;
255                         t = inst.config.doc.createTextNode(' ');
256                         d.appendChild(t);
257                         d.removeChild(t);
258                     }
259                     break;
260             }
261             if (Y.UA.gecko) {
262                 if (e.changedNode && !e.changedNode.test(btag)) {
263                     p = e.changedNode.ancestor(btag);
264                     if (p) {
265                         this._lastPara = p;
266                     }
267                 }
268             }
269             
270         },
271         initializer: function() {
272             var host = this.get(HOST);
273             if (host.editorBR) {
274                 Y.error('Can not plug EditorPara and EditorBR at the same time.');
275                 return;
276             }
278             host.on(NODE_CHANGE, Y.bind(this._onNodeChange, this));
279         }
280     }, {
281         /**
282         * editorPara
283         * @static
284         * @property NAME
285         */
286         NAME: 'editorPara',
287         /**
288         * editorPara
289         * @static
290         * @property NS
291         */
292         NS: 'editorPara',
293         ATTRS: {
294             host: {
295                 value: false
296             }
297         }
298     });
299     
300     Y.namespace('Plugin');
301     
302     Y.Plugin.EditorPara = EditorPara;
306 }, '3.5.1' ,{skinnable:false, requires:['editor-base']});