Ticket #2980: 2980.patch
File 2980.patch, 42.6 KB (added by , 15 years ago) |
---|
-
_source/lang/en.js
235 235 mergeRight : 'Merge Right', 236 236 mergeDown : 'Merge Down', 237 237 splitHorizontal : 'Split Cell Horizontally', 238 splitVertical : 'Split Cell Vertically' 238 splitVertical : 'Split Cell Vertically', 239 title : 'Cell Properties', 240 cellType : 'Cell Type', 241 rowSpan : 'Rows Span', 242 colSpan : 'Columns Span', 243 wordWrap : 'Word Wrap', 244 hAlign : 'Horizontal Alignment', 245 vAlign : 'Vertical Alignment', 246 alignTop : 'Top', 247 alignMiddle : 'Middle', 248 alignBottom : 'Bottom', 249 alignBaseline : 'Baseline', 250 bgColor : 'Background Color', 251 borderColor : 'Border Color', 252 data : 'Data', 253 header : 'Header', 254 yes : 'Yes', 255 no : 'No', 256 invalidWidth : 'Cell width must be a number.', 257 invalidHeight : 'Cell height must be a number.', 258 invalidRowSpan : 'Rows span must be a whole number.', 259 invalidColSpan : 'Columns span must be a whole number.' 239 260 }, 240 261 241 262 row : -
_source/plugins/menu/plugin.js
320 320 CKEDITOR.config.menu_groups = 321 321 'clipboard,' + 322 322 'form,' + 323 /*'tablecell,tablerow,tablecolumn,*/'table,'+323 'tablecell,tablecellproperties,tablerow,tablecolumn,table,'+ 324 324 'anchor,link,image,flash,' + 325 325 'checkbox,radio,textfield,hiddenfield,imagebutton,button,select,textarea'; -
_source/plugins/table/dialogs/table.js
16 16 data.info[id] = this.getValue(); 17 17 }; 18 18 19 // Copy all the attributes from one node to the other, kinda like a clone20 // skipAttributes is an object with the attributes that must NOT be copied21 function copyAttributes( source, dest, skipAttributes )22 {23 var attributes = source.$.attributes;24 25 for ( var n = 0 ; n < attributes.length ; n++ )26 {27 var attribute = attributes[n];28 29 if ( attribute.specified )30 {31 var attrName = attribute.nodeName;32 // We can set the type only once, so do it with the proper value, not copying it.33 if ( attrName in skipAttributes )34 continue;35 36 var attrValue = source.getAttribute( attrName );37 if ( attrValue === null )38 attrValue = attribute.nodeValue;39 40 dest.setAttribute( attrName, attrValue );41 }42 }43 // The style:44 if ( source.$.style.cssText !== '' )45 dest.$.style.cssText = source.$.style.cssText;46 }47 48 /**49 * Replaces a tag with another one, keeping its contents:50 * for example TD --> TH, and TH --> TD.51 * input: the original node, and the new tag name52 * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-renameNode53 */54 function renameNode( node , newTag )55 {56 // Only rename element nodes.57 if ( node.type != CKEDITOR.NODE_ELEMENT )58 return null;59 60 // If it's already correct exit here.61 if ( node.getName() == newTag )62 return node;63 64 var doc = node.getDocument();65 66 // Create the new node67 var newNode = new CKEDITOR.dom.element( newTag, doc );68 69 // Copy all attributes70 copyAttributes( node, newNode, {} );71 72 // Move children to the new node73 node.moveChildren( newNode );74 75 // Finally replace the node and return the new one76 node.$.parentNode.replaceChild( newNode.$, node.$ );77 78 return newNode;79 }80 81 19 function tableDialog( editor, command ) 82 20 { 83 21 var makeElement = function( name ){ return new CKEDITOR.dom.element( name, editor.document ); }; … … 174 112 // Change TD to TH: 175 113 for ( i = 0 ; i < theRow.getChildCount() ; i++ ) 176 114 { 177 var th = renameNode( theRow.getChild( i ), 'th' ); 178 if ( th ) 179 th.setAttribute( 'scope', 'col' ); 115 var th = theRow.getChild( i ); 116 if ( th.type == CKEDITOR.NODE_ELEMENT ) 117 { 118 th.renameNode( 'th' ); 119 if ( i == 0 ) 120 th.setAttribute( 'scope', 'col' ); 121 } 180 122 } 181 123 thead.append( theRow.remove() ); 182 124 } … … 193 135 theRow = thead.getFirst(); 194 136 for ( i = 0; i < theRow.getChildCount() ; i++ ) 195 137 { 196 var newCell = renameNode( theRow.getChild( i ), 'td' ); 197 if ( newCell ) 138 var newCell = theRow.getChild( i ); 139 if ( newCell.type == CKEDITOR.NODE_ELEMENT ) 140 { 141 newCell.renameNode( 'td' ); 198 142 newCell.removeAttribute( 'scope' ); 143 } 199 144 } 200 145 theRow.insertBefore( previousFirstRow ); 201 146 } … … 207 152 { 208 153 for( row = 0 ; row < table.$.rows.length ; row++ ) 209 154 { 210 newCell = renameNode( new CKEDITOR.dom.element( table.$.rows[row].cells[0] ), 'th');211 if ( newCell )212 155 var newCell = new CKEDITOR.dom.element( table.$.rows[ row ].cells[ 0 ] ); 156 newCell.renameNode( 'th' ); 157 newCell.setAttribute( 'scope', 'col' ); 213 158 } 214 159 } 215 160 … … 221 166 row = new CKEDITOR.dom.element( table.$.rows[i] ); 222 167 if ( row.getParent().getName() == 'tbody' ) 223 168 { 224 newCell = renameNode( new CKEDITOR.dom.element( row.$.cells[0] ), 'td');225 if ( newCell )226 169 var newCell = new CKEDITOR.dom.element( row.$.cells[0] ); 170 newCell.renameNode( 'td'); 171 newCell.removeAttribute( 'scope' ); 227 172 } 228 173 } 229 174 } -
_source/plugins/table/plugin.js
3 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 4 */ 5 5 6 CKEDITOR.plugins.add( 'table', 6 (function() 7 7 { 8 init : function( editor )8 CKEDITOR.plugins.add( 'table', 9 9 { 10 var table = CKEDITOR.plugins.table, 11 lang = editor.lang.table; 10 init : function( editor ) 11 { 12 var table = CKEDITOR.plugins.table, 13 lang = editor.lang.table; 12 14 13 editor.addCommand( 'table', new CKEDITOR.dialogCommand( 'table' ) ); 14 editor.addCommand( 'tableProperties', new CKEDITOR.dialogCommand( 'tableProperties' ) ); 15 editor.ui.addButton( 'Table', 16 { 17 label : lang.toolbar, 18 command : 'table' 19 }); 15 editor.addCommand( 'table', new CKEDITOR.dialogCommand( 'table' ) ); 16 editor.addCommand( 'tableProperties', new CKEDITOR.dialogCommand( 'tableProperties' ) ); 20 17 21 CKEDITOR.dialog.add( 'table', this.path + 'dialogs/table.js' ); 22 CKEDITOR.dialog.add( 'tableProperties', this.path + 'dialogs/table.js' ); 23 24 // If the "menu" plugin is loaded, register the menu items. 25 if ( editor.addMenuItems ) 26 { 27 editor.addMenuItems( 18 editor.ui.addButton( 'Table', 28 19 { 29 table : 30 { 31 label : lang.menu, 32 command : 'tableProperties', 33 group : 'table', 34 order : 5 35 }, 20 label : lang.toolbar, 21 command : 'table' 22 }); 36 23 37 // tabledelete : 38 // { 39 // label : lang.deleteTable, 40 // command : 'tableDelete', 41 // group : 'table', 42 // order : 1 43 // }, 24 CKEDITOR.dialog.add( 'table', this.path + 'dialogs/table.js' ); 25 CKEDITOR.dialog.add( 'tableProperties', this.path + 'dialogs/table.js' ); 44 26 45 tablecell : 27 // If the "menu" plugin is loaded, register the menu items. 28 if ( editor.addMenuItems ) 29 { 30 editor.addMenuItems( 46 31 { 47 label : lang.cell.menu, 48 group : 'tablecell', 49 order : 1, 50 getItems : function() 32 table : 51 33 { 52 return { 53 tablecell_insertBefore : CKEDITOR.TRISTATE_OFF, 54 tablecell_insertAfter : CKEDITOR.TRISTATE_OFF, 55 tablecell_delete : CKEDITOR.TRISTATE_OFF 56 }; 57 } 58 }, 34 label : lang.menu, 35 command : 'tableProperties', 36 group : 'table', 37 order : 5 38 }, 59 39 60 tablecell_insertBefore : 61 { 62 label : lang.cell.insertBefore, 63 group : 'tablecell', 64 order : 5 65 }, 66 67 tablecell_insertAfter : 68 { 69 label : lang.cell.insertAfter, 70 group : 'tablecell', 71 order : 10 72 }, 73 74 tablecell_delete : 75 { 76 label : lang.cell.deleteCell, 77 group : 'tablecell', 78 order : 15 79 }, 80 81 tablerow : 82 { 83 label : lang.row.menu, 84 group : 'tablerow', 85 order : 1, 86 getItems : function() 40 tabledelete : 87 41 { 88 return { 89 tablerow_insertBefore : CKEDITOR.TRISTATE_OFF, 90 tablerow_insertAfter : CKEDITOR.TRISTATE_OFF, 91 tablerow_delete : CKEDITOR.TRISTATE_OFF 92 }; 42 label : lang.deleteTable, 43 command : 'tableDelete', 44 group : 'table', 45 order : 1 93 46 } 94 }, 47 } ); 48 } 95 49 96 tablerow_insertBefore : 50 // If the "contextmenu" plugin is loaded, register the listeners. 51 if ( editor.contextMenu ) 52 { 53 editor.contextMenu.addListener( function( element, selection ) 97 54 { 98 label : lang.row.insertBefore, 99 group : 'tablerow', 100 order : 5 101 }, 55 if ( !element ) 56 return null; 102 57 103 tablerow_insertAfter : 104 { 105 label : lang.row.insertAfter, 106 group : 'tablerow', 107 order : 10 108 }, 58 var isTable = element.is( 'table' ) || element.hasAscendant( 'table' ); 109 59 110 tablerow_delete : 111 { 112 label : lang.row.deleteRow, 113 group : 'tablerow', 114 order : 15 115 }, 116 117 tablecolumn : 118 { 119 label : lang.column.menu, 120 group : 'tablecolumn', 121 order : 1, 122 getItems : function() 60 if ( isTable ) 123 61 { 124 62 return { 125 tablecolumn_insertBefore : CKEDITOR.TRISTATE_OFF, 126 tablecolumn_insertAfter : CKEDITOR.TRISTATE_OFF, 127 tablecolumn_delete : CKEDITOR.TRISTATE_OFF 63 tabledelete : CKEDITOR.TRISTATE_OFF, 64 table : CKEDITOR.TRISTATE_OFF 128 65 }; 129 66 } 130 },131 67 132 tablecolumn_insertBefore :133 {134 label : lang.column.insertBefore,135 group : 'tablecolumn',136 order : 5137 },138 139 tablecolumn_insertAfter :140 {141 label : lang.column.insertAfter,142 group : 'tablecolumn',143 order : 10144 },145 146 tablecolumn_delete :147 {148 label : lang.column.deleteColumn,149 group : 'tablecolumn',150 order : 15151 }152 });153 }154 155 // If the "contextmenu" plugin is loaded, register the listeners.156 if ( editor.contextMenu )157 {158 editor.contextMenu.addListener( function( element, selection )159 {160 if ( !element )161 68 return null; 162 163 var isTable = element.is( 'table' ) ; 164 var isCell = !isTable && element.hasAscendant( 'table' ) ; 165 166 if ( isTable || isCell ) 167 { 168 var ret = isCell ? 169 { 170 tablecell : CKEDITOR.TRISTATE_OFF, 171 tablerow : CKEDITOR.TRISTATE_OFF, 172 tablecolumn : CKEDITOR.TRISTATE_OFF 173 } 174 : {}; 175 176 ret.tabledelete = CKEDITOR.TRISTATE_OFF; 177 ret.table = CKEDITOR.TRISTATE_OFF; 178 179 return ret; 180 } 181 182 return null; 183 }); 69 } ); 70 } 184 71 } 185 } 186 } ); 72 } ); 73 })(); 74 -
_source/plugins/tabletools/dialogs/tableCell.js
1 /* 2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 CKEDITOR.dialog.add( 'cellProperties', function( editor ) 7 { 8 var langTable = editor.lang.table; 9 var langCell = langTable.cell; 10 var langCommon = editor.lang.common; 11 var validate = CKEDITOR.dialog.validate; 12 var widthPattern = /^(\d+(?:\.\d+)?)(px|%)$/, 13 heightPattern = /^(\d+(?:\.\d+)?)px$/; 14 var bind = CKEDITOR.tools.bind; 15 16 function spacer() 17 { 18 return { type : 'html', html : ' ' } 19 } 20 21 return { 22 title : langCell.title, 23 minWidth : 480, 24 minHeight : 140, 25 contents : [ 26 { 27 id : 'info', 28 label : langCell.title, 29 accessKey : 'I', 30 elements : 31 [ 32 { 33 type : 'hbox', 34 widths : [ '45%', '10%', '45%' ], 35 children : 36 [ 37 { 38 type : 'vbox', 39 padding : 0, 40 children : 41 [ 42 { 43 type : 'hbox', 44 widths : [ '70%', '30%' ], 45 children : 46 [ 47 { 48 type : 'text', 49 id : 'width', 50 label : langTable.width, 51 widths : [ '71%', '29%' ], 52 labelLayout : 'horizontal', 53 validate : validate[ 'number' ]( langCell.invalidWidth ), 54 setup : function( selectedCell ) 55 { 56 var widthMatch = widthPattern.exec( selectedCell.$.style.width ); 57 if ( widthMatch ) 58 this.setValue( widthMatch[1] ); 59 }, 60 commit : function( selectedCell ) 61 { 62 var unit = this.getDialog().getValueOf( 'info', 'widthType' ); 63 if ( this.getValue() != '' ) 64 selectedCell.$.style.width = this.getValue() + unit; 65 else 66 selectedCell.$.style.width = ''; 67 }, 68 'default' : '' 69 }, 70 { 71 type : 'select', 72 id : 'widthType', 73 labelLayout : 'horizontal', 74 widths : [ '0%', '100%' ], 75 label : '', 76 'default' : 'px', 77 items : 78 [ 79 [ langTable.widthPx, 'px' ], 80 [ langTable.widthPc, '%' ] 81 ], 82 setup : function( selectedCell ) 83 { 84 var widthMatch = widthPattern.exec( selectedCell.$.style.width ); 85 if ( widthMatch ) 86 this.setValue( widthMatch[2] ); 87 } 88 } 89 ] 90 }, 91 { 92 type : 'hbox', 93 widths : [ '70%', '30%' ], 94 children : 95 [ 96 { 97 type : 'text', 98 id : 'height', 99 label : langTable.height, 100 'default' : '', 101 widths : [ '71%', '29%' ], 102 labelLayout : 'horizontal', 103 validate : validate[ 'number' ]( langCell.invalidHeight ), 104 setup : function( selectedCell ) 105 { 106 var heightMatch = heightPattern.exec( selectedCell.$.style.height ); 107 if ( heightMatch ) 108 this.setValue( heightMatch[1] ); 109 }, 110 commit : function( selectedCell ) 111 { 112 if ( this.getValue() != '' ) 113 selectedCell.$.style.height = this.getValue() + 'px'; 114 else 115 selectedCell.$.style.height = ''; 116 } 117 }, 118 { 119 type : 'html', 120 html : langTable.widthPx 121 } 122 ] 123 }, 124 spacer(), 125 { 126 type : 'select', 127 id : 'wordWrap', 128 labelLayout : 'horizontal', 129 label : langCell.wordWrap, 130 widths : [ '50%', '50%' ], 131 'default' : 'yes', 132 items : 133 [ 134 [ langCell.yes, 'yes' ], 135 [ langCell.no, 'no' ] 136 ], 137 commit : function( selectedCell ) 138 { 139 if ( this.getValue() == 'no' ) 140 selectedCell.setAttribute( 'noWrap', 'nowrap' ); 141 else 142 selectedCell.removeAttribute( 'noWrap' ); 143 } 144 }, 145 spacer(), 146 { 147 type : 'select', 148 id : 'hAlign', 149 labelLayout : 'horizontal', 150 label : langCell.hAlign, 151 widths : [ '50%', '50%' ], 152 'default' : '', 153 items : 154 [ 155 [ langCommon.notSet, '' ], 156 [ langTable.alignLeft, 'left' ], 157 [ langTable.alignCenter, 'center' ], 158 [ langTable.alignRight, 'right' ] 159 ], 160 setup : function( selectedCell ) 161 { 162 this.setValue( selectedCell.getAttribute( 'align' ) || '' ); 163 }, 164 commit : function( selectedCell ) 165 { 166 if ( this.getValue() ) 167 selectedCell.setAttribute( 'align', this.getValue() ); 168 else 169 selectedCell.removeAttribute( 'align' ); 170 } 171 }, 172 { 173 type : 'select', 174 id : 'vAlign', 175 labelLayout : 'horizontal', 176 label : langCell.vAlign, 177 widths : [ '50%', '50%' ], 178 'default' : '', 179 items : 180 [ 181 [ langCommon.notSet, '' ], 182 [ langCell.alignTop, 'top' ], 183 [ langCell.alignMiddle, 'middle' ], 184 [ langCell.alignBottom, 'bottom' ], 185 [ langCell.alignBaseline, 'baseline' ] 186 ], 187 setup : function( selectedCell ) 188 { 189 this.setValue( selectedCell.getAttribute( 'vAlign' ) || '' ); 190 }, 191 commit : function( selectedCell ) 192 { 193 if ( this.getValue() ) 194 selectedCell.setAttribute( 'vAlign', this.getValue() ); 195 else 196 selectedCell.removeAttribute( 'vAlign' ); 197 } 198 }, 199 200 ] 201 }, 202 spacer(), 203 { 204 type : 'vbox', 205 padding : 0, 206 children : 207 [ 208 { 209 type : 'select', 210 id : 'cellType', 211 label : langCell.cellType, 212 labelLayout : 'horizontal', 213 widths : [ '50%', '50%' ], 214 'default' : 'td', 215 items : 216 [ 217 [ langCell.data, 'td' ], 218 [ langCell.header, 'th' ] 219 ], 220 setup : function( selectedCell ) 221 { 222 this.setValue( selectedCell.getName() ); 223 }, 224 commit : function( selectedCell ) 225 { 226 selectedCell.renameNode( this.getValue() ); 227 } 228 }, 229 spacer(), 230 { 231 type : 'text', 232 id : 'rowSpan', 233 label : langCell.rowSpan, 234 labelLayout : 'horizontal', 235 widths : [ '50%', '50%' ], 236 'default' : '', 237 validate : validate.integer( langCell.invalidRowSpan ), 238 setup : function( selectedCell ) 239 { 240 this.setValue( selectedCell.getAttribute( 'rowSpan' ) || '' ); 241 }, 242 commit : function( selectedCell ) 243 { 244 if ( this.getValue() ) 245 selectedCell.setAttribute( 'rowSpan', this.getValue() ); 246 else 247 selectedCell.removeAttribute( 'rowSpan' ); 248 } 249 }, 250 { 251 type : 'text', 252 id : 'colSpan', 253 label : langCell.colSpan, 254 labelLayout : 'horizontal', 255 widths : [ '50%', '50%' ], 256 'default' : '', 257 validate : validate.integer( langCell.invalidColSpan ), 258 setup : function( selectedCell ) 259 { 260 this.setValue( selectedCell.getAttribute( 'colSpan' ) || '' ); 261 }, 262 commit : function( selectedCell ) 263 { 264 if ( this.getValue() ) 265 selectedCell.setAttribute( 'colSpan', this.getValue() ); 266 else 267 selectedCell.removeAttribute( 'colSpan' ); 268 } 269 }, 270 spacer(), 271 { 272 type : 'text', 273 id : 'bgColor', 274 label : langCell.bgColor, 275 labelLayout : 'horizontal', 276 widths : [ '50%', '50%' ], 277 'default' : '', 278 setup : function( selectedCell ) 279 { 280 this.setValue( selectedCell.getAttribute( 'bgColor' ) || '' ); 281 }, 282 commit : function( selectedCell ) 283 { 284 if ( this.getValue() ) 285 selectedCell.setAttribute( 'bgColor', this.getValue() ); 286 else 287 selectedCell.removeAttribute( 'bgColor' ); 288 } 289 }, 290 { 291 type : 'text', 292 id : 'borderColor', 293 label : langCell.borderColor, 294 labelLayout : 'horizontal', 295 widths : [ '50%', '50%' ], 296 'default' : '', 297 setup : function( selectedCell ) 298 { 299 this.setValue( selectedCell.getAttribute( 'borderColor' ) || '' ); 300 }, 301 commit : function( selectedCell ) 302 { 303 if ( this.getValue() ) 304 selectedCell.setAttribute( 'borderColor', this.getValue() ); 305 else 306 selectedCell.removeAttribute( 'borderColor' ); 307 } 308 } 309 ] 310 } 311 ] 312 } 313 ] 314 } 315 ], 316 onShow : function() 317 { 318 // Get the selected table cell. 319 var startElement = editor.getSelection().getStartElement(); 320 this.cell = startElement.getAscendant( 'td', true ) || startElement.getAscendant( 'th', true ); 321 322 // Call setupContent(). 323 this.setupContent( this.cell ); 324 }, 325 onOk : function() 326 { 327 // Call commitContent(). 328 this.commitContent( this.cell ); 329 } 330 }; 331 } ); -
_source/plugins/tabletools/plugin.js
1 /* 2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 (function() 7 { 8 function removeRawAttribute( $node, attr ) 9 { 10 if ( CKEDITOR.env.ie ) 11 $node.removeAttribute( attr ); 12 else 13 delete $node[ attr ]; 14 } 15 16 var cellNodeRegex = /^(?:td|th)$/; 17 function getSelectedCells( selection ) 18 { 19 var ranges = selection.getRanges(); 20 var retval = []; 21 22 for ( var i = 0 ; i < ranges.length ; i++ ) 23 { 24 var range = ranges[ i ]; 25 var boundaryNodes = range.getBoundaryNodes(); 26 var currentNode = boundaryNodes.startNode; 27 var endNode = boundaryNodes.endNode; 28 var startCell = currentNode.getAscendant( 'td', true ) || currentNode.getAscendant( 'th', true ); 29 30 if ( startCell ) 31 retval.push( startCell ); 32 33 if ( range.collapsed || currentNode.equals( endNode ) ) 34 continue; 35 36 while ( !( currentNode = currentNode.getNextSourceNode() ).equals( endNode ) ) 37 { 38 if ( currentNode.type == CKEDITOR.NODE_ELEMENT && cellNodeRegex.test( currentNode.getName() ) ) 39 retval.push( currentNode ); 40 } 41 } 42 43 return retval; 44 } 45 46 function createTableMap( $refCell ) 47 { 48 var refCell = new CKEDITOR.dom.element( $refCell ); 49 var $table = ( refCell.getName() == 'table' ? $refCell : refCell.getAscendant( 'table' ) ).$; 50 var $rows = $table.rows; 51 52 // Row and column counters. 53 var r = -1; 54 var map = []; 55 for ( var i = 0 ; i < $rows.length ; i++ ) 56 { 57 r++; 58 if ( !map[ r ] ) 59 map[ r ] = []; 60 61 var c = -1; 62 63 for ( var j = 0 ; j < $rows[ i ].cells.length ; j++ ) 64 { 65 var $cell = $row[ i ].cells[ j ]; 66 67 c++; 68 while ( map[ r ][ c ] ) 69 c++; 70 71 var colSpan = isNaN( $cell.colSpan ) ? 1 : $cell.colSpan; 72 var rowSpan = isNaN( $cell.rowSpan ) ? 1 : $cell.rowSpan; 73 74 for ( var rs = 0 ; rs < rowSpan ; rs++ ) 75 { 76 if ( !map[ r + rs ] ) 77 map[ r + rs ] = []; 78 79 for ( var cs = 0 ; cs < colspan ; cs++ ) 80 map [ r + rs ][ c + cs ] = $rows[ i ].cells[ j ]; 81 } 82 83 c += colSpan - 1; 84 } 85 } 86 87 return map; 88 } 89 90 function installTableMap( tableMap, $table ) 91 { 92 /* 93 * IE BUG: rowSpan is always 1 in IE if the cell isn't attached to a row. So 94 * store is separately in another attribute. (#1917) 95 */ 96 var rowSpanAttr = CKEDITOR.env.ie ? '_cke_rowspan' : 'rowSpan'; 97 98 /* 99 * Disconnect all the cells in tableMap from their parents, set all colSpan 100 * and rowSpan attributes to 1. 101 */ 102 for ( var i = 0 ; i < tableMap.length ; i++ ) 103 { 104 for ( var j = 0 ; j < tableMap[ i ].length ; j++ ) 105 { 106 var $cell = tableMap[ i ][ j ]; 107 if ( $cell.parentNode ) 108 $cell.parentNode.removeChild( $cell ); 109 $cell.colSpan = $cell[ rowSpanAttr ] = 1; 110 } 111 } 112 113 // Scan by rows and set colSpan. 114 var maxCol = 0; 115 for ( var i = 0 ; i < tableMap.length ; i++ ) 116 { 117 for ( var j = 0 ; j < tableMap[ i ].length ; j++ ) 118 { 119 var $cell = tableMap[ i ][ j ]; 120 if ( !$cell ) 121 continue; 122 if ( j > maxCol ) 123 maxCol = j; 124 if ( $cell[ '_cke_colScanned' ] ) 125 continue; 126 if ( tableMap[ i ][ j - 1 ] == $cell ) 127 $cell.colSpan++; 128 if ( tableMap[ i ][ j + 1 ] != $cell ) 129 $cell[ '_cke_colScanned' ] = 1; 130 } 131 } 132 133 // Scan by columns and set rowSpan. 134 for ( var i = 0 ; i <= maxCol ; i++ ) 135 { 136 for ( var j = 0 ; j < tableMap.length ; j++ ) 137 { 138 if ( !tableMap[ j ] ) 139 continue; 140 var $cell = tableMap[ j ][ i ]; 141 if ( !$cell || $cell[ '_cke_rowScanned' ] ) 142 continue; 143 if ( tableMap[ j - 1 ] && tableMap[ j - 1 ][ i ] == $cell ) 144 $cell[ rowSpanAttr ]++; 145 if ( !tableMap[ j + 1 ] || tableMap[ j + 1 ][ i ] != $cell ) 146 $cell[ '_cke_rowScanned' ] = 1; 147 } 148 } 149 150 // Clear all temporary flags. 151 for ( var i = 0 ; i < tableMap.length ; i++ ) 152 { 153 for ( var j = 0 ; j < tableMap[ i ].length ; j++ ) 154 { 155 var $cell = tableMap[ i ][ j ]; 156 removeRawAttribute( $cell, '_cke_colScanned' ); 157 removeRawAttribute( $cell, '_cke_rowScanned' ); 158 } 159 } 160 161 // Insert physical rows and columns to table. 162 for ( var i = 0 ; i < tableMap.length ; i++ ) 163 { 164 var $row = $table.ownerDocument.createElement( 'tr' ); 165 for ( var j = 0 ; j < tableMap[ i ].length ; ) 166 { 167 var $cell = tableMap[ i ][ j ]; 168 if ( tableMap[ i - 1 ] && tableMap[ i - 1 ][ j ] == $cell ) 169 { 170 j += $cell.colSpan; 171 continue; 172 } 173 $row.appendChild( $cell ); 174 if ( rowSpanAttr != 'rowSpan' ) 175 { 176 $cell.rowSpan = cell[ rowSpanAttr ]; 177 $cell.removeAttribute( rowSpanAttr ); 178 } 179 j += $cell.colSpan; 180 if ( $cell.colSpan == 1 ) 181 $cell.removeAttribute( 'colSpan' ); 182 if ( $cell.rowSpan == 1 ) 183 $cell.removeAttribute( 'rowSpan' ); 184 } 185 186 if ( CKEDITOR.env.ie ) 187 $table.rows[ i ].replaceNode( $row ); 188 else 189 { 190 var dest = new CKEDITOR.dom.element( $table.rows[ i ] ); 191 var src = new CKEDITOR.dom.element( $row ); 192 dest.setHtml( '' ); 193 src.moveChildren( dest ); 194 } 195 } 196 } 197 198 function clearRow( $tr ) 199 { 200 // Get the array of row's cells. 201 var $cells = $tr.cells; 202 203 // Empty all cells. 204 for ( var i = 0 ; i < $cells.length ; i++ ) 205 { 206 $cells[ i ].innerHTML = ''; 207 208 if ( !CKEDITOR.env.ie ) 209 ( new CKEDITOR.dom.element( $cells[ i ] ) ).appendBogus(); 210 } 211 } 212 213 function insertRow( selection, insertBefore ) 214 { 215 // Get the row where the selection is placed in. 216 var row = selection.getStartElement().getAscendant( 'tr' ); 217 if ( !row ) 218 return; 219 220 // Create a clone of the row. 221 var newRow = row.clone( true ); 222 223 // Insert the new row before of it. 224 newRow.insertBefore( row ); 225 226 // Clean one of the rows to produce the illusion of inserting an empty row 227 // before or after. 228 clearRow( insertBefore ? newRow.$ : row.$ ); 229 } 230 231 function deleteRows( selectionOrRow ) 232 { 233 if ( selectionOrRow instanceof CKEDITOR.dom.selection ) 234 { 235 var cells = getSelectedCells( selectionOrRow ); 236 var rowsToDelete = []; 237 238 // Queue up the rows - it's possible and likely that we have duplicates. 239 for ( var i = 0 ; i < cells.length ; i++ ) 240 { 241 var row = cells[ i ].getParent(); 242 rowsToDelete[ row.$.rowIndex ] = row; 243 } 244 245 for ( var i = rowsToDelete.length ; i >= 0 ; i-- ) 246 { 247 if ( rowsToDelete[ i ] ) 248 deleteRows( rowsToDelete[ i ] ); 249 } 250 } 251 else if ( selectionOrRow instanceof CKEDITOR.dom.element ) 252 { 253 var table = selectionOrRow.getAscendant( 'table' ); 254 255 if ( table.$.rows.length == 1 ) 256 table.remove(); 257 else 258 selectionOrRow.remove(); 259 } 260 } 261 262 function insertColumn( selection, insertBefore ) 263 { 264 // Get the cell where the selection is placed in. 265 var startElement = selection.getStartElement(); 266 var cell = startElement.getAscendant( 'td', true ) || startElement.getAscendant( 'th', true ); 267 268 if ( !cell ) 269 return; 270 271 // Get the cell's table. 272 var table = cell.getAscendant( 'table' ); 273 var cellIndex = cell.$.cellIndex; 274 275 // Loop through all rows available in the table. 276 for ( var i = 0 ; i < table.$.rows.length ; i++ ) 277 { 278 var $row = table.$.rows[ i ]; 279 280 // If the row doesn't have enough cells, ignore it. 281 if ( $row.cells.length < ( cellIndex + 1 ) ) 282 continue; 283 284 cell = new CKEDITOR.dom.element( $row.cells[ cellIndex ].cloneNode( false ) ); 285 286 if ( !CKEDITOR.env.ie ) 287 cell.appendBogus(); 288 289 // Get back the currently selected cell. 290 var baseCell = new CKEDITOR.dom.element( $row.cells[ cellIndex ] ); 291 if ( insertBefore ) 292 cell.insertBefore( baseCell ); 293 else 294 cell.insertAfter( baseCell ); 295 } 296 } 297 298 function deleteColumns( selectionOrCell ) 299 { 300 if ( selectionOrCell instanceof CKEDITOR.dom.selection ) 301 { 302 var colsToDelete = getSelectedCells( selectionOrCell ); 303 for ( var i = colsToDelete.length ; i >= 0 ; i-- ) 304 { 305 if ( colsToDelete[ i ] ) 306 deleteColumns( colsToDelete[ i ] ); 307 } 308 } 309 else if ( selectionOrCell instanceof CKEDITOR.dom.element ) 310 { 311 // Get the cell's table. 312 var table = selectionOrCell.getAscendant( 'table' ); 313 314 // Get the cell index. 315 var cellIndex = selectionOrCell.$.cellIndex; 316 317 /* 318 * Loop through all rows from down to up, coz it's possible that some rows 319 * will be deleted. 320 */ 321 for ( var i = table.$.rows.length - 1 ; i >= 0 ; i-- ) 322 { 323 // Get the row. 324 var row = new CKEDITOR.dom.element( table.$.rows[ i ] ); 325 326 // If the cell to be removed is the first one and the row has just one cell. 327 if ( cellIndex == 0 && row.$.cells.length == 1 ) 328 { 329 deleteRows( row ); 330 continue; 331 } 332 333 // Else, just delete the cell. 334 if ( row.$.cells[ cellIndex ] ) 335 row.$.removeChild( row.$.cells[ cellIndex ] ); 336 } 337 } 338 } 339 340 function insertCell( selection, insertBefore ) 341 { 342 var startElement = selection.getStartElement(); 343 var cell = startElement.getAscendant( 'td', true ) || startElement.getAscendant( 'th', true ); 344 345 if ( !cell ) 346 return; 347 348 // Create the new cell element to be added. 349 var newCell = cell.clone(); 350 if ( !CKEDITOR.env.ie ) 351 newCell.appendBogus(); 352 353 if ( insertBefore ) 354 newCell.insertBefore( cell ); 355 else 356 newCell.insertAfter( cell ); 357 } 358 359 function deleteCells( selectionOrCell ) 360 { 361 if ( selectionOrCell instanceof CKEDITOR.dom.selection ) 362 { 363 var cellsToDelete = getSelectedCells( selectionOrCell ); 364 for ( var i = cellsToDelete.length - 1 ; i >= 0 ; i-- ) 365 deleteCells( cellsToDelete[ i ] ); 366 } 367 else if ( selectionOrCell instanceof CKEDITOR.dom.element ) 368 { 369 if ( selectionOrCell.getParent().getChildCount() == 1 ) 370 selectionOrCell.getParent().remove(); 371 else 372 selectionOrCell.remove(); 373 } 374 } 375 376 CKEDITOR.plugins.add( 'tabletools', 377 { 378 init : function( editor ) 379 { 380 var lang = editor.lang.table; 381 382 editor.addCommand( 'cellProperties', new CKEDITOR.dialogCommand( 'cellProperties' ) ); 383 CKEDITOR.dialog.add( 'cellProperties', this.path + 'dialogs/tableCell.js' ); 384 385 editor.addCommand( 'tableDelete', 386 { 387 exec : function( editor ) 388 { 389 var selection = editor.getSelection(); 390 var startElement = selection && selection.getStartElement(); 391 var table = startElement && startElement.getAscendant( 'table', true ); 392 393 if ( !table ) 394 return; 395 396 // Maintain the selection point at where the table was deleted. 397 selection.selectElement( table ); 398 var range = selection.getRanges()[0]; 399 range.collapse(); 400 selection.selectRanges( [ range ] ); 401 402 // If the table's parent has only one child, remove it as well. 403 if ( table.getParent().getChildCount() == 1 ) 404 table.getParent().remove(); 405 else 406 table.remove(); 407 } 408 } ); 409 410 editor.addCommand( 'rowDelete', 411 { 412 exec : function( editor ) 413 { 414 var selection = editor.getSelection(); 415 deleteRows( selection ); 416 } 417 } ); 418 419 editor.addCommand( 'rowInsertBefore', 420 { 421 exec : function( editor ) 422 { 423 var selection = editor.getSelection(); 424 insertRow( selection, true ); 425 } 426 } ); 427 428 editor.addCommand( 'rowInsertAfter', 429 { 430 exec : function( editor ) 431 { 432 var selection = editor.getSelection(); 433 insertRow( selection ); 434 } 435 } ); 436 437 editor.addCommand( 'columnDelete', 438 { 439 exec : function( editor ) 440 { 441 var selection = editor.getSelection(); 442 deleteColumns( selection ); 443 } 444 } ); 445 446 editor.addCommand( 'columnInsertBefore', 447 { 448 exec : function( editor ) 449 { 450 var selection = editor.getSelection(); 451 insertColumn( selection, true ); 452 } 453 } ); 454 455 editor.addCommand( 'columnInsertAfter', 456 { 457 exec : function( editor ) 458 { 459 var selection = editor.getSelection(); 460 insertColumn( selection ); 461 } 462 } ); 463 464 editor.addCommand( 'cellDelete', 465 { 466 exec : function( editor ) 467 { 468 var selection = editor.getSelection(); 469 deleteCells( selection ); 470 } 471 } ); 472 473 editor.addCommand( 'cellInsertBefore', 474 { 475 exec : function( editor ) 476 { 477 var selection = editor.getSelection(); 478 insertCell( selection, true ); 479 } 480 } ); 481 482 editor.addCommand( 'cellInsertAfter', 483 { 484 exec : function( editor ) 485 { 486 var selection = editor.getSelection(); 487 insertCell( selection ); 488 } 489 } ); 490 491 // If the "menu" plugin is loaded, register the menu items. 492 if ( editor.addMenuItems ) 493 { 494 editor.addMenuItems( 495 { 496 tablecell : 497 { 498 label : lang.cell.menu, 499 group : 'tablecell', 500 order : 1, 501 getItems : function() 502 { 503 var cells = getSelectedCells( editor.getSelection() ); 504 505 return { 506 tablecell_insertBefore : CKEDITOR.TRISTATE_OFF, 507 tablecell_insertAfter : CKEDITOR.TRISTATE_OFF, 508 tablecell_delete : CKEDITOR.TRISTATE_OFF, 509 tablecell_properties : cells.length == 1 ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED 510 }; 511 } 512 }, 513 514 tablecell_insertBefore : 515 { 516 label : lang.cell.insertBefore, 517 group : 'tablecell', 518 command : 'cellInsertBefore', 519 order : 5 520 }, 521 522 tablecell_insertAfter : 523 { 524 label : lang.cell.insertAfter, 525 group : 'tablecell', 526 command : 'cellInsertAfter', 527 order : 10 528 }, 529 530 tablecell_delete : 531 { 532 label : lang.cell.deleteCell, 533 group : 'tablecell', 534 command : 'cellDelete', 535 order : 15 536 }, 537 538 tablecell_properties : 539 { 540 label : lang.cell.title, 541 group : 'tablecellproperties', 542 command : 'cellProperties', 543 order : 20 544 }, 545 546 tablerow : 547 { 548 label : lang.row.menu, 549 group : 'tablerow', 550 order : 1, 551 getItems : function() 552 { 553 return { 554 tablerow_insertBefore : CKEDITOR.TRISTATE_OFF, 555 tablerow_insertAfter : CKEDITOR.TRISTATE_OFF, 556 tablerow_delete : CKEDITOR.TRISTATE_OFF 557 }; 558 } 559 }, 560 561 tablerow_insertBefore : 562 { 563 label : lang.row.insertBefore, 564 group : 'tablerow', 565 command : 'rowInsertBefore', 566 order : 5 567 }, 568 569 tablerow_insertAfter : 570 { 571 label : lang.row.insertAfter, 572 group : 'tablerow', 573 command : 'rowInsertAfter', 574 order : 10 575 }, 576 577 tablerow_delete : 578 { 579 label : lang.row.deleteRow, 580 group : 'tablerow', 581 command : 'rowDelete', 582 order : 15 583 }, 584 585 tablecolumn : 586 { 587 label : lang.column.menu, 588 group : 'tablecolumn', 589 order : 1, 590 getItems : function() 591 { 592 return { 593 tablecolumn_insertBefore : CKEDITOR.TRISTATE_OFF, 594 tablecolumn_insertAfter : CKEDITOR.TRISTATE_OFF, 595 tablecolumn_delete : CKEDITOR.TRISTATE_OFF 596 }; 597 } 598 }, 599 600 tablecolumn_insertBefore : 601 { 602 label : lang.column.insertBefore, 603 group : 'tablecolumn', 604 command : 'columnInsertBefore', 605 order : 5 606 }, 607 608 tablecolumn_insertAfter : 609 { 610 label : lang.column.insertAfter, 611 group : 'tablecolumn', 612 command : 'columnInsertAfter', 613 order : 10 614 }, 615 616 tablecolumn_delete : 617 { 618 label : lang.column.deleteColumn, 619 group : 'tablecolumn', 620 command : 'columnDelete', 621 order : 15 622 } 623 }); 624 } 625 626 // If the "contextmenu" plugin is laoded, register the listeners. 627 if ( editor.contextMenu ) 628 { 629 editor.contextMenu.addListener( function( element, selection ) 630 { 631 if ( !element ) 632 return null; 633 634 var isCell = !element.is( 'table' ) && element.hasAscendant( 'table' ) ; 635 636 if ( isCell ) 637 { 638 return { 639 tablecell : CKEDITOR.TRISTATE_OFF, 640 tablerow : CKEDITOR.TRISTATE_OFF, 641 tablecolumn : CKEDITOR.TRISTATE_OFF 642 }; 643 } 644 645 return null; 646 } ); 647 } 648 } 649 } ); 650 })() -
_source/core/config.js
150 150 * config.plugins = 'basicstyles,button,htmldataprocessor,toolbar,wysiwygarea'; 151 151 */ 152 152 153 plugins : 'basicstyles,blockquote,button,clipboard,colorbutton,contextmenu,elementspath,enterkey,entities,find,flash,font,format,forms,horizontalrule,htmldataprocessor,image,indent,justify,keystrokes,link,list,newpage,pagebreak,pastefromword,pastetext,preview,print,removeformat,save,smiley,showblocks,sourcearea,stylescombo,table, specialchar,tab,templates,toolbar,undo,wysiwygarea,wsc',153 plugins : 'basicstyles,blockquote,button,clipboard,colorbutton,contextmenu,elementspath,enterkey,entities,find,flash,font,format,forms,horizontalrule,htmldataprocessor,image,indent,justify,keystrokes,link,list,newpage,pagebreak,pastefromword,pastetext,preview,print,removeformat,save,smiley,showblocks,sourcearea,stylescombo,table,tabletools,specialchar,tab,templates,toolbar,undo,wysiwygarea,wsc', 154 154 155 155 /** 156 156 * The editor tabindex value. -
_source/core/dom/element.js
1248 1248 } 1249 1249 1250 1250 return $ && new CKEDITOR.dom.document( $.contentWindow.document ); 1251 }, 1252 1253 /** 1254 * Copy all the attributes from one node to the other, kinda like a clone 1255 * skipAttributes is an object with the attributes that must NOT be copied. 1256 * @param {CKEDITOR.dom.element} dest The destination element. 1257 * @param {Object} skipAttributes A dictionary of attributes to skip. 1258 * @example 1259 */ 1260 copyAttributes : function( dest, skipAttributes ) 1261 { 1262 var attributes = this.$.attributes; 1263 skipAttributes = skipAttributes || {}; 1264 1265 for ( var n = 0 ; n < attributes.length ; n++ ) 1266 { 1267 var attribute = attributes[n]; 1268 1269 if ( attribute.specified ) 1270 { 1271 var attrName = attribute.nodeName; 1272 // We can set the type only once, so do it with the proper value, not copying it. 1273 if ( attrName in skipAttributes ) 1274 continue; 1275 1276 var attrValue = this.getAttribute( attrName ); 1277 if ( attrValue === null ) 1278 attrValue = attribute.nodeValue; 1279 1280 dest.setAttribute( attrName, attrValue ); 1281 } 1282 } 1283 1284 // The style: 1285 if ( this.$.style.cssText !== '' ) 1286 dest.$.style.cssText = this.$.style.cssText; 1287 }, 1288 1289 /** 1290 * Changes the tag name of the current element. 1291 * @param {String} newTag The new tag for the element. 1292 */ 1293 renameNode : function( newTag ) 1294 { 1295 // If it's already correct exit here. 1296 if ( this.getName() == newTag ) 1297 return; 1298 1299 var doc = this.getDocument(); 1300 1301 // Create the new node. 1302 var newNode = new CKEDITOR.dom.element( newTag, doc ); 1303 1304 // Copy all attributes. 1305 this.copyAttributes( newNode ); 1306 1307 // Move children to the new node. 1308 this.moveChildren( newNode ); 1309 1310 // Replace the node. 1311 this.$.parentNode.replaceChild( newNode.$, this.$ ); 1312 newNode.$._cke_expando = this.$._cke_expando; 1313 this.$ = newNode.$; 1251 1314 } 1252 1315 }); -
_source/core/dom/range.js
601 601 { 602 602 childCount = endNode.getChildCount(); 603 603 if ( childCount > endOffset ) 604 endNode = endNode.getChild( endOffset ).getPreviousSourceNode( );604 endNode = endNode.getChild( endOffset ).getPreviousSourceNode( true ); 605 605 else if ( childCount < 1 ) 606 606 endNode = endNode.getPreviousSourceNode(); 607 607 else // endOffset > childCount but childCount is not 0