Show:
                            (function() {
                                'use strict';
                            
                                var IE_NON_DIRECTLY_EDITABLE_ELEMENT = {
                                    'table': 1,
                                    'col': 1,
                                    'colgroup': 1,
                                    'tbody': 1,
                                    'td': 1,
                                    'tfoot': 1,
                                    'th': 1,
                                    'thead': 1,
                                    'tr': 1
                                };
                            
                                /**
                                 * Table class utility. Provides methods for create, delete and update tables.
                                 *
                                 * @class CKEDITOR.Table
                                 * @constructor
                                 * @param {Object} editor The CKEditor instance.
                                 */
                            
                                function Table(editor) {
                                    this._editor = editor;
                                }
                            
                                Table.HEADING_BOTH = 'Both';
                                Table.HEADING_COL = 'Column';
                                Table.HEADING_NONE = 'None';
                                Table.HEADING_ROW = 'Row';
                            
                                Table.prototype = {
                                    constructor: Table,
                            
                                    /**
                                     * Creates a table.
                                     *
                                     * @method create
                                     * @param {Object} config Table configuration object
                                     * @return {Object} The created table
                                     */
                                    create: function(config) {
                                        var editor = this._editor;
                                        var table = this._createElement('table');
                            
                                        config = config || {};
                            
                                        // Generate the rows and cols.
                                        var tbody = table.append(this._createElement('tbody'));
                                        var rows = config.rows || 1;
                                        var cols = config.cols || 1;
                            
                                        for (var i = 0; i < rows; i++) {
                                            var row = tbody.append(this._createElement('tr'));
                                            for (var j = 0; j < cols; j++) {
                                                var cell = row.append(this._createElement('td'));
                            
                                                cell.appendBogus();
                                            }
                                        }
                            
                                        this.setAttributes(table, config.attrs);
                                        this.setHeading(table, config.heading);
                            
                                        // Insert the table element if we're creating one.
                                        editor.insertElement(table);
                            
                                        var firstCell = new CKEDITOR.dom.element(table.$.rows[0].cells[0]);
                                        var range = editor.createRange();
                                        range.moveToPosition(firstCell, CKEDITOR.POSITION_AFTER_START);
                                        range.select();
                            
                                        return table;
                                    },
                            
                                    /**
                                     * Retrieves a table from the current selection.
                                     *
                                     * @method getFromSelection
                                     * @return {CKEDITOR.dom.element} The retrieved table or null if not found.
                                     */
                                    getFromSelection: function() {
                                        var table;
                                        var selection = this._editor.getSelection();
                                        var selected = selection.getSelectedElement();
                            
                                        if (selected && selected.is('table')) {
                                            table = selected;
                                        } else {
                                            var ranges = selection.getRanges();
                            
                                            if (ranges.length > 0) {
                                                // Webkit could report the following range on cell selection (#4948):
                                                // <table><tr><td>[&nbsp;</td></tr></table>]
                            
                                                /* istanbul ignore else */
                                                if (CKEDITOR.env.webkit) {
                                                    ranges[0].shrink(CKEDITOR.NODE_ELEMENT);
                                                }
                            
                                                table = this._editor.elementPath(ranges[0].getCommonAncestor(true)).contains('table', 1);
                                            }
                                        }
                            
                                        return table;
                                    },
                            
                                    /**
                                     * Checks if a given table can be considered as editable. This method
                                     * workarounds a limitation of IE where for some elements (like table),
                                     * `isContentEditable` returns always false. This is because IE does not support
                                     * `contenteditable` on such elements. However, despite such elements
                                     * cannot be set as content editable directly, a content editable SPAN,
                                     * or DIV element can be placed inside the individual table cells.
                                     * See https://msdn.microsoft.com/en-us/library/ms537837%28v=VS.85%29.aspx
                                     *
                                     * @method isEditable
                                     * @param {CKEDITOR.dom.element} el The table element to test if editable
                                     * @return {Boolean}
                                     */
                                    isEditable: function (el) {
                                        if (!CKEDITOR.env.ie || !el.is(IE_NON_DIRECTLY_EDITABLE_ELEMENT)) {
                                            return !el.isReadOnly();
                                        }
                            
                                        if (el.hasAttribute('contenteditable')) {
                                            return (el.getAttribute('contenteditable') !== 'false');
                                        }
                            
                                        return this.isEditable(el.getParent());
                                    },
                            
                                    /**
                                     * Returns which heading style is set for the given table.
                                     *
                                     * @method getHeading
                                     * @param {CKEDITOR.dom.element} table The table to gather the heading from. If null, it will be retrieved from the current selection.
                                     * @return {String} The heading of the table. Expected values are `CKEDITOR.Table.NONE`, `CKEDITOR.Table.ROW`, `CKEDITOR.Table.COL` and `CKEDITOR.Table.BOTH`.
                                     */
                                    getHeading: function(table) {
                                        table = table || this.getFromSelection();
                            
                                        if (!table) {
                                            return null;
                                        }
                            
                                        var rowHeadingSettings = table.$.tHead !== null;
                            
                                        var colHeadingSettings = true;
                            
                                        // Check if all of the first cells in every row are TH
                                        for (var row = 0; row < table.$.rows.length; row++) {
                                            // If just one cell isn't a TH then it isn't a header column
                                            var cell = table.$.rows[row].cells[0];
                            
                                            if (cell && cell.nodeName.toLowerCase() !== 'th') {
                                                colHeadingSettings = false;
                                                break;
                                            }
                                        }
                            
                                        var headingSettings = Table.HEADING_NONE;
                            
                                        if (rowHeadingSettings) {
                                            headingSettings = Table.HEADING_ROW;
                                        }
                            
                                        if (colHeadingSettings) {
                                            headingSettings = (headingSettings === Table.HEADING_ROW ? Table.HEADING_BOTH : Table.HEADING_COL);
                                        }
                            
                                        return headingSettings;
                                    },
                            
                                    /**
                                     * Removes a table from the editor.
                                     *
                                     * @method remove
                                     * @param {CKEDITOR.dom.element} table The table element which table style should be removed.
                                     */
                                    remove: function(table) {
                                        var editor = this._editor;
                            
                                        if (table) {
                                            table.remove();
                                        } else {
                                            table = editor.elementPath().contains('table', 1);
                            
                                            if (table) {
                                                // If the table's parent has only one child remove it as well (unless it's a table cell, or the editable element) (#5416, #6289, #12110)
                                                var parent = table.getParent();
                                                var editable = editor.editable();
                            
                                                if (parent.getChildCount() === 1 && !parent.is('td', 'th') && !parent.equals(editable)) {
                                                    table = parent;
                                                }
                            
                                                var range = editor.createRange();
                                                range.moveToPosition(table, CKEDITOR.POSITION_BEFORE_START);
                                                table.remove();
                                                range.select();
                                            }
                                        }
                                    },
                            
                                    /**
                                     * Assigns provided attributes to a table.
                                     *
                                     * @method setAttributes
                                     * @param {Object} table The table to which the attributes should be assigned
                                     * @param {Object} attrs The attributes which have to be assigned to the table
                                     */
                                    setAttributes: function(table, attrs) {
                                        if (attrs) {
                                            Object.keys(attrs).forEach(function(attr) {
                                                table.setAttribute(attr, attrs[attr]);
                                            });
                                        }
                                    },
                            
                                    /**
                                     * Sets the appropriate table heading style to a table.
                                     *
                                     * @method setHeading
                                     * @param {CKEDITOR.dom.element} table The table element to which the heading should be set. If null, it will be retrieved from the current selection.
                                     * @param {String} heading The table heading to be set. Accepted values are: `CKEDITOR.Table.NONE`, `CKEDITOR.Table.ROW`, `CKEDITOR.Table.COL` and `CKEDITOR.Table.BOTH`.
                                     */
                                    setHeading: function(table, heading) {
                                        table = table || this.getFromSelection();
                            
                                        var i, newCell;
                                        var tableHead;
                                        var tableBody = table.getElementsByTag('tbody').getItem(0);
                            
                                        var tableHeading = this.getHeading(table);
                                        var hadColHeading = (tableHeading === Table.HEADING_COL || tableHeading === Table.HEADING_BOTH);
                            
                                        var needColHeading = heading === Table.HEADING_COL || heading === Table.HEADING_BOTH;
                                        var needRowHeading = heading === Table.HEADING_ROW || heading === Table.HEADING_BOTH;
                            
                                        // If we need row heading and don't have a <thead> element yet, move the
                                        // first row of the table to the head and convert the nodes to <th> ones.
                                        if (!table.$.tHead && needRowHeading) {
                                            var tableFirstRow = tableBody.getElementsByTag('tr').getItem(0);
                                            var tableFirstRowChildCount = tableFirstRow.getChildCount();
                            
                                            // Change TD to TH:
                                            for (i = 0; i < tableFirstRowChildCount; i++) {
                                                var cell = tableFirstRow.getChild(i);
                            
                                                // Skip bookmark nodes. (#6155)
                                                if (cell.type === CKEDITOR.NODE_ELEMENT && !cell.data('cke-bookmark')) {
                                                    cell.renameNode('th');
                                                    cell.setAttribute('scope', 'col');
                                                }
                                            }
                            
                                            tableHead = this._createElement(table.$.createTHead());
                                            tableHead.append(tableFirstRow.remove());
                                        }
                            
                                        // If we don't need row heading and we have a <thead> element, move the
                                        // row out of there and into the <tbody> element.
                                        if (table.$.tHead !== null && !needRowHeading) {
                                            // Move the row out of the THead and put it in the TBody:
                                            tableHead = this._createElement(table.$.tHead);
                            
                                            var previousFirstRow = tableBody.getFirst();
                            
                                            while (tableHead.getChildCount() > 0) {
                                                var newFirstRow = tableHead.getFirst();
                                                var newFirstRowChildCount = newFirstRow.getChildCount();
                            
                                                for (i = 0; i < newFirstRowChildCount; i++) {
                                                    newCell = newFirstRow.getChild(i);
                            
                                                    if (newCell.type === CKEDITOR.NODE_ELEMENT) {
                                                        newCell.renameNode('td');
                                                        newCell.removeAttribute('scope');
                                                    }
                                                }
                            
                                                newFirstRow.insertBefore(previousFirstRow);
                                            }
                            
                                            tableHead.remove();
                                        }
                            
                                        tableHeading = this.getHeading(table);
                                        var hasColHeading = (tableHeading === Table.HEADING_COL || tableHeading === Table.HEADING_BOTH);
                            
                                        // If we need column heading and the table doesn't have it, convert every first cell in
                                        // every row into a `<th scope="row">` element.
                                        if (!hasColHeading && needColHeading) {
                                            for (i = 0; i < table.$.rows.length; i++) {
                                                if (table.$.rows[i].cells[0].nodeName.toLowerCase() !== 'th') {
                                                    newCell = new CKEDITOR.dom.element(table.$.rows[i].cells[0]);
                                                    newCell.renameNode('th');
                                                    newCell.setAttribute('scope', 'row');
                                                }
                                            }
                                        }
                            
                                        // If we don't need column heading but the table has it, convert every first cell in every
                                        // row back into a `<td>` element.
                                        if (hadColHeading && !needColHeading) {
                                            for (i = 0; i < table.$.rows.length; i++) {
                                                var row = new CKEDITOR.dom.element(table.$.rows[i]);
                            
                                                if (row.getParent().getName() === 'tbody') {
                                                    newCell = new CKEDITOR.dom.element(row.$.cells[0]);
                                                    newCell.renameNode('td');
                                                    newCell.removeAttribute('scope');
                                                }
                                            }
                                        }
                                    },
                            
                                    /**
                                     * Creates a new CKEDITOR.dom.element using the passed tag name.
                                     *
                                     * @protected
                                     * @method _createElement
                                     * @param {String} name The tag name from which an element should be created
                                     * @return {CKEDITOR.dom.element} Instance of CKEDITOR DOM element class
                                     */
                                    _createElement: function(name) {
                                        return new CKEDITOR.dom.element(name, this._editor.document);
                                    }
                                };
                            
                                CKEDITOR.on('instanceReady', function(event) {
                                    var headingCommands = [Table.HEADING_NONE, Table.HEADING_ROW, Table.HEADING_COL, Table.HEADING_BOTH];
                            
                                    var tableUtils = new Table(event.editor);
                            
                                    headingCommands.forEach(function(heading) {
                                        event.editor.addCommand('tableHeading' + heading, {
                                            exec: function(editor) {
                                                tableUtils.setHeading(null, heading);
                                            }
                                        });
                                    });
                                });
                            
                                CKEDITOR.Table = CKEDITOR.Table || Table;
                            }());