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