(function() { tinymce.create('tinymce.plugins.annoSection', { init : function(ed, url) { var t = this; t.editor = ed; t.dom = ed.dom; ed.onKeyDown.addToTop(function(ed, e) { if (!e.shiftKey && !e.ctrlKey && e.keyCode == 13) { e.preventDefault(); t.insertPara(e); return false; } return true; }); }, getParentBlock : function(n) { var t = this, ed = t.editor; return ed.dom.getParent(n, ed.dom.isBlock); }, insertPara : function(e) { 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; var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car; var TRUE = true, FALSE = false; ed.undoManager.beforeChange(); // If root blocks are forced then use Operas default behavior since it's really good // Removed due to bug: #1853816 // if (se.forced_root_block && isOpera) // return TRUE; // Setup before range rb = d.createRange(); // If is before the first block element and in body, then move it into first block element rb.setStart(s.anchorNode, s.anchorOffset); rb.collapse(TRUE); // Setup after range ra = d.createRange(); // If is before the first block element and in body, then move it into first block element ra.setStart(s.focusNode, s.focusOffset); ra.collapse(TRUE); // Setup start/end points dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0; sn = dir ? s.anchorNode : s.focusNode; so = dir ? s.anchorOffset : s.focusOffset; en = dir ? s.focusNode : s.anchorNode; eo = dir ? s.focusOffset : s.anchorOffset; // If selection is in empty table cell if (sn === en && /^(TD|TH|CAP)$/.test(sn.nodeName)) { if (sn.firstChild.nodeName == 'BR') dom.remove(sn.firstChild); // Remove BR // Create two new block elements if (sn.childNodes.length == 0) { ed.dom.add(sn, se.element, null, '
'); aft = ed.dom.add(sn, se.element, null, '
'); } else { n = sn.innerHTML; sn.innerHTML = ''; ed.dom.add(sn, se.element, null, n); aft = ed.dom.add(sn, se.element, null, '
'); } // Move caret into the last one r = d.createRange(); r.selectNodeContents(aft); r.collapse(1); ed.selection.setRng(r); return FALSE; } function insertBr(ed) { var selection = ed.selection, rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h; // Insert BR element rng.insertNode(br = dom.create('br')); // Place caret after BR rng.setStartAfter(br); rng.setEndAfter(br); selection.setRng(rng); // Could not place caret after BR then insert an nbsp entity and move the caret if (selection.getSel().focusNode == br.previousSibling) { selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br)); selection.collapse(TRUE); } // Create a temporary DIV after the BR and get the position as it // seems like getPos() returns 0 for text nodes and BR elements. dom.insertAfter(div, br); divYPos = dom.getPos(div).y; dom.remove(div); // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117 if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port. ed.getWin().scrollTo(0, divYPos); }; // If the caret is in an invalid location in FF we need to move it into the first block if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) { sn = en = sn.firstChild; so = eo = 0; rb = d.createRange(); rb.setStart(sn, 0); ra = d.createRange(); ra.setStart(en, 0); } // Never use body as start or end node sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes sn = sn.nodeName == "BODY" ? sn.firstChild : sn; en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes en = en.nodeName == "BODY" ? en.firstChild : en; // Get start and end blocks sb = t.getParentBlock(sn); eb = t.getParentBlock(en); bn = sb ? sb.nodeName : se.element; // Get block name to create if (!/^(P|BODY|HTML)$/.test(bn)) { insertBr(ed); return FALSE; } // Return inside list use default browser behavior if (n = dom.getParent(sb, 'li,pre')) { if (n.nodeName == 'LI') return splitList(ed.selection, t.dom, n); return TRUE; } // If caption or absolute layers then always generate new blocks within if (sb && (sb.nodeName == 'CAP' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) { bn = se.element; sb = null; } // Use P instead if (/(TD|TABLE|TH|CAPTION|CAP)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) { bn = se.element; sb = eb = null; } // Setup new before and after blocks bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn); aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn); // Remove id from after clone aft.removeAttribute('id'); // Is header and cursor is at the end, then force paragraph under if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb)) aft = ed.dom.create(se.element); // Find start chop node n = sc = sn; do { if (n == b || n.nodeType == 9 || dom.isBlock(n) || /(TD|TABLE|TH|CAPTION|CAP)/.test(n.nodeName)) break; sc = n; } while ((n = n.previousSibling ? n.previousSibling : n.parentNode)); // Find end chop node n = ec = en; do { if (n == b || n.nodeType == 9 || dom.isBlock(n) || /(TD|TABLE|TH|CAPTION|CAP)/.test(n.nodeName)) break; ec = n; } while ((n = n.nextSibling ? n.nextSibling : n.parentNode)); // Place first chop part into before block element if (sc.nodeName == bn) rb.setStart(sc, 0); else rb.setStartBefore(sc); rb.setEnd(sn, so); bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari // Place secnd chop part within new block element try { ra.setEndAfter(ec); } catch(ex) { //console.debug(s.focusNode, s.focusOffset); } ra.setStart(en, eo); aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari // Create range around everything r = d.createRange(); if (!sc.previousSibling && sc.parentNode.nodeName == bn) { r.setStartBefore(sc.parentNode); } else { if (rb.startContainer.nodeName == bn && rb.startOffset == 0) r.setStartBefore(rb.startContainer); else r.setStart(rb.startContainer, rb.startOffset); } if (!ec.nextSibling && ec.parentNode.nodeName == bn) r.setEndAfter(ec.parentNode); else r.setEnd(ra.endContainer, ra.endOffset); // Delete and replace it with new block elements r.deleteContents(); // if (isOpera) // ed.getWin().scrollTo(0, vp.y); // Never wrap blocks in blocks if (bef.firstChild && bef.firstChild.nodeName == bn) bef.innerHTML = bef.firstChild.innerHTML; if (aft.firstChild && aft.firstChild.nodeName == bn) aft.innerHTML = aft.firstChild.innerHTML; function appendStyles(e, en) { var nl = [], nn, n, i; e.innerHTML = ''; // Make clones of style elements if (se.keep_styles) { n = en; do { // We only want style specific elements if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) { nn = n.cloneNode(FALSE); dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique nl.push(nn); } } while (n = n.parentNode); } // Append style elements to aft if (nl.length > 0) { for (i = nl.length - 1, nn = e; i >= 0; i--) nn = nn.appendChild(nl[i]); // Padd most inner style element nl[0].innerHTML = isOpera ? '\u00a0' : '
'; // Extra space for Opera so that the caret can move there return nl[0]; // Move caret to most inner element } else e.innerHTML = '
'; }; // Padd empty blocks if (dom.isEmpty(bef)) appendStyles(bef, sn); // Fill empty afterblook with current style if (dom.isEmpty(aft)) car = appendStyles(aft, en); // Opera needs this one backwards for older versions // if (isOpera && parseFloat(opera.version()) < 9.5) { // r.insertNode(bef); // r.insertNode(aft); // } else { r.insertNode(aft); r.insertNode(bef); // } // Normalize aft.normalize(); bef.normalize(); // Move cursor and scroll into view ed.selection.select(aft, true); ed.selection.collapse(true); // 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 y = ed.dom.getPos(aft).y; //ch = aft.clientHeight; // Is element within viewport if (y < vp.y || y + 25 > vp.y + vp.h) { 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 /*console.debug( 'Element: y=' + y + ', h=' + ch + ', ' + 'Viewport: y=' + vp.y + ", h=" + vp.h + ', bottom=' + (vp.y + vp.h) );*/ } ed.undoManager.add(); return FALSE; }, getInfo : function() { return { longname : 'Annotum Link Dialog', author : 'Crowd Favorite', authorurl : 'http://crowdfavorite.org', infourl : '', version : "1.0" }; } }); // Register plugin tinymce.PluginManager.add('annoSection', tinymce.plugins.annoSection); })();