]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/tiny_mce/classes/ForceBlocks.js
Release 6.2.3
[Github/sugarcrm.git] / include / javascript / tiny_mce / classes / ForceBlocks.js
1 /**
2  * ForceBlocks.js
3  *
4  * Copyright 2009, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://tinymce.moxiecode.com/license
8  * Contributing: http://tinymce.moxiecode.com/contributing
9  */
10
11 (function(tinymce) {
12         // Shorten names
13         var Event = tinymce.dom.Event,
14                 isIE = tinymce.isIE,
15                 isGecko = tinymce.isGecko,
16                 isOpera = tinymce.isOpera,
17                 each = tinymce.each,
18                 extend = tinymce.extend,
19                 TRUE = true,
20                 FALSE = false;
21
22         function cloneFormats(node) {
23                 var clone, temp, inner;
24
25                 do {
26                         if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {
27                                 if (clone) {
28                                         temp = node.cloneNode(false);
29                                         temp.appendChild(clone);
30                                         clone = temp;
31                                 } else {
32                                         clone = inner = node.cloneNode(false);
33                                 }
34
35                                 clone.removeAttribute('id');
36                         }
37                 } while (node = node.parentNode);
38
39                 if (clone)
40                         return {wrapper : clone, inner : inner};
41         };
42
43         // Checks if the selection/caret is at the end of the specified block element
44         function isAtEnd(rng, par) {
45                 var rng2 = par.ownerDocument.createRange();
46
47                 rng2.setStart(rng.endContainer, rng.endOffset);
48                 rng2.setEndAfter(par);
49
50                 // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element
51                 return rng2.cloneContents().textContent.length == 0;
52         };
53
54         function splitList(selection, dom, li) {
55                 var listBlock, block;
56
57                 if (dom.isEmpty(li)) {
58                         listBlock = dom.getParent(li, 'ul,ol');
59
60                         if (!dom.getParent(listBlock.parentNode, 'ul,ol')) {
61                                 dom.split(listBlock, li);
62                                 block = dom.create('p', 0, '<br data-mce-bogus="1" />');
63                                 dom.replace(block, li);
64                                 selection.select(block, 1);
65                         }
66
67                         return FALSE;
68                 }
69
70                 return TRUE;
71         };
72
73         /**
74          * This is a internal class and no method in this class should be called directly form the out side.
75          */
76         tinymce.create('tinymce.ForceBlocks', {
77                 ForceBlocks : function(ed) {
78                         var t = this, s = ed.settings, elm;
79
80                         t.editor = ed;
81                         t.dom = ed.dom;
82                         elm = (s.forced_root_block || 'p').toLowerCase();
83                         s.element = elm.toUpperCase();
84
85                         ed.onPreInit.add(t.setup, t);
86                 },
87
88                 setup : function() {
89                         var t = this, ed = t.editor, s = ed.settings, dom = ed.dom, selection = ed.selection, blockElements = ed.schema.getBlockElements();
90
91                         // Force root blocks
92                         if (s.forced_root_block) {
93                                 function addRootBlocks() {
94                                         var node = selection.getStart(), rootNode = ed.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF;
95
96                                         if (!node || node.nodeType !== 1)
97                                                 return;
98
99                                         // Check if node is wrapped in block
100                                         while (node != rootNode) {
101                                                 if (blockElements[node.nodeName])
102                                                         return;
103
104                                                 node = node.parentNode;
105                                         }
106
107                                         // Get current selection
108                                         rng = selection.getRng();
109                                         if (rng.setStart) {
110                                                 startContainer = rng.startContainer;
111                                                 startOffset = rng.startOffset;
112                                                 endContainer = rng.endContainer;
113                                                 endOffset = rng.endOffset;
114                                         } else {
115                                                 // Force control range into text range
116                                                 if (rng.item) {
117                                                         rng = ed.getDoc().body.createTextRange();
118                                                         rng.moveToElementText(rng.item(0));
119                                                 }
120
121                                                 tmpRng = rng.duplicate();
122                                                 tmpRng.collapse(true);
123                                                 startOffset = tmpRng.move('character', offset) * -1;
124
125                                                 if (!tmpRng.collapsed) {
126                                                         tmpRng = rng.duplicate();
127                                                         tmpRng.collapse(false);
128                                                         endOffset = (tmpRng.move('character', offset) * -1) - startOffset;
129                                                 }
130                                         }
131
132                                         // Wrap non block elements and text nodes
133                                         for (node = rootNode.firstChild; node; node) {
134                                                 if (node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName])) {
135                                                         if (!rootBlockNode) {
136                                                                 rootBlockNode = dom.create(s.forced_root_block);
137                                                                 node.parentNode.insertBefore(rootBlockNode, node);
138                                                         }
139
140                                                         tempNode = node;
141                                                         node = node.nextSibling;
142                                                         rootBlockNode.appendChild(tempNode);
143                                                 } else {
144                                                         rootBlockNode = null;
145                                                         node = node.nextSibling;
146                                                 }
147                                         }
148
149                                         if (rng.setStart) {
150                                                 rng.setStart(startContainer, startOffset);
151                                                 rng.setEnd(endContainer, endOffset);
152                                                 selection.setRng(rng);
153                                         } else {
154                                                 try {
155                                                         rng = ed.getDoc().body.createTextRange();
156                                                         rng.moveToElementText(rootNode);
157                                                         rng.collapse(true);
158                                                         rng.moveStart('character', startOffset);
159
160                                                         if (endOffset > 0)
161                                                                 rng.moveEnd('character', endOffset);
162
163                                                         rng.select();
164                                                 } catch (ex) {
165                                                         // Ignore
166                                                 }
167                                         }
168
169                                         ed.nodeChanged();
170                                 };
171
172                                 ed.onKeyUp.add(addRootBlocks);
173                                 ed.onClick.add(addRootBlocks);
174                         }
175
176                         if (s.force_br_newlines) {
177                                 // Force IE to produce BRs on enter
178                                 if (isIE) {
179                                         ed.onKeyPress.add(function(ed, e) {
180                                                 var n;
181
182                                                 if (e.keyCode == 13 && selection.getNode().nodeName != 'LI') {
183                                                         selection.setContent('<br id="__" /> ', {format : 'raw'});
184                                                         n = dom.get('__');
185                                                         n.removeAttribute('id');
186                                                         selection.select(n);
187                                                         selection.collapse();
188                                                         return Event.cancel(e);
189                                                 }
190                                         });
191                                 }
192                         }
193
194                         if (s.force_p_newlines) {
195                                 if (!isIE) {
196                                         ed.onKeyPress.add(function(ed, e) {
197                                                 if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))
198                                                         Event.cancel(e);
199                                         });
200                                 } else {
201                                         // Ungly hack to for IE to preserve the formatting when you press
202                                         // enter at the end of a block element with formatted contents
203                                         // This logic overrides the browsers default logic with
204                                         // custom logic that enables us to control the output
205                                         tinymce.addUnload(function() {
206                                                 t._previousFormats = 0; // Fix IE leak
207                                         });
208
209                                         ed.onKeyPress.add(function(ed, e) {
210                                                 t._previousFormats = 0;
211
212                                                 // Clone the current formats, this will later be applied to the new block contents
213                                                 if (e.keyCode == 13 && !e.shiftKey && ed.selection.isCollapsed() && s.keep_styles)
214                                                         t._previousFormats = cloneFormats(ed.selection.getStart());
215                                         });
216
217                                         ed.onKeyUp.add(function(ed, e) {
218                                                 // Let IE break the element and the wrap the new caret location in the previous formats
219                                                 if (e.keyCode == 13 && !e.shiftKey) {
220                                                         var parent = ed.selection.getStart(), fmt = t._previousFormats;
221
222                                                         // Parent is an empty block
223                                                         if (!parent.hasChildNodes() && fmt) {
224                                                                 parent = dom.getParent(parent, dom.isBlock);
225
226                                                                 if (parent && parent.nodeName != 'LI') {
227                                                                         parent.innerHTML = '';
228
229                                                                         if (t._previousFormats) {
230                                                                                 parent.appendChild(fmt.wrapper);
231                                                                                 fmt.inner.innerHTML = '\uFEFF';
232                                                                         } else
233                                                                                 parent.innerHTML = '\uFEFF';
234
235                                                                         selection.select(parent, 1);
236                                                                         selection.collapse(true);
237                                                                         ed.getDoc().execCommand('Delete', false, null);
238                                                                         t._previousFormats = 0;
239                                                                 }
240                                                         }
241                                                 }
242                                         });
243                                 }
244
245                                 if (isGecko) {
246                                         ed.onKeyDown.add(function(ed, e) {
247                                                 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)
248                                                         t.backspaceDelete(e, e.keyCode == 8);
249                                         });
250                                 }
251                         }
252
253                         // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973
254                         if (tinymce.isWebKit) {
255                                 function insertBr(ed) {
256                                         var rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h;
257
258                                         // Insert BR element
259                                         rng.insertNode(br = dom.create('br'));
260
261                                         // Place caret after BR
262                                         rng.setStartAfter(br);
263                                         rng.setEndAfter(br);
264                                         selection.setRng(rng);
265
266                                         // Could not place caret after BR then insert an nbsp entity and move the caret
267                                         if (selection.getSel().focusNode == br.previousSibling) {
268                                                 selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br));
269                                                 selection.collapse(TRUE);
270                                         }
271
272                                         // Create a temporary DIV after the BR and get the position as it
273                                         // seems like getPos() returns 0 for text nodes and BR elements.
274                                         dom.insertAfter(div, br);
275                                         divYPos = dom.getPos(div).y;
276                                         dom.remove(div);
277
278                                         // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117
279                                         if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port.
280                                                 ed.getWin().scrollTo(0, divYPos);
281                                 };
282
283                                 ed.onKeyPress.add(function(ed, e) {
284                                         if (e.keyCode == 13 && (e.shiftKey || (s.force_br_newlines && !dom.getParent(selection.getNode(), 'h1,h2,h3,h4,h5,h6,ol,ul')))) {
285                                                 insertBr(ed);
286                                                 Event.cancel(e);
287                                         }
288                                 });
289                         }
290
291                         // IE specific fixes
292                         if (isIE) {
293                                 // Replaces IE:s auto generated paragraphs with the specified element name
294                                 if (s.element != 'P') {
295                                         ed.onKeyPress.add(function(ed, e) {
296                                                 t.lastElm = selection.getNode().nodeName;
297                                         });
298
299                                         ed.onKeyUp.add(function(ed, e) {
300                                                 var bl, n = selection.getNode(), b = ed.getBody();
301
302                                                 if (b.childNodes.length === 1 && n.nodeName == 'P') {
303                                                         n = dom.rename(n, s.element);
304                                                         selection.select(n);
305                                                         selection.collapse();
306                                                         ed.nodeChanged();
307                                                 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {
308                                                         bl = dom.getParent(n, 'p');
309
310                                                         if (bl) {
311                                                                 dom.rename(bl, s.element);
312                                                                 ed.nodeChanged();
313                                                         }
314                                                 }
315                                         });
316                                 }
317                         }
318                 },
319
320                 getParentBlock : function(n) {
321                         var d = this.dom;
322
323                         return d.getParent(n, d.isBlock);
324                 },
325
326                 insertPara : function(e) {
327                         var t = this, ed = t.editor, dom = ed.dom, d = ed.getDoc(), se = ed.settings, s = ed.selection.getSel(), r = s.getRangeAt(0), b = d.body;
328                         var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car;
329
330                         ed.undoManager.beforeChange();
331
332                         // If root blocks are forced then use Operas default behavior since it's really good
333 // Removed due to bug: #1853816
334 //                      if (se.forced_root_block && isOpera)
335 //                              return TRUE;
336
337                         // Setup before range
338                         rb = d.createRange();
339
340                         // If is before the first block element and in body, then move it into first block element
341                         rb.setStart(s.anchorNode, s.anchorOffset);
342                         rb.collapse(TRUE);
343
344                         // Setup after range
345                         ra = d.createRange();
346
347                         // If is before the first block element and in body, then move it into first block element
348                         ra.setStart(s.focusNode, s.focusOffset);
349                         ra.collapse(TRUE);
350
351                         // Setup start/end points
352                         dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;
353                         sn = dir ? s.anchorNode : s.focusNode;
354                         so = dir ? s.anchorOffset : s.focusOffset;
355                         en = dir ? s.focusNode : s.anchorNode;
356                         eo = dir ? s.focusOffset : s.anchorOffset;
357
358                         // If selection is in empty table cell
359                         if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {
360                                 if (sn.firstChild.nodeName == 'BR')
361                                         dom.remove(sn.firstChild); // Remove BR
362
363                                 // Create two new block elements
364                                 if (sn.childNodes.length == 0) {
365                                         ed.dom.add(sn, se.element, null, '<br />');
366                                         aft = ed.dom.add(sn, se.element, null, '<br />');
367                                 } else {
368                                         n = sn.innerHTML;
369                                         sn.innerHTML = '';
370                                         ed.dom.add(sn, se.element, null, n);
371                                         aft = ed.dom.add(sn, se.element, null, '<br />');
372                                 }
373
374                                 // Move caret into the last one
375                                 r = d.createRange();
376                                 r.selectNodeContents(aft);
377                                 r.collapse(1);
378                                 ed.selection.setRng(r);
379
380                                 return FALSE;
381                         }
382
383                         // If the caret is in an invalid location in FF we need to move it into the first block
384                         if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {
385                                 sn = en = sn.firstChild;
386                                 so = eo = 0;
387                                 rb = d.createRange();
388                                 rb.setStart(sn, 0);
389                                 ra = d.createRange();
390                                 ra.setStart(en, 0);
391                         }
392
393                         // Never use body as start or end node
394                         sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
395                         sn = sn.nodeName == "BODY" ? sn.firstChild : sn;
396                         en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
397                         en = en.nodeName == "BODY" ? en.firstChild : en;
398
399                         // Get start and end blocks
400                         sb = t.getParentBlock(sn);
401                         eb = t.getParentBlock(en);
402                         bn = sb ? sb.nodeName : se.element; // Get block name to create
403
404                         // Return inside list use default browser behavior
405                         if (n = t.dom.getParent(sb, 'li,pre')) {
406                                 if (n.nodeName == 'LI')
407                                         return splitList(ed.selection, t.dom, n);
408
409                                 return TRUE;
410                         }
411
412                         // If caption or absolute layers then always generate new blocks within
413                         if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {
414                                 bn = se.element;
415                                 sb = null;
416                         }
417
418                         // If caption or absolute layers then always generate new blocks within
419                         if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {
420                                 bn = se.element;
421                                 eb = null;
422                         }
423
424                         // Use P instead
425                         if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {
426                                 bn = se.element;
427                                 sb = eb = null;
428                         }
429
430                         // Setup new before and after blocks
431                         bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);
432                         aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);
433
434                         // Remove id from after clone
435                         aft.removeAttribute('id');
436
437                         // Is header and cursor is at the end, then force paragraph under
438                         if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))
439                                 aft = ed.dom.create(se.element);
440
441                         // Find start chop node
442                         n = sc = sn;
443                         do {
444                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
445                                         break;
446
447                                 sc = n;
448                         } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));
449
450                         // Find end chop node
451                         n = ec = en;
452                         do {
453                                 if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
454                                         break;
455
456                                 ec = n;
457                         } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));
458
459                         // Place first chop part into before block element
460                         if (sc.nodeName == bn)
461                                 rb.setStart(sc, 0);
462                         else
463                                 rb.setStartBefore(sc);
464
465                         rb.setEnd(sn, so);
466                         bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
467
468                         // Place secnd chop part within new block element
469                         try {
470                                 ra.setEndAfter(ec);
471                         } catch(ex) {
472                                 //console.debug(s.focusNode, s.focusOffset);
473                         }
474
475                         ra.setStart(en, eo);
476                         aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
477
478                         // Create range around everything
479                         r = d.createRange();
480                         if (!sc.previousSibling && sc.parentNode.nodeName == bn) {
481                                 r.setStartBefore(sc.parentNode);
482                         } else {
483                                 if (rb.startContainer.nodeName == bn && rb.startOffset == 0)
484                                         r.setStartBefore(rb.startContainer);
485                                 else
486                                         r.setStart(rb.startContainer, rb.startOffset);
487                         }
488
489                         if (!ec.nextSibling && ec.parentNode.nodeName == bn)
490                                 r.setEndAfter(ec.parentNode);
491                         else
492                                 r.setEnd(ra.endContainer, ra.endOffset);
493
494                         // Delete and replace it with new block elements
495                         r.deleteContents();
496
497                         if (isOpera)
498                                 ed.getWin().scrollTo(0, vp.y);
499
500                         // Never wrap blocks in blocks
501                         if (bef.firstChild && bef.firstChild.nodeName == bn)
502                                 bef.innerHTML = bef.firstChild.innerHTML;
503
504                         if (aft.firstChild && aft.firstChild.nodeName == bn)
505                                 aft.innerHTML = aft.firstChild.innerHTML;
506
507                         function appendStyles(e, en) {
508                                 var nl = [], nn, n, i;
509
510                                 e.innerHTML = '';
511
512                                 // Make clones of style elements
513                                 if (se.keep_styles) {
514                                         n = en;
515                                         do {
516                                                 // We only want style specific elements
517                                                 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {
518                                                         nn = n.cloneNode(FALSE);
519                                                         dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique
520                                                         nl.push(nn);
521                                                 }
522                                         } while (n = n.parentNode);
523                                 }
524
525                                 // Append style elements to aft
526                                 if (nl.length > 0) {
527                                         for (i = nl.length - 1, nn = e; i >= 0; i--)
528                                                 nn = nn.appendChild(nl[i]);
529
530                                         // Padd most inner style element
531                                         nl[0].innerHTML = isOpera ? '\u00a0' : '<br />'; // Extra space for Opera so that the caret can move there
532                                         return nl[0]; // Move caret to most inner element
533                                 } else
534                                         e.innerHTML = isOpera ? '\u00a0' : '<br />'; // Extra space for Opera so that the caret can move there
535                         };
536                                 
537                         // Padd empty blocks
538                         if (dom.isEmpty(bef))
539                                 appendStyles(bef, sn);
540
541                         // Fill empty afterblook with current style
542                         if (dom.isEmpty(aft))
543                                 car = appendStyles(aft, en);
544
545                         // Opera needs this one backwards for older versions
546                         if (isOpera && parseFloat(opera.version()) < 9.5) {
547                                 r.insertNode(bef);
548                                 r.insertNode(aft);
549                         } else {
550                                 r.insertNode(aft);
551                                 r.insertNode(bef);
552                         }
553
554                         // Normalize
555                         aft.normalize();
556                         bef.normalize();
557
558                         // Move cursor and scroll into view
559                         ed.selection.select(aft, true);
560                         ed.selection.collapse(true);
561
562                         // scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs
563                         y = ed.dom.getPos(aft).y;
564                         //ch = aft.clientHeight;
565
566                         // Is element within viewport
567                         if (y < vp.y || y + 25 > vp.y + vp.h) {
568                                 ed.getWin().scrollTo(0, y < vp.y ? y : y - vp.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks
569
570                                 /*console.debug(
571                                         'Element: y=' + y + ', h=' + ch + ', ' +
572                                         'Viewport: y=' + vp.y + ", h=" + vp.h + ', bottom=' + (vp.y + vp.h)
573                                 );*/
574                         }
575
576                         ed.undoManager.add();
577
578                         return FALSE;
579                 },
580
581                 backspaceDelete : function(e, bs) {
582                         var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn, walker;
583
584                         // Delete when caret is behind a element doesn't work correctly on Gecko see #3011651
585                         if (!bs && r.collapsed && sc.nodeType == 1 && r.startOffset == sc.childNodes.length) {
586                                 walker = new tinymce.dom.TreeWalker(sc.lastChild, sc);
587
588                                 // Walk the dom backwards until we find a text node
589                                 for (n = sc.lastChild; n; n = walker.prev()) {
590                                         if (n.nodeType == 3) {
591                                                 r.setStart(n, n.nodeValue.length);
592                                                 r.collapse(true);
593                                                 se.setRng(r);
594                                                 return;
595                                         }
596                                 }
597                         }
598
599                         // The caret sometimes gets stuck in Gecko if you delete empty paragraphs
600                         // This workaround removes the element by hand and moves the caret to the previous element
601                         if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {
602                                 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {
603                                         // Find previous block element
604                                         n = sc;
605                                         while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;
606
607                                         if (n) {
608                                                 if (sc != b.firstChild) {
609                                                         // Find last text node
610                                                         w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE);
611                                                         while (tn = w.nextNode())
612                                                                 n = tn;
613
614                                                         // Place caret at the end of last text node
615                                                         r = ed.getDoc().createRange();
616                                                         r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);
617                                                         r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);
618                                                         se.setRng(r);
619
620                                                         // Remove the target container
621                                                         ed.dom.remove(sc);
622                                                 }
623
624                                                 return Event.cancel(e);
625                                         }
626                                 }
627                         }
628                 }
629         });
630 })(tinymce);