function nodeiter() { if (arguments.length) { this.startnode = arguments[0]; this.endnode = this._get_next_sibling(arguments[0]); this.node = this._get_next(null); }; }; nodeiter.prototype.next = function next() { // some tricks to store a reference to the next node rather than the // current, to allow modifying the current from the calling code without // messing up the logic to find the next item var curr = this.node; this.node = this._get_next(curr); return curr; }; nodeiter.prototype._get_next = function _get_next(node) { if (!node) { node = this.startnode; } else if (node.childNodes.length) { node = node.childNodes[0]; } else { node = this._get_next_sibling(node); if (node == this.endnode) { return null; }; }; return node; }; nodeiter.prototype._get_next_sibling = function _get_next_sibling(node) { var c = node; while (!c.nextSibling) { c = c.parentNode; if (!c) { return; }; }; return c.nextSibling; }; function SSSEditor() { if (arguments.length) { this._init.apply(this, arguments); }; }; SSSEditor.prototype._init = function _init(window, config) { this.window = window; this.document = window.document; this.config = config; this._init_event_handlers(); this._init_ce(); }; SSSEditor.prototype._init_ce = function _init_ce() { this.document.designMode = 'on'; }; SSSEditor.prototype._init_event_handlers = function _init_event_handlers() { misclib.addEventHandler(this.document, 'keydown', this.onkeydown, this); misclib.addEventHandler(this.document, 'keyup', this.onkeyup, this); misclib.addEventHandler(this.document, 'keypress', this.onkeypress, this); }; SSSEditor.prototype.onkeydown = function onkeydown(e) { //this.cancel_event(e); }; SSSEditor.prototype.onkeyup = function onkeyup(e) { this.cancel_event(e); }; SSSEditor.prototype.onkeypress = function onkeypress(e) { //top.testing.debug(e.keyCode + ' - ' + e.charCode); if (!e.altKey) { if (e.ctrlKey) { top.testing.debug(e.charCode); this.cancel_event(e); }; return; }; switch (e.charCode) { case 98: this.addElement('b'); break; case 100: this.addElement('div'); break; case 117: alert(this.window.document.getElementsByTagName('body')[0] .innerHTML); break; }; this.cancel_event(e); //this.cancel_event(e); }; SSSEditor.prototype.cancel_event = function cancel_event(e) { if (e.preventDefault) { e.preventDefault(); }; if (e.stopPropagation) { e.stopPropagation(); }; e.cancelBubble = true; e.returnValue = false; }; SSSEditor.prototype.addElement = function addElement(nodename, isblock) { var win = this.window; var sel = win.getSelection(); var range = sel.getRangeAt(0); if (range.startContainer == range.endContainer && range.startOffset == range.endOffset) { top.testing.debug('no selection'); // no selection // alert('please make a selection first'); return; }; top.testing.debug('sc: ' + range.startContainer.nodeName + ' - ' + range.startContainer.innerHTML); top.testing.debug('ec: ' + range.endContainer.nodeName + ' - ' + range.endContainer.innerHTML); top.testing.debug('ac: ' + range.commonAncestorContainer.nodeName + ' - ' + range.commonAncestorContainer.innerHTML); top.testing.debug('so: ' + range.startOffset); top.testing.debug('eo: ' + range.endOffset); top.testing.debug('cn: ' + range.startContainer.childNodes.length); top.testing.debug('sc == ec == ac: ' + (range.startContainer == range.endContainer)); top.testing.debug('cac: ' + range.startContainer); var sc = range.startContainer; var ec = range.endContainer; var so = range.startOffset; var eo = range.endOffset; var cac = range.commonAncestorContainer; // some shortcuts for fully selected nodes if (sc.nodeType == 1 && sc == ec && so == 0 && eo == 1) { top.testing.debug('only one node of type ' + sc.nodeName); if (sc.nodeName.toLowerCase() == nodename.toLowerCase()) { top.testing.debug('one el with same nodeName as insert node'); range.collapse(true); while (sc.hasChildNodes()) { sc.parentNode.insertBefore(sc.lastChild, sc); }; sc.parentNode.removeChild(sc); return; }; // deal with one node here in case of isblock? } else if (sc.nodeType == 3 && sc == ec && so == 0 && eo == sc.nodeValue) { top.testing.debug('one text node'); if (sc.parentNode.childNodes.length == 1 && sc.parentNode.nodeName.toLowerCase() == nodename.toLowerCase()) { top.testing.debug('parent el with same nodeName as insert node'); var p = sc.parentNode; p.parentNode.replaceChild(sc, p); range.collapse(true); }; }; top.testing.debug('surrounding sel contents'); if (isblock || cac.nodeType == 3) { // we can use the browser's built-in 'surroundContents' top.testing.debug('going to surround'); var node = win.document.createElement(nodename); range.surroundContents(node); top.testing.debug('nodevalue now: ' + node.innerHTML); var sel = win.getSelection(); sel.selectAllChildren(node); } else { // get all text nodes that are actually selected, splitting the first // and last if required, then surround them var iter = new nodeiter(cac); var started = false; var textnodes = []; while (true) { var item = iter.next(); if (!started && item == sc) { started = true; // if item.nodeType == 3 and so > 0, break up item and // surround last bit if (item.nodeType == 3 && so > 0) { var t1 = win.document.createTextNode( item.nodeValue.substr(0, so)); var t2 = win.document.createTextNode( item.nodeValue.substr(so)); item.parentNode.insertBefore(t1, item); item.parentNode.replaceChild(t2, item); item = t2; }; } else if (!started) { continue; }; if (item.nodeType == 3) { // if item == ec and eo < item.nodeValue.length, break up item if (item == ec) { started = false; }; if (item == ec && eo < item.nodeValue.length) { testing.debug('splitting end container'); var t1 = win.document.createTextNode( item.nodeValue.substr(0, eo)); var t2 = win.document.createTextNode( item.nodeValue.substr(eo)); item.parentNode.insertBefore(t1, item); item.parentNode.replaceChild(t2, item); item = t1; }; testing.debug('pushing text node ' + item.nodeValue); textnodes.push(item); }; if (!started || (ec.nodeType == 1 && item == ec.lastChild)) { testing.debug('end container found'); break; }; }; var selstart, selend; testing.debug('textnodes: ' + textnodes.length); for (var i=0; i < textnodes.length; i++) { var tn = textnodes[i]; testing.debug('text node: ' + textnodes[i]); var p = tn.parentNode; top.testing.debug(tn.nodeType + ' pinfo ' + tn.nodeValue + ': ' + p.childNodes.length + ' child, ' + 'nodename == createnodename: ' + (p.nodeName.toLowerCase() == nodename.toLowerCase())); if (p.childNodes.length == 1 && p.nodeName.toLowerCase() == nodename.toLowerCase()) { testing.debug('reverting'); // revert already surrounded node p.parentNode.replaceChild(tn, p); if (!selstart) { selstart = tn; }; selend = tn; } else { var node = win.document.createElement(nodename); textnodes[i].parentNode.replaceChild(node, tn); node.appendChild(tn); if (!selstart) { selstart = node; }; selend = node; }; }; var sel = win.getSelection(); var range = sel.getRangeAt(0); range.setStart(selstart, 0); range.setEnd( selend, selend.nodeType == 3 ? selend.nodeValue.length : 1); sel.addRange(range); }; };