Show:
                                /**
                                 * Wraps some common Selection/Range functionality into a simple object
                                 * @class EditorSelection
                                 * @constructor
                                 * @module editor
                                 * @submodule selection
                                 */
                            
                                //TODO This shouldn't be there, Y.Node doesn't normalize getting textnode content.
                                var textContent = 'textContent',
                                INNER_HTML = 'innerHTML',
                                FONT_FAMILY = 'fontFamily';
                            
                                if (Y.UA.ie && Y.UA.ie < 11) {
                                    textContent = 'nodeValue';
                                }
                            
                                Y.EditorSelection = function(domEvent) {
                                    var sel, par, ieNode, nodes, rng, i,
                                        comp, moved = 0, n, id, root = Y.EditorSelection.ROOT;
                            
                            
                                    if (Y.config.win.getSelection && (!Y.UA.ie || Y.UA.ie < 9 || Y.UA.ie > 10)) {
                                        sel = Y.config.win.getSelection();
                                    } else if (Y.config.doc.selection) {
                                        sel = Y.config.doc.selection.createRange();
                                    }
                                    this._selection = sel;
                            
                                    if (!sel) {
                                        return false;
                                    }
                            
                                    if (sel.pasteHTML) {
                                        this.isCollapsed = (sel.compareEndPoints('StartToEnd', sel)) ? false : true;
                                        if (this.isCollapsed) {
                                            this.anchorNode = this.focusNode = Y.one(sel.parentElement());
                            
                                            if (domEvent) {
                                                ieNode = Y.config.doc.elementFromPoint(domEvent.clientX, domEvent.clientY);
                                            }
                                            rng = sel.duplicate();
                                            if (!ieNode) {
                                                par = sel.parentElement();
                                                nodes = par.childNodes;
                            
                                                for (i = 0; i < nodes.length; i++) {
                                                    //This causes IE to not allow a selection on a doubleclick
                                                    //rng.select(nodes[i]);
                                                    if (rng.inRange(sel)) {
                                                        if (!ieNode) {
                                                            ieNode = nodes[i];
                                                        }
                                                    }
                                                }
                                            }
                            
                                            this.ieNode = ieNode;
                            
                                            if (ieNode) {
                                                if (ieNode.nodeType !== 3) {
                                                    if (ieNode.firstChild) {
                                                        ieNode = ieNode.firstChild;
                                                    }
                                                    if (root.compareTo(ieNode)) {
                                                        if (ieNode.firstChild) {
                                                            ieNode = ieNode.firstChild;
                                                        }
                                                    }
                                                }
                                                this.anchorNode = this.focusNode = Y.EditorSelection.resolve(ieNode);
                            
                                                rng.moveToElementText(sel.parentElement());
                                                comp = sel.compareEndPoints('StartToStart', rng);
                                                if (comp) {
                                                    //We are not at the beginning of the selection.
                                                    //Setting the move to something large, may need to increase it later
                                                    moved = this.getEditorOffset(root);
                                                    sel.move('character', -(moved));
                                                }
                            
                                                this.anchorOffset = this.focusOffset = moved;
                            
                                                this.anchorTextNode = this.focusTextNode = Y.one(ieNode);
                                            }
                            
                            
                                        } else {
                                            //This helps IE deal with a selection and nodeChange events
                                            if (sel.htmlText && sel.htmlText !== '') {
                                                n = Y.Node.create(sel.htmlText);
                                                if (n && n.get('id')) {
                                                    id = n.get('id');
                                                    this.anchorNode = this.focusNode = Y.one('#' + id);
                                                } else if (n) {
                                                    n = n.get('childNodes');
                                                    this.anchorNode = this.focusNode = n.item(0);
                                                }
                                            }
                                        }
                            
                                        //var self = this;
                                        //debugger;
                                    } else {
                                        this.isCollapsed = sel.isCollapsed;
                                        this.anchorNode = Y.EditorSelection.resolve(sel.anchorNode);
                                        this.focusNode = Y.EditorSelection.resolve(sel.focusNode);
                                        this.anchorOffset = sel.anchorOffset;
                                        this.focusOffset = sel.focusOffset;
                            
                                        this.anchorTextNode = Y.one(sel.anchorNode || this.anchorNode);
                                        this.focusTextNode = Y.one(sel.focusNode || this.focusNode);
                                    }
                                    if (Y.Lang.isString(sel.text)) {
                                        this.text = sel.text;
                                    } else {
                                        if (sel.toString) {
                                            this.text = sel.toString();
                                        } else {
                                            this.text = '';
                                        }
                                    }
                                };
                            
                                /**
                                * Utility method to remove dead font-family styles from an element.
                                * @static
                                * @method removeFontFamily
                                */
                                Y.EditorSelection.removeFontFamily = function(n) {
                                    n.removeAttribute('face');
                                    var s = n.getAttribute('style').toLowerCase();
                                    if (s === '' || (s === 'font-family: ')) {
                                        n.removeAttribute('style');
                                    }
                                    if (s.match(Y.EditorSelection.REG_FONTFAMILY)) {
                                        s = s.replace(Y.EditorSelection.REG_FONTFAMILY, '');
                                        n.setAttribute('style', s);
                                    }
                                };
                            
                                /**
                                * Performs a prefilter on all nodes in the editor. Looks for nodes with a style: fontFamily or font face
                                * It then creates a dynamic class assigns it and removed the property. This is so that we don't lose
                                * the fontFamily when selecting nodes.
                                * @static
                                * @method filter
                                */
                                Y.EditorSelection.filter = function(blocks) {
                                    Y.log('Filtering nodes', 'info', 'editor-selection');
                            
                                    var startTime = (new Date()).getTime(),
                                        editorSelection = Y.EditorSelection,
                                        root = editorSelection.ROOT,
                                        endTime,
                                        nodes = root.all(editorSelection.ALL),
                                        baseNodes = root.all('strong,em'),
                                        doc = Y.config.doc, hrs,
                                        classNames = {}, cssString = '',
                                        ls, startTime1 = (new Date()).getTime(),
                                        endTime1;
                            
                                    nodes.each(function(n) {
                                        var raw = Y.Node.getDOMNode(n);
                                        if (raw.style[FONT_FAMILY]) {
                                            classNames['.' + n._yuid] = raw.style[FONT_FAMILY];
                                            n.addClass(n._yuid);
                            
                                            editorSelection.removeFontFamily(raw);
                                        }
                                    });
                                    endTime1 = (new Date()).getTime();
                                    Y.log('Node Filter Timer: ' + (endTime1 - startTime1) + 'ms', 'info', 'editor-selection');
                            
                                    root.all('.hr').addClass('yui-skip').addClass('yui-non');
                            
                                    if (Y.UA.ie) {
                                        hrs = Y.Node.getDOMNode(root).getElementsByTagName('hr');
                                        Y.each(hrs, function(hr) {
                                            var el = doc.createElement('div'),
                                            s = el.style;
                            
                                            el.className = 'hr yui-non yui-skip';
                            
                                            el.setAttribute('readonly', true);
                                            el.setAttribute('contenteditable', false); //Keep it from being Edited
                                            if (hr.parentNode) {
                                                hr.parentNode.replaceChild(el, hr);
                                            }
                                            //Had to move to inline style. writes for ie's < 8. They don't render el.setAttribute('style');
                                            s.border = '1px solid #ccc';
                                            s.lineHeight = '0';
                                            s.height = '0';
                                            s.fontSize = '0';
                                            s.marginTop = '5px';
                                            s.marginBottom = '5px';
                                            s.marginLeft = '0px';
                                            s.marginRight = '0px';
                                            s.padding = '0';
                                        });
                                    }
                            
                            
                                    Y.each(classNames, function(v, k) {
                                        cssString += k + ' { font-family: ' + v.replace(/"/gi, '') + '; }';
                                    });
                                    Y.StyleSheet(cssString, 'editor');
                            
                            
                                    //Not sure about this one?
                                    baseNodes.each(function(n, k) {
                                        var t = n.get('tagName').toLowerCase(),
                                            newTag = 'i';
                                        if (t === 'strong') {
                                            newTag = 'b';
                                        }
                                        editorSelection.prototype._swap(baseNodes.item(k), newTag);
                                    });
                            
                                    //Filter out all the empty UL/OL's
                                    ls = root.all('ol,ul');
                                    ls.each(function(v) {
                                        var lis = v.all('li');
                                        if (!lis.size()) {
                                            v.remove();
                                        }
                                    });
                            
                                    if (blocks) {
                                        editorSelection.filterBlocks();
                                    }
                                    endTime = (new Date()).getTime();
                                    Y.log('Filter Timer: ' + (endTime - startTime) + 'ms', 'info', 'editor-selection');
                                };
                            
                                /**
                                * Method attempts to replace all "orphined" text nodes in the main body by wrapping them with a <p>. Called from filter.
                                * @static
                                * @method filterBlocks
                                */
                                Y.EditorSelection.filterBlocks = function() {
                                    Y.log('RAW filter blocks', 'info', 'editor-selection');
                                    var startTime = (new Date()).getTime(), endTime,
                                        childs = Y.Node.getDOMNode(Y.EditorSelection.ROOT).childNodes, i, node, wrapped = false, doit = true,
                                        sel, single, br, c, s, html;
                            
                                    if (childs) {
                                        for (i = 0; i < childs.length; i++) {
                                            node = Y.one(childs[i]);
                                            if (!node.test(Y.EditorSelection.BLOCKS)) {
                                                doit = true;
                                                if (childs[i].nodeType === 3) {
                                                    c = childs[i][textContent].match(Y.EditorSelection.REG_CHAR);
                                                    s = childs[i][textContent].match(Y.EditorSelection.REG_NON);
                                                    if (c === null && s) {
                                                        doit = false;
                            
                                                    }
                                                }
                                                if (doit) {
                                                    if (!wrapped) {
                                                        wrapped = [];
                                                    }
                                                    wrapped.push(childs[i]);
                                                }
                                            } else {
                                                wrapped = Y.EditorSelection._wrapBlock(wrapped);
                                            }
                                        }
                                        wrapped = Y.EditorSelection._wrapBlock(wrapped);
                                    }
                            
                                    single = Y.all(Y.EditorSelection.DEFAULT_BLOCK_TAG);
                                    if (single.size() === 1) {
                                        Y.log('Only One default block tag (' + Y.EditorSelection.DEFAULT_BLOCK_TAG + '), focus it..', 'info', 'editor-selection');
                                        br = single.item(0).all('br');
                                        if (br.size() === 1) {
                                            if (!br.item(0).test('.yui-cursor')) {
                                                br.item(0).remove();
                                            }
                                            html = single.item(0).get('innerHTML');
                                            if (html === '' || html === ' ') {
                                                Y.log('Paragraph empty, focusing cursor', 'info', 'editor-selection');
                                                single.set('innerHTML', Y.EditorSelection.CURSOR);
                                                sel = new Y.EditorSelection();
                                                sel.focusCursor(true, true);
                                            }
                                            if (br.item(0).test('.yui-cursor') && Y.UA.ie) {
                                                br.item(0).remove();
                                            }
                                        }
                                    } else {
                                        single.each(function(p) {
                                            var html = p.get('innerHTML');
                                            if (html === '') {
                                                Y.log('Empty Paragraph Tag Found, Removing It', 'info', 'editor-selection');
                                                p.remove();
                                            }
                                        });
                                    }
                            
                                    endTime = (new Date()).getTime();
                                    Y.log('FilterBlocks Timer: ' + (endTime - startTime) + 'ms', 'info', 'editor-selection');
                                };
                            
                                /**
                                * Regular Expression used to find dead font-family styles
                                * @static
                                * @property REG_FONTFAMILY
                                */
                                Y.EditorSelection.REG_FONTFAMILY = /font-family:\s*;/;
                            
                                /**
                                * Regular Expression to determine if a string has a character in it
                                * @static
                                * @property REG_CHAR
                                */
                                Y.EditorSelection.REG_CHAR = /[a-zA-Z-0-9_!@#\$%\^&*\(\)-=_+\[\]\\{}|;':",.\/<>\?]/gi;
                            
                                /**
                                * Regular Expression to determine if a string has a non-character in it
                                * @static
                                * @property REG_NON
                                */
                                Y.EditorSelection.REG_NON = /[\s|\n|\t]/gi;
                            
                                /**
                                * Regular Expression to remove all HTML from a string
                                * @static
                                * @property REG_NOHTML
                                */
                                Y.EditorSelection.REG_NOHTML = /<\S[^><]*>/g;
                            
                            
                                /**
                                * Wraps an array of elements in a Block level tag
                                * @static
                                * @private
                                * @method _wrapBlock
                                */
                                Y.EditorSelection._wrapBlock = function(wrapped) {
                                    if (wrapped) {
                                        var newChild = Y.Node.create('<' + Y.EditorSelection.DEFAULT_BLOCK_TAG + '></' + Y.EditorSelection.DEFAULT_BLOCK_TAG + '>'),
                                            firstChild = Y.one(wrapped[0]), i;
                            
                                        for (i = 1; i < wrapped.length; i++) {
                                            newChild.append(wrapped[i]);
                                        }
                                        firstChild.replace(newChild);
                                        newChild.prepend(firstChild);
                                    }
                                    return false;
                                };
                            
                                /**
                                * Undoes what filter does enough to return the HTML from the Editor, then re-applies the filter.
                                * @static
                                * @method unfilter
                                * @return {String} The filtered HTML
                                */
                                Y.EditorSelection.unfilter = function() {
                                    var root = Y.EditorSelection.ROOT,
                                        nodes = root.all('[class]'),
                                        html = '', nons, ids,
                                        body = root;
                            
                                    Y.log('UnFiltering nodes', 'info', 'editor-selection');
                            
                                    nodes.each(function(n) {
                                        if (n.hasClass(n._yuid)) {
                                            //One of ours
                                            n.setStyle(FONT_FAMILY, n.getStyle(FONT_FAMILY));
                                            n.removeClass(n._yuid);
                                            if (n.getAttribute('class') === '') {
                                                n.removeAttribute('class');
                                            }
                                        }
                                    });
                            
                                    nons = root.all('.yui-non');
                                    nons.each(function(n) {
                                        if (!n.hasClass('yui-skip') && n.get('innerHTML') === '') {
                                            n.remove();
                                        } else {
                                            n.removeClass('yui-non').removeClass('yui-skip');
                                        }
                                    });
                            
                                    ids = root.all('[id]');
                                    ids.each(function(n) {
                                        if (n.get('id').indexOf('yui_3_') === 0) {
                                            n.removeAttribute('id');
                                            n.removeAttribute('_yuid');
                                        }
                                    });
                            
                                    if (body) {
                                        html = body.get('innerHTML');
                                    }
                            
                                    root.all('.hr').addClass('yui-skip').addClass('yui-non');
                            
                                    /*
                                    nodes.each(function(n) {
                                        n.addClass(n._yuid);
                                        n.setStyle(FONT_FAMILY, '');
                                        if (n.getAttribute('style') === '') {
                                            n.removeAttribute('style');
                                        }
                                    });
                                    */
                            
                                    return html;
                                };
                                /**
                                * Resolve a node from the selection object and return a Node instance
                                * @static
                                * @method resolve
                                * @param {HTMLElement} n The HTMLElement to resolve. Might be a TextNode, gives parentNode.
                                * @return {Node} The Resolved node
                                */
                                Y.EditorSelection.resolve = function(n) {
                                    if (!n) {
                                        return Y.EditorSelection.ROOT;
                                    }
                            
                                    if (n && n.nodeType === 3) {
                                        //Adding a try/catch here because in rare occasions IE will
                                        //Throw a error accessing the parentNode of a stranded text node.
                                        //In the case of Ctrl+Z (Undo)
                                        try {
                                            n = n.parentNode;
                                        } catch (re) {
                                            n = Y.EditorSelection.ROOT;
                                        }
                                    }
                                    return Y.one(n);
                                };
                            
                                /**
                                * Returns the innerHTML of a node with all HTML tags removed.
                                * @static
                                * @method getText
                                * @param {Node} node The Node instance to remove the HTML from
                                * @return {String} The string of text
                                */
                                Y.EditorSelection.getText = function(node) {
                                    var txt = node.get('innerHTML').replace(Y.EditorSelection.REG_NOHTML, '');
                                    //Clean out the cursor subs to see if the Node is empty
                                    txt = txt.replace('<span><br></span>', '').replace('<br>', '');
                                    return txt;
                                };
                            
                                //Y.EditorSelection.DEFAULT_BLOCK_TAG = 'div';
                                Y.EditorSelection.DEFAULT_BLOCK_TAG = 'p';
                            
                                /**
                                * The selector to use when looking for Nodes to cache the value of: [style],font[face]
                                * @static
                                * @property ALL
                                */
                                Y.EditorSelection.ALL = '[style],font[face]';
                            
                                /**
                                * The selector to use when looking for block level items.
                                * @static
                                * @property BLOCKS
                                */
                                Y.EditorSelection.BLOCKS = 'p,div,ul,ol,table,style';
                                /**
                                * The temporary fontname applied to a selection to retrieve their values: yui-tmp
                                * @static
                                * @property TMP
                                */
                                Y.EditorSelection.TMP = 'yui-tmp';
                                /**
                                * The default tag to use when creating elements: span
                                * @static
                                * @property DEFAULT_TAG
                                */
                                Y.EditorSelection.DEFAULT_TAG = 'span';
                            
                                /**
                                * The id of the outer cursor wrapper
                                * @static
                                * @property CURID
                                */
                                Y.EditorSelection.CURID = 'yui-cursor';
                            
                                /**
                                * The id used to wrap the inner space of the cursor position
                                * @static
                                * @property CUR_WRAPID
                                */
                                Y.EditorSelection.CUR_WRAPID = 'yui-cursor-wrapper';
                            
                                /**
                                * The default HTML used to focus the cursor..
                                * @static
                                * @property CURSOR
                                */
                                Y.EditorSelection.CURSOR = '<span><br class="yui-cursor"></span>';
                            
                                /**
                                * The default HTML element from which data will be retrieved. Default: body
                                * @static
                                * @property ROOT
                                */
                                Y.EditorSelection.ROOT = Y.one('body');
                            
                                Y.EditorSelection.hasCursor = function() {
                                    var cur = Y.all('#' + Y.EditorSelection.CUR_WRAPID);
                                    Y.log('Has Cursor: ' + cur.size(), 'info', 'editor-selection');
                                    return cur.size();
                                };
                            
                                /**
                                * Called from Editor keydown to remove the "extra" space before the cursor.
                                * @static
                                * @method cleanCursor
                                */
                                Y.EditorSelection.cleanCursor = function() {
                                    //Y.log('Cleaning Cursor', 'info', 'Selection');
                                    var cur, sel = 'br.yui-cursor';
                                    cur = Y.all(sel);
                                    if (cur.size()) {
                                        cur.each(function(b) {
                                            var c = b.get('parentNode.parentNode.childNodes'), html;
                                            if (c.size()) {
                                                b.remove();
                                            } else {
                                                html = Y.EditorSelection.getText(c.item(0));
                                                if (html !== '') {
                                                    b.remove();
                                                }
                                            }
                                        });
                                    }
                                    /*
                                    var cur = Y.all('#' + Y.EditorSelection.CUR_WRAPID);
                                    if (cur.size()) {
                                        cur.each(function(c) {
                                            var html = c.get('innerHTML');
                                            if (html == '&nbsp;' || html == '<br>') {
                                                if (c.previous() || c.next()) {
                                                    c.remove();
                                                }
                                            }
                                        });
                                    }
                                    */
                                };
                            
                                Y.EditorSelection.prototype = {
                                    /**
                                    * Range text value
                                    * @property text
                                    * @type String
                                    */
                                    text: null,
                                    /**
                                    * Flag to show if the range is collapsed or not
                                    * @property isCollapsed
                                    * @type Boolean
                                    */
                                    isCollapsed: null,
                                    /**
                                    * A Node instance of the parentNode of the anchorNode of the range
                                    * @property anchorNode
                                    * @type Node
                                    */
                                    anchorNode: null,
                                    /**
                                    * The offset from the range object
                                    * @property anchorOffset
                                    * @type Number
                                    */
                                    anchorOffset: null,
                                    /**
                                    * A Node instance of the actual textNode of the range.
                                    * @property anchorTextNode
                                    * @type Node
                                    */
                                    anchorTextNode: null,
                                    /**
                                    * A Node instance of the parentNode of the focusNode of the range
                                    * @property focusNode
                                    * @type Node
                                    */
                                    focusNode: null,
                                    /**
                                    * The offset from the range object
                                    * @property focusOffset
                                    * @type Number
                                    */
                                    focusOffset: null,
                                    /**
                                    * A Node instance of the actual textNode of the range.
                                    * @property focusTextNode
                                    * @type Node
                                    */
                                    focusTextNode: null,
                                    /**
                                    * The actual Selection/Range object
                                    * @property _selection
                                    * @private
                                    */
                                    _selection: null,
                                    /**
                                    * Wrap an element, with another element
                                    * @private
                                    * @method _wrap
                                    * @param {HTMLElement} n The node to wrap
                                    * @param {String} tag The tag to use when creating the new element.
                                    * @return {HTMLElement} The wrapped node
                                    */
                                    _wrap: function(n, tag) {
                                        var tmp = Y.Node.create('<' + tag + '></' + tag + '>');
                                        tmp.set(INNER_HTML, n.get(INNER_HTML));
                                        n.set(INNER_HTML, '');
                                        n.append(tmp);
                                        return Y.Node.getDOMNode(tmp);
                                    },
                                    /**
                                    * Swap an element, with another element
                                    * @private
                                    * @method _swap
                                    * @param {HTMLElement} n The node to swap
                                    * @param {String} tag The tag to use when creating the new element.
                                    * @return {HTMLElement} The new node
                                    */
                                    _swap: function(n, tag) {
                                        var tmp = Y.Node.create('<' + tag + '></' + tag + '>');
                                        tmp.set(INNER_HTML, n.get(INNER_HTML));
                                        n.replace(tmp, n);
                                        return Y.Node.getDOMNode(tmp);
                                    },
                                    /**
                                    * Get all the nodes in the current selection. This method will actually perform a filter first.
                                    * Then it calls doc.execCommand('fontname', null, 'yui-tmp') to touch all nodes in the selection.
                                    * The it compiles a list of all nodes affected by the execCommand and builds a NodeList to return.
                                    * @method getSelected
                                    * @return {NodeList} A NodeList of all items in the selection.
                                    */
                                    getSelected: function() {
                                        var editorSelection = Y.EditorSelection,
                                            root = editorSelection.ROOT,
                                            nodes,
                                            items = [];
                            
                                        editorSelection.filter();
                                        Y.config.doc.execCommand('fontname', null, editorSelection.TMP);
                                        nodes = root.all(editorSelection.ALL);
                            
                                        nodes.each(function(n, k) {
                                            if (n.getStyle(FONT_FAMILY) === editorSelection.TMP) {
                                                n.setStyle(FONT_FAMILY, '');
                                                editorSelection.removeFontFamily(n);
                                                if (!n.compareTo(root)) {
                                                    items.push(Y.Node.getDOMNode(nodes.item(k)));
                                                }
                                            }
                                        });
                                        return Y.all(items);
                                    },
                                    /**
                                    * Insert HTML at the current cursor position and return a Node instance of the newly inserted element.
                                    * @method insertContent
                                    * @param {String} html The HTML to insert.
                                    * @return {Node} The inserted Node.
                                    */
                                    insertContent: function(html) {
                                        return this.insertAtCursor(html, this.anchorTextNode, this.anchorOffset, true);
                                    },
                                    /**
                                    * Insert HTML at the current cursor position, this method gives you control over the text node to insert into and the offset where to put it.
                                    * @method insertAtCursor
                                    * @param {String} html The HTML to insert.
                                    * @param {Node} node The text node to break when inserting.
                                    * @param {Number} offset The left offset of the text node to break and insert the new content.
                                    * @param {Boolean} collapse Should the range be collapsed after insertion. default: false
                                    * @return {Node} The inserted Node.
                                    */
                                    insertAtCursor: function(html, node, offset, collapse) {
                                        var cur = Y.Node.create('<' + Y.EditorSelection.DEFAULT_TAG + ' class="yui-non"></' + Y.EditorSelection.DEFAULT_TAG + '>'),
                                            inHTML, txt, txt2, newNode, range = this.createRange(), b, root = Y.EditorSelection.ROOT;
                            
                                        if (root.compareTo(node)) {
                                            b = Y.Node.create('<span></span>');
                                            node.append(b);
                                            node = b;
                                        }
                            
                            
                                        if (range.pasteHTML) {
                                            if (offset === 0 && node && !node.previous() && node.get('nodeType') === 3) {
                                                /*
                                                * For some strange reason, range.pasteHTML fails if the node is a textNode and
                                                * the offset is 0. (The cursor is at the beginning of the line)
                                                * It will always insert the new content at position 1 instead of
                                                * position 0. Here we test for that case and do it the hard way.
                                                */
                                                node.insert(html, 'before');
                                                if (range.moveToElementText) {
                                                    range.moveToElementText(Y.Node.getDOMNode(node.previous()));
                                                }
                                                //Move the cursor after the new node
                                                range.collapse(false);
                                                range.select();
                                                return node.previous();
                                            } else {
                                                newNode = Y.Node.create(html);
                                                try {
                                                    range.pasteHTML('<span id="rte-insert"></span>');
                                                } catch (e) {}
                                                inHTML = root.one('#rte-insert');
                                                if (inHTML) {
                                                    inHTML.set('id', '');
                                                    inHTML.replace(newNode);
                                                    if (range.moveToElementText) {
                                                        range.moveToElementText(Y.Node.getDOMNode(newNode));
                                                    }
                                                    range.collapse(false);
                                                    range.select();
                                                    return newNode;
                                                } else {
                                                    Y.on('available', function() {
                                                        inHTML.set('id', '');
                                                        inHTML.replace(newNode);
                                                        if (range.moveToElementText) {
                                                            range.moveToElementText(Y.Node.getDOMNode(newNode));
                                                        }
                                                        range.collapse(false);
                                                        range.select();
                                                    }, '#rte-insert');
                                                }
                                            }
                                        } else {
                                            //TODO using Y.Node.create here throws warnings & strips first white space character
                                            //txt = Y.one(Y.Node.create(inHTML.substr(0, offset)));
                                            //txt2 = Y.one(Y.Node.create(inHTML.substr(offset)));
                                            if (offset > 0) {
                                                inHTML = node.get(textContent);
                            
                                                txt = Y.one(Y.config.doc.createTextNode(inHTML.substr(0, offset)));
                                                txt2 = Y.one(Y.config.doc.createTextNode(inHTML.substr(offset)));
                            
                                                node.replace(txt, node);
                                                newNode = Y.Node.create(html);
                                                if (newNode.get('nodeType') === 11) {
                                                    b = Y.Node.create('<span></span>');
                                                    b.append(newNode);
                                                    newNode = b;
                                                }
                                                txt.insert(newNode, 'after');
                                                //if (txt2 && txt2.get('length')) {
                                                if (txt2) {
                                                    newNode.insert(cur, 'after');
                                                    cur.insert(txt2, 'after');
                                                    this.selectNode(cur, collapse);
                                                }
                                            } else {
                                                if (node.get('nodeType') === 3) {
                                                    node = node.get('parentNode') || root;
                                                }
                                                newNode = Y.Node.create(html);
                                                html = node.get('innerHTML').replace(/\n/gi, '');
                                                if (html === '' || html === '<br>') {
                                                    node.append(newNode);
                                                } else {
                                                    if (newNode.get('parentNode')) {
                                                        node.insert(newNode, 'before');
                                                    } else {
                                                        root.prepend(newNode);
                                                    }
                                                }
                                                if (node.get('firstChild').test('br')) {
                                                    node.get('firstChild').remove();
                                                }
                                            }
                                        }
                                        return newNode;
                                    },
                                    /**
                                    * Get all elements inside a selection and wrap them with a new element and return a NodeList of all elements touched.
                                    * @method wrapContent
                                    * @param {String} tag The tag to wrap all selected items with.
                                    * @return {NodeList} A NodeList of all items in the selection.
                                    */
                                    wrapContent: function(tag) {
                                        tag = (tag) ? tag : Y.EditorSelection.DEFAULT_TAG;
                            
                                        if (!this.isCollapsed) {
                                            Y.log('Wrapping selection with: ' + tag, 'info', 'editor-selection');
                                            var items = this.getSelected(),
                                                changed = [], range, last, first, range2;
                            
                                            items.each(function(n, k) {
                                                var t = n.get('tagName').toLowerCase();
                                                if (t === 'font') {
                                                    changed.push(this._swap(items.item(k), tag));
                                                } else {
                                                    changed.push(this._wrap(items.item(k), tag));
                                                }
                                            }, this);
                            
                                            range = this.createRange();
                                            first = changed[0];
                                            last = changed[changed.length - 1];
                                            if (this._selection.removeAllRanges) {
                                                range.setStart(changed[0], 0);
                                                range.setEnd(last, last.childNodes.length);
                                                this._selection.removeAllRanges();
                                                this._selection.addRange(range);
                                            } else {
                                                if (range.moveToElementText) {
                                                    range.moveToElementText(Y.Node.getDOMNode(first));
                                                    range2 = this.createRange();
                                                    range2.moveToElementText(Y.Node.getDOMNode(last));
                                                    range.setEndPoint('EndToEnd', range2);
                                                }
                                                range.select();
                                            }
                            
                                            changed = Y.all(changed);
                                            Y.log('Returning NodeList with (' + changed.size() + ') item(s)' , 'info', 'editor-selection');
                                            return changed;
                            
                            
                                        } else {
                                            Y.log('Can not wrap a collapsed selection, use insertContent', 'error', 'editor-selection');
                                            return Y.all([]);
                                        }
                                    },
                                    /**
                                    * Find and replace a string inside a text node and replace it with HTML focusing the node after
                                    * to allow you to continue to type.
                                    * @method replace
                                    * @param {String} se The string to search for.
                                    * @param {String} re The string of HTML to replace it with.
                                    * @return {Node} The node inserted.
                                    */
                                    replace: function(se,re) {
                                        Y.log('replacing (' + se + ') with (' + re + ')');
                                        var range = this.createRange(), node, txt, index, newNode;
                            
                                        if (range.getBookmark) {
                                            index = range.getBookmark();
                                            txt = this.anchorNode.get('innerHTML').replace(se, re);
                                            this.anchorNode.set('innerHTML', txt);
                                            range.moveToBookmark(index);
                                            newNode = Y.one(range.parentElement());
                                        } else {
                                            node = this.anchorTextNode;
                                            txt = node.get(textContent);
                                            index = txt.indexOf(se);
                            
                                            txt = txt.replace(se, '');
                                            node.set(textContent, txt);
                                            newNode = this.insertAtCursor(re, node, index, true);
                                        }
                                        return newNode;
                                    },
                                    /**
                                    * Destroy the range.
                                    * @method remove
                                    * @chainable
                                    * @return {EditorSelection}
                                    */
                                    remove: function() {
                                        if (this._selection && this._selection.removeAllRanges) {
                                            this._selection.removeAllRanges();
                                        }
                                        return this;
                                    },
                                    /**
                                    * Wrapper for the different range creation methods.
                                    * @method createRange
                                    * @return {Range}
                                    */
                                    createRange: function() {
                                        if (Y.config.doc.selection) {
                                            return Y.config.doc.selection.createRange();
                                        } else {
                                            return Y.config.doc.createRange();
                                        }
                                    },
                                    /**
                                    * Select a Node (hilighting it).
                                    * @method selectNode
                                    * @param {Node} node The node to select
                                    * @param {Boolean} collapse Should the range be collapsed after insertion. default: false
                                    * @chainable
                                    * @return {EditorSelection}
                                    */
                                    selectNode: function(node, collapse, end) {
                                        if (!node) {
                                            Y.log('Node passed to selectNode is null', 'error', 'editor-selection');
                                            return;
                                        }
                                        end = end || 0;
                                        node = Y.Node.getDOMNode(node);
                                        var range = this.createRange();
                                        if (range.selectNode) {
                                            try {
                                                range.selectNode(node);
                                            } catch (err) {
                                                // Ignore selection errors like INVALID_NODE_TYPE_ERR
                                            }
                                            this._selection.removeAllRanges();
                                            this._selection.addRange(range);
                                            if (collapse) {
                                                try {
                                                    this._selection.collapse(node, end);
                                                } catch (err) {
                                                    this._selection.collapse(node, 0);
                                                }
                                            }
                                        } else {
                                            if (node.nodeType === 3) {
                                                node = node.parentNode;
                                            }
                                            try {
                                                range.moveToElementText(node);
                                            } catch(e) {}
                                            if (collapse) {
                                                range.collapse(((end) ? false : true));
                                            }
                                            range.select();
                                        }
                                        return this;
                                    },
                                    /**
                                    * Put a placeholder in the DOM at the current cursor position.
                                    * @method setCursor
                                    * @return {Node}
                                    */
                                    setCursor: function() {
                                        this.removeCursor(false);
                                        return this.insertContent(Y.EditorSelection.CURSOR);
                                    },
                                    /**
                                    * Get the placeholder in the DOM at the current cursor position.
                                    * @method getCursor
                                    * @return {Node}
                                    */
                                    getCursor: function() {
                                        return Y.EditorSelection.ROOT.all('.' + Y.EditorSelection.CURID).get('parentNode');
                                    },
                                    /**
                                    * Remove the cursor placeholder from the DOM.
                                    * @method removeCursor
                                    * @param {Boolean} keep Setting this to true will keep the node, but remove the unique parts that make it the cursor.
                                    * @return {Node}
                                    */
                                    removeCursor: function(keep) {
                                        var cur = this.getCursor();
                                        if (cur && cur.remove) {
                                            if (keep) {
                                                cur.set('innerHTML', '<br class="yui-cursor">');
                                            } else {
                                                cur.remove();
                                            }
                                        }
                                        return cur;
                                    },
                                    /**
                                    * Gets a stored cursor and focuses it for editing, must be called sometime after setCursor
                                    * @method focusCursor
                                    * @return {Node}
                                    */
                                    focusCursor: function(collapse, end) {
                                        if (collapse !== false) {
                                            collapse = true;
                                        }
                                        if (end !== false) {
                                            end = true;
                                        }
                                        var cur = this.removeCursor(true);
                                        if (cur) {
                                            cur.each(function(c) {
                                                this.selectNode(c, collapse, end);
                                            }, this);
                                        }
                                    },
                                    /**
                                    * Generic toString for logging.
                                    * @method toString
                                    * @return {String}
                                    */
                                    toString: function() {
                                        return 'EditorSelection Object';
                                    },
                            
                                    /**
                                     Gets the offset of the selection for the selection within the current
                                     editor
                                     @public
                                     @method getEditorOffset
                                     @param {Y.Node} [node] Element used to measure the offset to
                                     @return Number Number of characters the selection is from the beginning
                                     @since 3.13.0
                                     */
                                    getEditorOffset: function(node) {
                                        var container = (node || Y.EditorSelection.ROOT).getDOMNode(),
                                            caretOffset = 0,
                                            doc = Y.config.doc,
                                            win = Y.config.win,
                                            sel,
                                            range,
                                            preCaretRange;
                            
                                        if (typeof win.getSelection !== "undefined") {
                                            range = win.getSelection().getRangeAt(0);
                                            preCaretRange = range.cloneRange();
                                            preCaretRange.selectNodeContents(container);
                                            preCaretRange.setEnd(range.endContainer, range.endOffset);
                                            caretOffset = preCaretRange.toString().length;
                                        } else {
                                            sel = doc.selection;
                            
                                            if ( sel && sel.type !== "Control") {
                                                range = sel.createRange();
                                                preCaretRange = doc.body.createTextRange();
                                                preCaretRange.moveToElementText(container);
                                                preCaretRange.setEndPoint("EndToEnd", range);
                                                caretOffset = preCaretRange.text.length;
                                            }
                                        }
                            
                                        return caretOffset;
                                    }
                                };
                            
                                //TODO Remove this alias in 3.6.0
                                Y.Selection = Y.EditorSelection;