From f0ddce4df179efb5ee03fa7975fd7eaaf8477fb4 Mon Sep 17 00:00:00 2001 From: Jetha Chan Date: Tue, 15 Apr 2014 11:34:21 +0800 Subject: [PATCH] MDL-44758 editor-atto: prevent table-in-table creation Prevent new table creation, displaying table context menu instead when cursor is within caption, when multiple cells are selected, when selection contains both nodes within and outside of the table, and when selection contains nodes from multiple tables. --- .../moodle-atto_table-button-debug.js | 67 ++++++++++++++----- .../moodle-atto_table-button-min.js | 6 +- .../moodle-atto_table-button.js | 67 ++++++++++++++----- .../plugins/table/yui/src/button/js/button.js | 67 ++++++++++++++----- 4 files changed, 156 insertions(+), 51 deletions(-) diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js index 9a488ac28b3..a1ef10532bc 100644 --- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js +++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js @@ -179,30 +179,27 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi * @private */ _displayTableEditor: function(e) { - var selection = this.get('host').getSelectionParentNode(), - cell; - - if (!selection) { - // We don't have a current selection at all, so show the standard dialogue. - return this._displayDialogue(e); - } - - // Check all of the table cells found in the selection. - Y.one(selection).ancestors('th, td', true).each(function(node) { - if (this.editor.contains(node)) { - cell = node; - } - }, this); - + var cell = this._getSuitableTableCell(); if (cell) { // Add the cell to the EventFacade to save duplication in when showing the menu. e.tableCell = cell; return this._showTableMenu(e); } - return this._displayDialogue(e); }, + /** + * Returns whether or not the parameter node exists within the editor. + * + * @method _stopAtContentEditableFilter + * @param {Node} node + * @private + * @return {boolean} whether or not the parameter node exists within the editor. + */ + _stopAtContentEditableFilter: function(node) { + this.editor.contains(node); + }, + /** * Return the edit table dialogue content, attaching any required * events. @@ -247,6 +244,44 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi return this._content; }, + /** + * Given the current selection, return a table cell suitable for table editing + * purposes, i.e. the first table cell selected, or the first cell in the table + * that the selection exists in, or null if not within a table. + * + * @method _getSuitableTableCell + * @private + * @return {Node} suitable target cell, or null if not within a table + */ + _getSuitableTableCell: function() { + var targetcell = null, + host = this.get('host'); + + host.getSelectedNodes().some(function (node) { + if (node.ancestor('td, th, caption', true, this._stopAtContentEditableFilter)) { + targetcell = node; + + var caption = node.ancestor('caption', true, this._stopAtContentEditableFilter); + if (caption) { + var table = caption.get('parentNode'); + if (table) { + targetcell = table.one('td, th'); + } + } + + // Once we've found a cell to target, we shouldn't need to keep looking. + return true; + } + }); + + if (targetcell) { + var selection = host.getSelectionFromNode(targetcell); + host.setSelection(selection); + } + + return targetcell; + }, + /** * Change a node from one type to another, copying all attributes and children. * diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js index b32f78807ae..d2ffa29d684 100644 --- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js +++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js @@ -1,3 +1,3 @@ -YUI.add("moodle-atto_table-button",function(e,t){var n="atto_table",r='




',i='






',s={CAPTION:"caption",HEADERS:"headers",ROWS:"rows",COLUMNS:"columns",SUBMIT:"submit",FORM:"atto_form"},o={CAPTION:"."+s.CAPTION,HEADERS:"."+s.HEADERS,ROWS:"."+s.ROWS,COLUMNS:"."+s.COLUMNS,SUBMIT:"."+s.SUBMIT,FORM:".atto_form"};e.namespace("M.atto_table").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_contextMenu:null,_lastTarget:null,_menuOptions:null,initializer:function(){this.addButton({icon:"e/table",callback:this._displayTableEditor,tags:"table"}),e.UA.gecko&&(document.execCommand("enableInlineTableEditing",!1,!1),document.execCommand("enableObjectResizing",!1,!1))},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection();if(this._currentSelection!==!1&&!this._currentSelection.collapsed){var e=this.getDialogue({headerContent:M.util.get_string("createtable",n),focusAfterHide:!0});e.set("bodyContent",this._getDialogueContent()).show()}},_displayTableEditor:function(t){var n=this.get("host").getSelectionParentNode(),r;return n?(e.one(n).ancestors("th, td",!0).each(function(e){this.editor.contains(e)&&(r=e)},this),r?(t.tableCell=r,this._showTableMenu(t)):this._displayDialogue(t)):this._displayDialogue(t)},_getEditDialogueContent:function(){var t=e.Handlebars.compile(r);return this._content=e.Node.create(t({CSS:s,elementid:this.get("host").get("elementid"),component:n})),this._content.one(".submit").on("click",this._updateTable,this),this._content},_getDialogueContent:function(){var t=e.Handlebars.compile(i);return this._content=e.Node.create(t({CSS:s,elementid:this.get("host").get("elementid"),component:n})),this._content.one(".submit").on("click",this._setTable,this),this._content},_changeNodeType:function(t,n){var r=e.Node.create("<"+n+">");return r.setAttrs(t.getAttrs()),t.get("childNodes").each(function(e){r.append(e.remove())}),t.replace(r),r},_updateTable:function(t){var n,r,i,s;t.preventDefault(),this.getDialogue({focusAfterHide:null}).hide(),n=t.currentTarget.ancestor(o.FORM).one(o.CAPTION),r=t.currentTarget.ancestor(o.FORM).one(o.HEADERS),i=this._lastTarget.ancestor("table"),s=i.one("caption"),s||(s=e.Node.create(""+l,u+=""+e.Escape.html(n.get("value"))+""+l,a=0;if(s.get("value")==="columns"||s.get("value")==="both"){a=1,u+=""+l+""+l;for(f=0;f'+l;u+=""+l+""+l}u+=""+l;for(;a"+l;for(f=0;f"+l:u+=''+l;u+=""+l}u+=""+l,u+=""+l+"
",this.get("host").insertContentAtFocusPoint(u),this.markUpdated()},_findColumnCells:function(){var t=this._getColumnIndex(this -._lastTarget),n=this._lastTarget.ancestor("table").all("tr"),r=new e.NodeList,i=new e.NodeList,s=new e.NodeList;return n.each(function(e){var n=e.all("td, th"),o=n.item(t),u=n.item(t-1),a=n.item(t+1);r.push(o),u&&i.push(u),a&&s.push(a)}),{current:r,prev:i,next:s}},_hideInvalidEntries:function(e){var t=this._lastTarget.ancestor("table"),n=this._lastTarget.ancestor("tr"),r=t.all("tr"),i=r.indexOf(n),s=r.item(i-1),o=s?s.one("td"):null;!n||!o?e.one('[data-change="moverowup"]').hide():e.one('[data-change="moverowup"]').show();var u=r.item(i+1),a=n?n.one("td"):!1;!n||!u||!a?e.one('[data-change="moverowdown"]').hide():e.one('[data-change="moverowdown"]').show();var f=this._findColumnCells();f.prev.filter("td").size()>0?e.one('[data-change="movecolumnleft"]').show():e.one('[data-change="movecolumnleft"]').hide();var l=f.current.filter("td").size()>0;f.next.size()>0&&l?e.one('[data-change="movecolumnright"]').show():e.one('[data-change="movecolumnright"]').hide(),f.current.filter("td").size()>0?e.one('[data-change="deletecolumn"]').show():e.one('[data-change="deletecolumn"]').hide(),!n||!n.one("td")?e.one('[data-change="deleterow"]').hide():e.one('[data-change="deleterow"]').show()},_showTableMenu:function(t){t.preventDefault();var r;this._contextMenu||(this._menuOptions=[{text:M.util.get_string("addcolumnafter",n),data:{change:"addcolumnafter"}},{text:M.util.get_string("addrowafter",n),data:{change:"addrowafter"}},{text:M.util.get_string("moverowup",n),data:{change:"moverowup"}},{text:M.util.get_string("moverowdown",n),data:{change:"moverowdown"}},{text:M.util.get_string("movecolumnleft",n),data:{change:"movecolumnleft"}},{text:M.util.get_string("movecolumnright",n),data:{change:"movecolumnright"}},{text:M.util.get_string("deleterow",n),data:{change:"deleterow"}},{text:M.util.get_string("deletecolumn",n),data:{change:"deletecolumn"}},{text:M.util.get_string("edittable",n),data:{change:"edittable"}}],this._contextMenu=new e.M.editor_atto.Menu({items:this._menuOptions}),r=this._contextMenu.get("boundingBox"),r.delegate("click",this._handleTableChange,"a",this)),r=this._contextMenu.get("boundingBox"),this._lastTarget=t.tableCell.ancestor(".editor_atto_content td, .editor_atto_content th",!0),this._hideInvalidEntries(r),e.Array.each(this.get("host").openMenus,function(e){e.set("focusAfterHide",null)});var i=this.buttons[this.name];this.get("host")._setTabFocus(i),this._contextMenu.show(),this._contextMenu.align(this.buttons.table,[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL]),this._contextMenu.set("focusAfterHide",i),r.one("a")&&r.one("a").focus(),this.get("host").openMenus=[this._contextMenu]},_handleTableChange:function(e){e.preventDefault(),this._contextMenu.set("focusAfterHide",this.get("host").editor),this._contextMenu.hide(e);switch(e.target.getData("change")){case"addcolumnafter":this._addColumnAfter();break;case"addrowafter":this._addRowAfter();break;case"deleterow":this._deleteRow();break;case"deletecolumn":this._deleteColumn();break;case"edittable":this._editTable();break;case"moverowdown":this._moveRowDown();break;case"moverowup":this._moveRowUp();break;case"movecolumnleft":this._moveColumnLeft();break;case"movecolumnright":this._moveColumnRight()}},_getRowIndex:function(e){var t=e.ancestor("table"),n=e.ancestor("tr");if(!t||!n)return;var r=t.all("tr");return r.indexOf(n)},_getColumnIndex:function(e){var t=e.ancestor("tr");if(!t)return;var n=t.all("td, th");return n.indexOf(e)},_deleteRow:function(){var e=this._lastTarget.ancestor("tr");e&&e.one("td")&&e.remove(!0),this.markUpdated()},_moveRowUp:function(){var e=this._lastTarget.ancestor("tr"),t=e.previous("tr");if(!e||!t)return;e.swap(t),this.markUpdated()},_moveColumnLeft:function(){var e=this._findColumnCells();if(e.current.size()>0&&e.prev.size()>0&&e.current.size()===e.prev.size()){var t=0;for(t=0;t "),1)},_removeCaption:function(){var e=this._lastTarget.ancestor("table"),t=e.one("caption");t&&t.remove(!0)},_moveColumnRight:function(){var e=this._findColumnCells();if(e.next.size()>0&&e.current.size()===e.next.size()&&e.current.filter("td").size()>0){var t=0;for(t=0;t");t.replace(n),t=n}t.setHTML(" ")}),n.insert(newrow,t),this.markUpdated()},_addColumnAfter:function(){var t=this._findColumnCells(),n=!0,r=t.next;t.next.size()<=0&&(n=!1,r=t.current),e.each(r,function(e){var t=e.cloneNode();t.setHTML(" "),n?e.get("parentNode").insert(t,e):(e.get("parentNode").insert(t,e),e.swap(t))},this),this -.markUpdated()}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","moodle-editor_atto-menu","event","event-valuechange"]}); +YUI.add("moodle-atto_table-button",function(e,t){var n="atto_table",r='




',i='






',s={CAPTION:"caption",HEADERS:"headers",ROWS:"rows",COLUMNS:"columns",SUBMIT:"submit",FORM:"atto_form"},o={CAPTION:"."+s.CAPTION,HEADERS:"."+s.HEADERS,ROWS:"."+s.ROWS,COLUMNS:"."+s.COLUMNS,SUBMIT:"."+s.SUBMIT,FORM:".atto_form"};e.namespace("M.atto_table").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_contextMenu:null,_lastTarget:null,_menuOptions:null,initializer:function(){this.addButton({icon:"e/table",callback:this._displayTableEditor,tags:"table"}),e.UA.gecko&&(document.execCommand("enableInlineTableEditing",!1,!1),document.execCommand("enableObjectResizing",!1,!1))},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection();if(this._currentSelection!==!1&&!this._currentSelection.collapsed){var e=this.getDialogue({headerContent:M.util.get_string("createtable",n),focusAfterHide:!0});e.set("bodyContent",this._getDialogueContent()).show()}},_displayTableEditor:function(e){var t=this._getSuitableTableCell();return t?(e.tableCell=t,this._showTableMenu(e)):this._displayDialogue(e)},_stopAtContentEditableFilter:function(e){this.editor.contains(e)},_getEditDialogueContent:function(){var t=e.Handlebars.compile(r);return this._content=e.Node.create(t({CSS:s,elementid:this.get("host").get("elementid"),component:n})),this._content.one(".submit").on("click",this._updateTable,this),this._content},_getDialogueContent:function(){var t=e.Handlebars.compile(i);return this._content=e.Node.create(t({CSS:s,elementid:this.get("host").get("elementid"),component:n})),this._content.one(".submit").on("click",this._setTable,this),this._content},_getSuitableTableCell:function(){var e=null,t=this.get("host");t.getSelectedNodes().some(function(t){if(t.ancestor("td, th, caption",!0,this._stopAtContentEditableFilter)){e=t;var n=t.ancestor("caption",!0,this._stopAtContentEditableFilter);if(n){var r=n.get("parentNode");r&&(e=r.one("td, th"))}return!0}});if(e){var n=t.getSelectionFromNode(e);t.setSelection(n)}return e},_changeNodeType:function(t,n){var r=e.Node.create("<"+n+">");return r.setAttrs(t.getAttrs()),t.get("childNodes").each(function(e){r.append(e.remove())}),t.replace(r),r},_updateTable:function(t){var n,r,i,s;t.preventDefault(),this.getDialogue({focusAfterHide:null}).hide(),n=t.currentTarget.ancestor(o.FORM).one(o.CAPTION),r=t.currentTarget.ancestor(o.FORM).one(o.HEADERS),i=this._lastTarget.ancestor("table"),s=i.one("caption"),s||(s=e.Node.create(""+l,u+=""+e.Escape.html(n.get("value"))+""+l,a=0;if(s.get("value")==="columns"||s.get("value")==="both"){a=1,u+=""+l+""+l;for(f=0;f'+l;u+=""+l+""+l}u+=""+l;for(;a"+l;for(f=0;f"+l:u+=''+l;u+=""+l}u+=""+l,u+=""+l+"
",this.get("host").insertContentAtFocusPoint(u),this.markUpdated()},_findColumnCells:function(){var t=this._getColumnIndex(this._lastTarget),n=this._lastTarget.ancestor("table").all("tr"),r=new e.NodeList,i=new e.NodeList,s=new e.NodeList;return n.each(function(e){var n=e.all("td, th"),o=n.item(t),u=n.item(t-1),a=n.item(t+1);r.push(o),u&&i.push(u),a&&s.push(a)}),{current:r,prev:i,next:s}},_hideInvalidEntries:function(e){var t=this._lastTarget.ancestor("table"),n=this._lastTarget.ancestor("tr"),r=t.all("tr"),i=r.indexOf(n),s=r.item(i-1),o=s?s.one("td"):null;!n||!o?e.one('[data-change="moverowup"]').hide():e.one('[data-change="moverowup"]').show();var u=r.item(i+1),a=n?n.one("td"):!1;!n||!u||!a?e.one('[data-change="moverowdown"]').hide():e.one('[data-change="moverowdown"]').show();var f=this._findColumnCells();f.prev.filter("td").size()>0?e.one('[data-change="movecolumnleft"]').show():e.one('[data-change="movecolumnleft"]').hide();var l=f.current.filter("td").size()>0;f.next.size()>0&&l?e.one('[data-change="movecolumnright"]').show():e.one('[data-change="movecolumnright"]').hide(),f.current.filter("td").size()>0?e.one('[data-change="deletecolumn"]').show():e.one('[data-change="deletecolumn"]').hide(),!n||!n.one("td")?e.one('[data-change="deleterow"]').hide():e.one('[data-change="deleterow"]').show()},_showTableMenu:function(t){t.preventDefault();var r;this._contextMenu||(this._menuOptions=[{text:M.util.get_string("addcolumnafter",n),data:{change:"addcolumnafter"}},{text:M.util.get_string("addrowafter",n),data:{change:"addrowafter"}},{text:M.util.get_string("moverowup",n),data:{change:"moverowup"}},{text:M.util.get_string("moverowdown",n),data:{change:"moverowdown"}},{text:M.util.get_string("movecolumnleft",n),data:{change:"movecolumnleft"}},{text:M.util.get_string("movecolumnright",n),data:{change:"movecolumnright"}},{text:M.util.get_string("deleterow",n),data:{change:"deleterow"}},{text:M.util.get_string("deletecolumn",n),data:{change:"deletecolumn"}},{text:M.util.get_string("edittable",n),data:{change:"edittable"}}],this._contextMenu=new e.M.editor_atto.Menu({items:this._menuOptions}),r=this._contextMenu.get("boundingBox"),r.delegate("click",this._handleTableChange,"a",this)),r=this._contextMenu.get("boundingBox"),this._lastTarget=t.tableCell.ancestor(".editor_atto_content td, .editor_atto_content th",!0),this._hideInvalidEntries(r),e.Array.each(this.get("host").openMenus,function(e){e.set("focusAfterHide",null)});var i=this.buttons[this.name];this.get("host")._setTabFocus(i),this._contextMenu.show(),this._contextMenu.align(this.buttons.table,[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL]),this._contextMenu.set("focusAfterHide",i),r.one("a")&&r.one("a").focus(),this.get("host").openMenus=[this._contextMenu]},_handleTableChange:function(e){e.preventDefault(),this._contextMenu.set("focusAfterHide",this.get("host").editor),this._contextMenu.hide(e);switch(e.target.getData("change")){case"addcolumnafter":this._addColumnAfter();break;case"addrowafter":this._addRowAfter();break;case"deleterow":this._deleteRow();break;case"deletecolumn":this._deleteColumn();break;case"edittable":this._editTable();break;case"moverowdown":this._moveRowDown();break;case"moverowup":this._moveRowUp();break;case"movecolumnleft":this._moveColumnLeft();break;case"movecolumnright":this._moveColumnRight()}},_getRowIndex:function(e){var t=e.ancestor("table"),n=e.ancestor("tr");if(!t||!n)return;var r=t.all("tr");return r.indexOf(n)},_getColumnIndex:function(e){var t=e.ancestor("tr");if(!t)return;var n=t.all("td, th");return n.indexOf(e)},_deleteRow:function(){var e=this._lastTarget.ancestor("tr");e&&e.one("td")&&e.remove(!0),this.markUpdated()},_moveRowUp:function(){var e=this._lastTarget.ancestor("tr"),t=e.previous("tr");if(!e||!t)return;e.swap(t),this.markUpdated()},_moveColumnLeft:function(){var e=this._findColumnCells();if(e.current.size()>0&&e.prev.size()>0&&e.current.size()===e.prev.size()){var t=0;for(t=0;t "),1)},_removeCaption:function(){var e=this._lastTarget.ancestor("table"),t=e.one("caption");t&&t.remove(!0)},_moveColumnRight:function(){var e=this._findColumnCells();if(e.next.size()>0&&e.current.size()===e.next.size()&&e.current.filter("td").size()>0){var t=0;for(t=0;t");t.replace(n),t=n}t.setHTML(" " +)}),n.insert(newrow,t),this.markUpdated()},_addColumnAfter:function(){var t=this._findColumnCells(),n=!0,r=t.next;t.next.size()<=0&&(n=!1,r=t.current),e.each(r,function(e){var t=e.cloneNode();t.setHTML(" "),n?e.get("parentNode").insert(t,e):(e.get("parentNode").insert(t,e),e.swap(t))},this),this.markUpdated()}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","moodle-editor_atto-menu","event","event-valuechange"]}); diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js index 9a488ac28b3..a1ef10532bc 100644 --- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js +++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js @@ -179,30 +179,27 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi * @private */ _displayTableEditor: function(e) { - var selection = this.get('host').getSelectionParentNode(), - cell; - - if (!selection) { - // We don't have a current selection at all, so show the standard dialogue. - return this._displayDialogue(e); - } - - // Check all of the table cells found in the selection. - Y.one(selection).ancestors('th, td', true).each(function(node) { - if (this.editor.contains(node)) { - cell = node; - } - }, this); - + var cell = this._getSuitableTableCell(); if (cell) { // Add the cell to the EventFacade to save duplication in when showing the menu. e.tableCell = cell; return this._showTableMenu(e); } - return this._displayDialogue(e); }, + /** + * Returns whether or not the parameter node exists within the editor. + * + * @method _stopAtContentEditableFilter + * @param {Node} node + * @private + * @return {boolean} whether or not the parameter node exists within the editor. + */ + _stopAtContentEditableFilter: function(node) { + this.editor.contains(node); + }, + /** * Return the edit table dialogue content, attaching any required * events. @@ -247,6 +244,44 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi return this._content; }, + /** + * Given the current selection, return a table cell suitable for table editing + * purposes, i.e. the first table cell selected, or the first cell in the table + * that the selection exists in, or null if not within a table. + * + * @method _getSuitableTableCell + * @private + * @return {Node} suitable target cell, or null if not within a table + */ + _getSuitableTableCell: function() { + var targetcell = null, + host = this.get('host'); + + host.getSelectedNodes().some(function (node) { + if (node.ancestor('td, th, caption', true, this._stopAtContentEditableFilter)) { + targetcell = node; + + var caption = node.ancestor('caption', true, this._stopAtContentEditableFilter); + if (caption) { + var table = caption.get('parentNode'); + if (table) { + targetcell = table.one('td, th'); + } + } + + // Once we've found a cell to target, we shouldn't need to keep looking. + return true; + } + }); + + if (targetcell) { + var selection = host.getSelectionFromNode(targetcell); + host.setSelection(selection); + } + + return targetcell; + }, + /** * Change a node from one type to another, copying all attributes and children. * diff --git a/lib/editor/atto/plugins/table/yui/src/button/js/button.js b/lib/editor/atto/plugins/table/yui/src/button/js/button.js index a7f81e4772f..44f1e9f5330 100644 --- a/lib/editor/atto/plugins/table/yui/src/button/js/button.js +++ b/lib/editor/atto/plugins/table/yui/src/button/js/button.js @@ -177,30 +177,27 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi * @private */ _displayTableEditor: function(e) { - var selection = this.get('host').getSelectionParentNode(), - cell; - - if (!selection) { - // We don't have a current selection at all, so show the standard dialogue. - return this._displayDialogue(e); - } - - // Check all of the table cells found in the selection. - Y.one(selection).ancestors('th, td', true).each(function(node) { - if (this.editor.contains(node)) { - cell = node; - } - }, this); - + var cell = this._getSuitableTableCell(); if (cell) { // Add the cell to the EventFacade to save duplication in when showing the menu. e.tableCell = cell; return this._showTableMenu(e); } - return this._displayDialogue(e); }, + /** + * Returns whether or not the parameter node exists within the editor. + * + * @method _stopAtContentEditableFilter + * @param {Node} node + * @private + * @return {boolean} whether or not the parameter node exists within the editor. + */ + _stopAtContentEditableFilter: function(node) { + this.editor.contains(node); + }, + /** * Return the edit table dialogue content, attaching any required * events. @@ -245,6 +242,44 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi return this._content; }, + /** + * Given the current selection, return a table cell suitable for table editing + * purposes, i.e. the first table cell selected, or the first cell in the table + * that the selection exists in, or null if not within a table. + * + * @method _getSuitableTableCell + * @private + * @return {Node} suitable target cell, or null if not within a table + */ + _getSuitableTableCell: function() { + var targetcell = null, + host = this.get('host'); + + host.getSelectedNodes().some(function (node) { + if (node.ancestor('td, th, caption', true, this._stopAtContentEditableFilter)) { + targetcell = node; + + var caption = node.ancestor('caption', true, this._stopAtContentEditableFilter); + if (caption) { + var table = caption.get('parentNode'); + if (table) { + targetcell = table.one('td, th'); + } + } + + // Once we've found a cell to target, we shouldn't need to keep looking. + return true; + } + }); + + if (targetcell) { + var selection = host.getSelectionFromNode(targetcell); + host.setSelection(selection); + } + + return targetcell; + }, + /** * Change a node from one type to another, copying all attributes and children. *