Show:
                            (function() {
                                'use strict';
                            
                                if (CKEDITOR.plugins.get('ae_selectionregion')) {
                                    return;
                                }
                            
                                CKEDITOR.SELECTION_TOP_TO_BOTTOM = 0;
                                CKEDITOR.SELECTION_BOTTOM_TO_TOP = 1;
                                CKEDITOR.SELECTION_LEFT_TO_RIGHT = 2;
                                CKEDITOR.SELECTION_RIGHT_TO_LEFT = 3;
                            
                                /**
                                 * SelectionRegion utility class which provides metadata about the selection. The metadata may be the start and end
                                 * rectangles, caret region, etc. **This class is not intended to be used standalone. Its functions will
                                 * be merged into each editor instance, so the developer may use them directly via the editor, without making
                                 * an instance of this class**.
                                 *
                                 * @class CKEDITOR.plugins.ae_selectionregion
                                 * @constructor
                                 */
                                function SelectionRegion() {}
                            
                                SelectionRegion.prototype = {
                                    constructor: SelectionRegion,
                            
                                    /**
                                     * Creates selection from two points in page coordinates.
                                     *
                                     * @method createSelectionFromPoint
                                     * @param {Number} x X point in page coordinates.
                                     * @param {Number} y Y point in page coordinates.
                                     */
                                    createSelectionFromPoint: function(x, y) {
                                        this.createSelectionFromRange(x, y, x, y);
                                    },
                            
                                    /**
                                     * Creates selection from range. A range consists from two points in page coordinates.
                                     *
                                     * @method createSelectionFromRange
                                     * @param {Number} startX X coordinate of the first point.
                                     * @param {Number} startY Y coordinate of the first point.
                                     * @param {Number} endX X coordinate of the second point.
                                     * @param {Number} endY Y coordinate of the second point.
                                     */
                                    createSelectionFromRange: function(startX, startY, endX, endY) {
                                        var end;
                                        var endContainer;
                                        var endOffset;
                                        var range;
                                        var start;
                                        var startContainer;
                                        var startOffset;
                            
                                        if (typeof document.caretPositionFromPoint === 'function') {
                                            start = document.caretPositionFromPoint(startX, startY);
                                            end = document.caretPositionFromPoint(endX, endY);
                            
                                            startContainer = start.offsetNode;
                                            endContainer = end.offsetNode;
                            
                                            startOffset = start.offset;
                                            endOffset = end.offset;
                            
                                            range = this.createRange();
                                        } else if (typeof document.caretRangeFromPoint === 'function') {
                                            start = document.caretRangeFromPoint(startX, startY);
                                            end = document.caretRangeFromPoint(endX, endY);
                            
                                            startContainer = start.startContainer;
                                            endContainer = end.startContainer;
                            
                                            startOffset = start.startOffset;
                                            endOffset = end.startOffset;
                            
                                            range = this.createRange();
                                        }
                            
                                        if (range && document.getSelection) {
                                            range.setStart(new CKEDITOR.dom.node(startContainer), startOffset);
                                            range.setEnd(new CKEDITOR.dom.node(endContainer), endOffset);
                            
                                            this.getSelection().selectRanges([range]);
                                        } else if (typeof document.body.createTextRange === 'function') {
                                            var selection = this.getSelection();
                            
                                            selection.unlock();
                            
                                            range = document.body.createTextRange();
                                            range.moveToPoint(startX, startY);
                            
                                            var endRange = range.duplicate();
                                            endRange.moveToPoint(endX, endY);
                            
                                            range.setEndPoint('EndToEnd', endRange);
                                            range.select();
                            
                                            this.getSelection().lock();
                                        }
                                    },
                            
                                    /**
                                     * Returns the region of the current position of the caret. The points are in page coordinates.
                                     *
                                     * @method getCaretRegion
                                     * @return {Object} Returns object with the following properties:
                                     * - bottom
                                     * - left
                                     * - right
                                     * - top
                                     */
                                    getCaretRegion: function() {
                                        var selection = this.getSelection();
                            
                                        var region = {
                                            bottom: 0,
                                            left: 0,
                                            right: 0,
                                            top: 0
                                        };
                            
                                        var bookmarks = selection.createBookmarks();
                            
                                        if (!bookmarks.length) {
                                            return region;
                                        }
                            
                                        var bookmarkNodeEl = bookmarks[0].startNode.$;
                            
                                        bookmarkNodeEl.style.display = 'inline-block';
                            
                                        region = new CKEDITOR.dom.element(bookmarkNodeEl).getClientRect();
                            
                                        bookmarkNodeEl.parentNode.removeChild(bookmarkNodeEl);
                            
                                        var scrollPos = new CKEDITOR.dom.window(window).getScrollPosition();
                            
                                        region.bottom = scrollPos.y + region.bottom;
                                        region.left = scrollPos.x + region.left;
                                        region.right = scrollPos.x + region.right;
                                        region.top = scrollPos.y + region.top;
                            
                                        return region;
                                    },
                            
                                    /**
                                     * Returns data for the current selection.
                                     *
                                     * @method getSelectionData
                                     * @return {Object|null} Returns an object with the following data:
                                     * - element - The currently selected element, if any
                                     * - text - The selected text
                                     * - region - The data, returned from {{#crossLink "CKEDITOR.plugins.ae_selectionregion/getSelectionRegion:method"}}{{/crossLink}}
                                     */
                                    getSelectionData: function() {
                                        var selection = this.getSelection();
                            
                                        if (!selection.getNative()) {
                                            return null;
                                        }
                            
                                        var result = {
                                            element: selection.getSelectedElement(),
                                            text: selection.getSelectedText()
                                        };
                            
                                        result.region = this.getSelectionRegion(selection);
                            
                                        return result;
                                    },
                            
                                    /**
                                     * Returns the region of the current selection.
                                     *
                                     * @method getSelectionRegion
                                     * @return {Object} Returns object which is being returned from
                                     * {{#crossLink "CKEDITOR.plugins.ae_selectionregion/getClientRectsRegion:method"}}{{/crossLink}} with three more properties:
                                     * - direction - the direction of the selection. Can be one of these:
                                     *   1. CKEDITOR.SELECTION_TOP_TO_BOTTOM
                                     *   2. CKEDITOR.SELECTION_BOTTOM_TO_TOP
                                     * - height - The height of the selection region
                                     * - width - The width of the selection region
                                     */
                                    getSelectionRegion: function() {
                                        var region = this.getClientRectsRegion();
                            
                                        region.direction = this.getSelectionDirection();
                            
                                        region.height = region.bottom - region.top;
                                        region.width = region.right - region.left;
                            
                                        return region;
                                    },
                            
                                    /**
                                     * Returns true if the current selection is empty, false otherwise.
                                     *
                                     * @method isSelectionEmpty
                                     * @return {Boolean} Returns true if the current selection is empty, false otherwise.
                                     */
                                    isSelectionEmpty: function() {
                                        var ranges;
                            
                                        var selection = this.getSelection();
                            
                                        return (selection.getType() === CKEDITOR.SELECTION_NONE) ||
                                            ((ranges = selection.getRanges()) && ranges.length === 1 && ranges[0].collapsed);
                                    },
                            
                                    /**
                                     * Returns object with data about the [client rectangles](https://developer.mozilla.org/en-US/docs/Web/API/Element.getClientRects) of the selection,
                                     * normalized across browses. All offsets below are in page coordinates.
                                     *
                                     * @method getClientRectsRegion
                                     * @return {Object} Returns object with the following data:
                                     * - bottom - bottom offset of all client rectangles
                                     * - left - left offset of all client rectangles
                                     * - right - right offset of all client rectangles
                                     * - top - top offset of all client rectangles
                                     * - startRect - An Object, which contains the following information:
                                     *     + bottom - bottom offset
                                     *     + height - the height of the rectangle
                                     *     + left - left offset of the selection
                                     *     + right - right offset of the selection
                                     *     + top - top offset of the selection
                                     *     + width - the width of the rectangle
                                     * - endRect - An Object, which contains the following information:
                                     *     + bottom - bottom offset
                                     *     + height - the height of the rectangle
                                     *     + left - left offset of the selection
                                     *     + right - right offset of the selection
                                     *     + top - top offset of the selection
                                     *     + width - the width of the rectangle
                                     *
                                     * If there is no native selection, the objects will be filled with 0.
                                     */
                                    getClientRectsRegion: function() {
                                        var selection = this.getSelection();
                                        var nativeSelection = selection.getNative();
                            
                                        var defaultRect = {
                                            bottom: 0,
                                            height: 0,
                                            left: 0,
                                            right: 0,
                                            top: 0,
                                            width: 0
                                        };
                            
                                        var region = {
                                            bottom: 0,
                                            endRect: defaultRect,
                                            left: 0,
                                            right: 0,
                                            top: 0,
                                            startRect: defaultRect
                                        };
                            
                                        if (!nativeSelection) {
                                            return region;
                                        }
                            
                                        var bottom = 0;
                                        var clientRects;
                                        var left = Infinity;
                                        var rangeCount;
                                        var right = -Infinity;
                                        var top = Infinity;
                            
                                        if (nativeSelection.createRange) {
                                            clientRects = nativeSelection.createRange().getClientRects();
                                        } else {
                                            rangeCount = nativeSelection.rangeCount;
                                            clientRects = (nativeSelection.rangeCount > 0) ? nativeSelection.getRangeAt(0).getClientRects() : [];
                                        }
                            
                                        if (clientRects.length === 0) {
                                            region = this.getCaretRegion();
                                        } else {
                                            for (var i = 0, length = clientRects.length; i < length; i++) {
                                                var item = clientRects[i];
                            
                                                if (item.left < left) {
                                                    left = item.left;
                                                }
                            
                                                if (item.right > right) {
                                                    right = item.right;
                                                }
                            
                                                if (item.top < top) {
                                                    top = item.top;
                                                }
                            
                                                if (item.bottom > bottom) {
                                                    bottom = item.bottom;
                                                }
                                            }
                            
                                            var scrollPos = new CKEDITOR.dom.window(window).getScrollPosition();
                            
                                            region.bottom = scrollPos.y + bottom;
                                            region.left = scrollPos.x + left;
                                            region.right = scrollPos.x + right;
                                            region.top = scrollPos.y + top;
                            
                                            if (clientRects.length) {
                                                var endRect = clientRects[clientRects.length - 1];
                                                var startRect = clientRects[0];
                            
                                                region.endRect = {
                                                    bottom: scrollPos.y + endRect.bottom,
                                                    height: endRect.height,
                                                    left: scrollPos.x + endRect.left,
                                                    right: scrollPos.x + endRect.right,
                                                    top: scrollPos.y + endRect.top,
                                                    width: endRect.width
                                                };
                            
                                                region.startRect = {
                                                    bottom: scrollPos.y + startRect.bottom,
                                                    height: startRect.height,
                                                    left: scrollPos.x + startRect.left,
                                                    right: scrollPos.x + startRect.right,
                                                    top: scrollPos.y + startRect.top,
                                                    width: startRect.width
                                                };
                                            }
                                        }
                            
                                        return region;
                                    },
                            
                                    /**
                                     * Retrieves the direction of the selection. The direction is from top to bottom or from bottom to top.
                                     * For IE < 9 it is not possible, so the direction for these browsers will be always CKEDITOR.SELECTION_TOP_TO_BOTTOM.
                                     *
                                     * @method getSelectionDirection
                                     * @return {Number} Returns a number which represents selection direction. It might be one of these:
                                     * - CKEDITOR.SELECTION_TOP_TO_BOTTOM;
                                     * - CKEDITOR.SELECTION_BOTTOM_TO_TOP;
                                     */
                                    getSelectionDirection: function() {
                                        var direction = CKEDITOR.SELECTION_TOP_TO_BOTTOM;
                                        var selection = this.getSelection();
                                        var nativeSelection = selection.getNative();
                            
                                        if (!nativeSelection) {
                                            return direction;
                                        }
                            
                                        var anchorNode;
                            
                                        if ((anchorNode = nativeSelection.anchorNode) && anchorNode.compareDocumentPosition) {
                                            var position = anchorNode.compareDocumentPosition(nativeSelection.focusNode);
                            
                                            if (!position && nativeSelection.anchorOffset > nativeSelection.focusOffset || position === Node.DOCUMENT_POSITION_PRECEDING) {
                                                direction = CKEDITOR.SELECTION_BOTTOM_TO_TOP;
                                            }
                                        }
                            
                                        return direction;
                                    }
                                };
                            
                                CKEDITOR.plugins.add(
                                    'ae_selectionregion', {
                                        /**
                                         * Initializer lifecycle implementation for the SelectionRegion plugin.
                                         *
                                         * @method init
                                         * @protected
                                         * @param {Object} editor The current CKEditor instance.
                                         */
                                        init: function(editor) {
                                            var attr,
                                                hasOwnProperty;
                            
                                            hasOwnProperty = Object.prototype.hasOwnProperty;
                            
                                            for (attr in SelectionRegion.prototype) {
                                                if (hasOwnProperty.call(SelectionRegion.prototype, attr) && typeof editor[attr] === 'undefined') {
                                                    editor[attr] = SelectionRegion.prototype[attr];
                                                }
                                            }
                                        }
                                    }
                                );
                            }());