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