src/pyams_skin/resources/js/ext/tinymce/dev/plugins/table/classes/TableGrid.js
changeset 69 a361355b55c7
equal deleted inserted replaced
68:fd8fb93e1b6a 69:a361355b55c7
       
     1 /**
       
     2  * TableGrid.js
       
     3  *
       
     4  * Copyright, Moxiecode Systems AB
       
     5  * Released under LGPL License.
       
     6  *
       
     7  * License: http://www.tinymce.com/license
       
     8  * Contributing: http://www.tinymce.com/contributing
       
     9  */
       
    10 
       
    11 /**
       
    12  * This class creates a grid out of a table element. This
       
    13  * makes it a whole lot easier to handle complex tables with
       
    14  * col/row spans.
       
    15  *
       
    16  * @class tinymce.tableplugin.TableGrid
       
    17  * @private
       
    18  */
       
    19 define("tinymce/tableplugin/TableGrid", [
       
    20 	"tinymce/util/Tools",
       
    21 	"tinymce/Env"
       
    22 ], function(Tools, Env) {
       
    23 	var each = Tools.each;
       
    24 
       
    25 	function getSpanVal(td, name) {
       
    26 		return parseInt(td.getAttribute(name) || 1, 10);
       
    27 	}
       
    28 
       
    29 	return function(editor, table) {
       
    30 		var grid, gridWidth, startPos, endPos, selectedCell, selection = editor.selection, dom = selection.dom;
       
    31 
       
    32 		function buildGrid() {
       
    33 			var startY = 0;
       
    34 
       
    35 			grid = [];
       
    36 			gridWidth = 0;
       
    37 
       
    38 			each(['thead', 'tbody', 'tfoot'], function(part) {
       
    39 				var rows = dom.select('> ' + part + ' tr', table);
       
    40 
       
    41 				each(rows, function(tr, y) {
       
    42 					y += startY;
       
    43 
       
    44 					each(dom.select('> td, > th', tr), function(td, x) {
       
    45 						var x2, y2, rowspan, colspan;
       
    46 
       
    47 						// Skip over existing cells produced by rowspan
       
    48 						if (grid[y]) {
       
    49 							while (grid[y][x]) {
       
    50 								x++;
       
    51 							}
       
    52 						}
       
    53 
       
    54 						// Get col/rowspan from cell
       
    55 						rowspan = getSpanVal(td, 'rowspan');
       
    56 						colspan = getSpanVal(td, 'colspan');
       
    57 
       
    58 						// Fill out rowspan/colspan right and down
       
    59 						for (y2 = y; y2 < y + rowspan; y2++) {
       
    60 							if (!grid[y2]) {
       
    61 								grid[y2] = [];
       
    62 							}
       
    63 
       
    64 							for (x2 = x; x2 < x + colspan; x2++) {
       
    65 								grid[y2][x2] = {
       
    66 									part: part,
       
    67 									real: y2 == y && x2 == x,
       
    68 									elm: td,
       
    69 									rowspan: rowspan,
       
    70 									colspan: colspan
       
    71 								};
       
    72 							}
       
    73 						}
       
    74 
       
    75 						gridWidth = Math.max(gridWidth, x + 1);
       
    76 					});
       
    77 				});
       
    78 
       
    79 				startY += rows.length;
       
    80 			});
       
    81 		}
       
    82 
       
    83 		function cloneNode(node, children) {
       
    84 			node = node.cloneNode(children);
       
    85 			node.removeAttribute('id');
       
    86 
       
    87 			return node;
       
    88 		}
       
    89 
       
    90 		function getCell(x, y) {
       
    91 			var row;
       
    92 
       
    93 			row = grid[y];
       
    94 			if (row) {
       
    95 				return row[x];
       
    96 			}
       
    97 		}
       
    98 
       
    99 		function setSpanVal(td, name, val) {
       
   100 			if (td) {
       
   101 				val = parseInt(val, 10);
       
   102 
       
   103 				if (val === 1) {
       
   104 					td.removeAttribute(name, 1);
       
   105 				} else {
       
   106 					td.setAttribute(name, val, 1);
       
   107 				}
       
   108 			}
       
   109 		}
       
   110 
       
   111 		function isCellSelected(cell) {
       
   112 			return cell && (dom.hasClass(cell.elm, 'mce-item-selected') || cell == selectedCell);
       
   113 		}
       
   114 
       
   115 		function getSelectedRows() {
       
   116 			var rows = [];
       
   117 
       
   118 			each(table.rows, function(row) {
       
   119 				each(row.cells, function(cell) {
       
   120 					if (dom.hasClass(cell, 'mce-item-selected') || (selectedCell && cell == selectedCell.elm)) {
       
   121 						rows.push(row);
       
   122 						return false;
       
   123 					}
       
   124 				});
       
   125 			});
       
   126 
       
   127 			return rows;
       
   128 		}
       
   129 
       
   130 		function deleteTable() {
       
   131 			var rng = dom.createRng();
       
   132 
       
   133 			rng.setStartAfter(table);
       
   134 			rng.setEndAfter(table);
       
   135 
       
   136 			selection.setRng(rng);
       
   137 
       
   138 			dom.remove(table);
       
   139 		}
       
   140 
       
   141 		function cloneCell(cell) {
       
   142 			var formatNode, cloneFormats = {};
       
   143 
       
   144 			if (editor.settings.table_clone_elements !== false) {
       
   145 				cloneFormats = Tools.makeMap(
       
   146 					(editor.settings.table_clone_elements || 'strong em b i span font h1 h2 h3 h4 h5 h6 p div').toUpperCase(),
       
   147 					/[ ,]/
       
   148 				);
       
   149 			}
       
   150 
       
   151 			// Clone formats
       
   152 			Tools.walk(cell, function(node) {
       
   153 				var curNode;
       
   154 
       
   155 				if (node.nodeType == 3) {
       
   156 					each(dom.getParents(node.parentNode, null, cell).reverse(), function(node) {
       
   157 						if (!cloneFormats[node.nodeName]) {
       
   158 							return;
       
   159 						}
       
   160 
       
   161 						node = cloneNode(node, false);
       
   162 
       
   163 						if (!formatNode) {
       
   164 							formatNode = curNode = node;
       
   165 						} else if (curNode) {
       
   166 							curNode.appendChild(node);
       
   167 						}
       
   168 
       
   169 						curNode = node;
       
   170 					});
       
   171 
       
   172 					// Add something to the inner node
       
   173 					if (curNode) {
       
   174 						curNode.innerHTML = Env.ie ? '&nbsp;' : '<br data-mce-bogus="1" />';
       
   175 					}
       
   176 
       
   177 					return false;
       
   178 				}
       
   179 			}, 'childNodes');
       
   180 
       
   181 			cell = cloneNode(cell, false);
       
   182 			setSpanVal(cell, 'rowSpan', 1);
       
   183 			setSpanVal(cell, 'colSpan', 1);
       
   184 
       
   185 			if (formatNode) {
       
   186 				cell.appendChild(formatNode);
       
   187 			} else {
       
   188 				if (!Env.ie || Env.ie > 10) {
       
   189 					cell.innerHTML = '<br data-mce-bogus="1" />';
       
   190 				}
       
   191 			}
       
   192 
       
   193 			return cell;
       
   194 		}
       
   195 
       
   196 		function cleanup() {
       
   197 			var rng = dom.createRng(), row;
       
   198 
       
   199 			// Empty rows
       
   200 			each(dom.select('tr', table), function(tr) {
       
   201 				if (tr.cells.length === 0) {
       
   202 					dom.remove(tr);
       
   203 				}
       
   204 			});
       
   205 
       
   206 			// Empty table
       
   207 			if (dom.select('tr', table).length === 0) {
       
   208 				rng.setStartBefore(table);
       
   209 				rng.setEndBefore(table);
       
   210 				selection.setRng(rng);
       
   211 				dom.remove(table);
       
   212 				return;
       
   213 			}
       
   214 
       
   215 			// Empty header/body/footer
       
   216 			each(dom.select('thead,tbody,tfoot', table), function(part) {
       
   217 				if (part.rows.length === 0) {
       
   218 					dom.remove(part);
       
   219 				}
       
   220 			});
       
   221 
       
   222 			// Restore selection to start position if it still exists
       
   223 			buildGrid();
       
   224 
       
   225 			// If we have a valid startPos object
       
   226 			if (startPos) {
       
   227 				// Restore the selection to the closest table position
       
   228 				row = grid[Math.min(grid.length - 1, startPos.y)];
       
   229 				if (row) {
       
   230 					selection.select(row[Math.min(row.length - 1, startPos.x)].elm, true);
       
   231 					selection.collapse(true);
       
   232 				}
       
   233 			}
       
   234 		}
       
   235 
       
   236 		function fillLeftDown(x, y, rows, cols) {
       
   237 			var tr, x2, r, c, cell;
       
   238 
       
   239 			tr = grid[y][x].elm.parentNode;
       
   240 			for (r = 1; r <= rows; r++) {
       
   241 				tr = dom.getNext(tr, 'tr');
       
   242 
       
   243 				if (tr) {
       
   244 					// Loop left to find real cell
       
   245 					for (x2 = x; x2 >= 0; x2--) {
       
   246 						cell = grid[y + r][x2].elm;
       
   247 
       
   248 						if (cell.parentNode == tr) {
       
   249 							// Append clones after
       
   250 							for (c = 1; c <= cols; c++) {
       
   251 								dom.insertAfter(cloneCell(cell), cell);
       
   252 							}
       
   253 
       
   254 							break;
       
   255 						}
       
   256 					}
       
   257 
       
   258 					if (x2 == -1) {
       
   259 						// Insert nodes before first cell
       
   260 						for (c = 1; c <= cols; c++) {
       
   261 							tr.insertBefore(cloneCell(tr.cells[0]), tr.cells[0]);
       
   262 						}
       
   263 					}
       
   264 				}
       
   265 			}
       
   266 		}
       
   267 
       
   268 		function split() {
       
   269 			each(grid, function(row, y) {
       
   270 				each(row, function(cell, x) {
       
   271 					var colSpan, rowSpan, i;
       
   272 
       
   273 					if (isCellSelected(cell)) {
       
   274 						cell = cell.elm;
       
   275 						colSpan = getSpanVal(cell, 'colspan');
       
   276 						rowSpan = getSpanVal(cell, 'rowspan');
       
   277 
       
   278 						if (colSpan > 1 || rowSpan > 1) {
       
   279 							setSpanVal(cell, 'rowSpan', 1);
       
   280 							setSpanVal(cell, 'colSpan', 1);
       
   281 
       
   282 							// Insert cells right
       
   283 							for (i = 0; i < colSpan - 1; i++) {
       
   284 								dom.insertAfter(cloneCell(cell), cell);
       
   285 							}
       
   286 
       
   287 							fillLeftDown(x, y, rowSpan - 1, colSpan);
       
   288 						}
       
   289 					}
       
   290 				});
       
   291 			});
       
   292 		}
       
   293 
       
   294 		function merge(cell, cols, rows) {
       
   295 			var pos, startX, startY, endX, endY, x, y, startCell, endCell, children, count;
       
   296 
       
   297 			// Use specified cell and cols/rows
       
   298 			if (cell) {
       
   299 				pos = getPos(cell);
       
   300 				startX = pos.x;
       
   301 				startY = pos.y;
       
   302 				endX = startX + (cols - 1);
       
   303 				endY = startY + (rows - 1);
       
   304 			} else {
       
   305 				startPos = endPos = null;
       
   306 
       
   307 				// Calculate start/end pos by checking for selected cells in grid works better with context menu
       
   308 				each(grid, function(row, y) {
       
   309 					each(row, function(cell, x) {
       
   310 						if (isCellSelected(cell)) {
       
   311 							if (!startPos) {
       
   312 								startPos = {x: x, y: y};
       
   313 							}
       
   314 
       
   315 							endPos = {x: x, y: y};
       
   316 						}
       
   317 					});
       
   318 				});
       
   319 
       
   320 				// Use selection, but make sure startPos is valid before accessing
       
   321 				if (startPos) {
       
   322 					startX = startPos.x;
       
   323 					startY = startPos.y;
       
   324 					endX = endPos.x;
       
   325 					endY = endPos.y;
       
   326 				}
       
   327 			}
       
   328 
       
   329 			// Find start/end cells
       
   330 			startCell = getCell(startX, startY);
       
   331 			endCell = getCell(endX, endY);
       
   332 
       
   333 			// Check if the cells exists and if they are of the same part for example tbody = tbody
       
   334 			if (startCell && endCell && startCell.part == endCell.part) {
       
   335 				// Split and rebuild grid
       
   336 				split();
       
   337 				buildGrid();
       
   338 
       
   339 				// Set row/col span to start cell
       
   340 				startCell = getCell(startX, startY).elm;
       
   341 				setSpanVal(startCell, 'colSpan', (endX - startX) + 1);
       
   342 				setSpanVal(startCell, 'rowSpan', (endY - startY) + 1);
       
   343 
       
   344 				// Remove other cells and add it's contents to the start cell
       
   345 				for (y = startY; y <= endY; y++) {
       
   346 					for (x = startX; x <= endX; x++) {
       
   347 						if (!grid[y] || !grid[y][x]) {
       
   348 							continue;
       
   349 						}
       
   350 
       
   351 						cell = grid[y][x].elm;
       
   352 
       
   353 						/*jshint loopfunc:true */
       
   354 						/*eslint no-loop-func:0 */
       
   355 						if (cell != startCell) {
       
   356 							// Move children to startCell
       
   357 							children = Tools.grep(cell.childNodes);
       
   358 							each(children, function(node) {
       
   359 								startCell.appendChild(node);
       
   360 							});
       
   361 
       
   362 							// Remove bogus nodes if there is children in the target cell
       
   363 							if (children.length) {
       
   364 								children = Tools.grep(startCell.childNodes);
       
   365 								count = 0;
       
   366 								each(children, function(node) {
       
   367 									if (node.nodeName == 'BR' && dom.getAttrib(node, 'data-mce-bogus') && count++ < children.length - 1) {
       
   368 										startCell.removeChild(node);
       
   369 									}
       
   370 								});
       
   371 							}
       
   372 
       
   373 							dom.remove(cell);
       
   374 						}
       
   375 					}
       
   376 				}
       
   377 
       
   378 				// Remove empty rows etc and restore caret location
       
   379 				cleanup();
       
   380 			}
       
   381 		}
       
   382 
       
   383 		function insertRow(before) {
       
   384 			var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell, rowSpan;
       
   385 
       
   386 			// Find first/last row
       
   387 			each(grid, function(row, y) {
       
   388 				each(row, function(cell) {
       
   389 					if (isCellSelected(cell)) {
       
   390 						cell = cell.elm;
       
   391 						rowElm = cell.parentNode;
       
   392 						newRow = cloneNode(rowElm, false);
       
   393 						posY = y;
       
   394 
       
   395 						if (before) {
       
   396 							return false;
       
   397 						}
       
   398 					}
       
   399 				});
       
   400 
       
   401 				if (before) {
       
   402 					return !posY;
       
   403 				}
       
   404 			});
       
   405 
       
   406 			// If posY is undefined there is nothing for us to do here...just return to avoid crashing below
       
   407 			if (posY === undefined) {
       
   408 				return;
       
   409 			}
       
   410 
       
   411 			for (x = 0; x < grid[0].length; x++) {
       
   412 				// Cell not found could be because of an invalid table structure
       
   413 				if (!grid[posY][x]) {
       
   414 					continue;
       
   415 				}
       
   416 
       
   417 				cell = grid[posY][x].elm;
       
   418 
       
   419 				if (cell != lastCell) {
       
   420 					if (!before) {
       
   421 						rowSpan = getSpanVal(cell, 'rowspan');
       
   422 						if (rowSpan > 1) {
       
   423 							setSpanVal(cell, 'rowSpan', rowSpan + 1);
       
   424 							continue;
       
   425 						}
       
   426 					} else {
       
   427 						// Check if cell above can be expanded
       
   428 						if (posY > 0 && grid[posY - 1][x]) {
       
   429 							otherCell = grid[posY - 1][x].elm;
       
   430 							rowSpan = getSpanVal(otherCell, 'rowSpan');
       
   431 							if (rowSpan > 1) {
       
   432 								setSpanVal(otherCell, 'rowSpan', rowSpan + 1);
       
   433 								continue;
       
   434 							}
       
   435 						}
       
   436 					}
       
   437 
       
   438 					// Insert new cell into new row
       
   439 					newCell = cloneCell(cell);
       
   440 					setSpanVal(newCell, 'colSpan', cell.colSpan);
       
   441 
       
   442 					newRow.appendChild(newCell);
       
   443 
       
   444 					lastCell = cell;
       
   445 				}
       
   446 			}
       
   447 
       
   448 			if (newRow.hasChildNodes()) {
       
   449 				if (!before) {
       
   450 					dom.insertAfter(newRow, rowElm);
       
   451 				} else {
       
   452 					rowElm.parentNode.insertBefore(newRow, rowElm);
       
   453 				}
       
   454 			}
       
   455 		}
       
   456 
       
   457 		function insertCol(before) {
       
   458 			var posX, lastCell;
       
   459 
       
   460 			// Find first/last column
       
   461 			each(grid, function(row) {
       
   462 				each(row, function(cell, x) {
       
   463 					if (isCellSelected(cell)) {
       
   464 						posX = x;
       
   465 
       
   466 						if (before) {
       
   467 							return false;
       
   468 						}
       
   469 					}
       
   470 				});
       
   471 
       
   472 				if (before) {
       
   473 					return !posX;
       
   474 				}
       
   475 			});
       
   476 
       
   477 			each(grid, function(row, y) {
       
   478 				var cell, rowSpan, colSpan;
       
   479 
       
   480 				if (!row[posX]) {
       
   481 					return;
       
   482 				}
       
   483 
       
   484 				cell = row[posX].elm;
       
   485 				if (cell != lastCell) {
       
   486 					colSpan = getSpanVal(cell, 'colspan');
       
   487 					rowSpan = getSpanVal(cell, 'rowspan');
       
   488 
       
   489 					if (colSpan == 1) {
       
   490 						if (!before) {
       
   491 							dom.insertAfter(cloneCell(cell), cell);
       
   492 							fillLeftDown(posX, y, rowSpan - 1, colSpan);
       
   493 						} else {
       
   494 							cell.parentNode.insertBefore(cloneCell(cell), cell);
       
   495 							fillLeftDown(posX, y, rowSpan - 1, colSpan);
       
   496 						}
       
   497 					} else {
       
   498 						setSpanVal(cell, 'colSpan', cell.colSpan + 1);
       
   499 					}
       
   500 
       
   501 					lastCell = cell;
       
   502 				}
       
   503 			});
       
   504 		}
       
   505 
       
   506 		function deleteCols() {
       
   507 			var cols = [];
       
   508 
       
   509 			// Get selected column indexes
       
   510 			each(grid, function(row) {
       
   511 				each(row, function(cell, x) {
       
   512 					if (isCellSelected(cell) && Tools.inArray(cols, x) === -1) {
       
   513 						each(grid, function(row) {
       
   514 							var cell = row[x].elm, colSpan;
       
   515 
       
   516 							colSpan = getSpanVal(cell, 'colSpan');
       
   517 
       
   518 							if (colSpan > 1) {
       
   519 								setSpanVal(cell, 'colSpan', colSpan - 1);
       
   520 							} else {
       
   521 								dom.remove(cell);
       
   522 							}
       
   523 						});
       
   524 
       
   525 						cols.push(x);
       
   526 					}
       
   527 				});
       
   528 			});
       
   529 
       
   530 			cleanup();
       
   531 		}
       
   532 
       
   533 		function deleteRows() {
       
   534 			var rows;
       
   535 
       
   536 			function deleteRow(tr) {
       
   537 				var pos, lastCell;
       
   538 
       
   539 				// Move down row spanned cells
       
   540 				each(tr.cells, function(cell) {
       
   541 					var rowSpan = getSpanVal(cell, 'rowSpan');
       
   542 
       
   543 					if (rowSpan > 1) {
       
   544 						setSpanVal(cell, 'rowSpan', rowSpan - 1);
       
   545 						pos = getPos(cell);
       
   546 						fillLeftDown(pos.x, pos.y, 1, 1);
       
   547 					}
       
   548 				});
       
   549 
       
   550 				// Delete cells
       
   551 				pos = getPos(tr.cells[0]);
       
   552 				each(grid[pos.y], function(cell) {
       
   553 					var rowSpan;
       
   554 
       
   555 					cell = cell.elm;
       
   556 
       
   557 					if (cell != lastCell) {
       
   558 						rowSpan = getSpanVal(cell, 'rowSpan');
       
   559 
       
   560 						if (rowSpan <= 1) {
       
   561 							dom.remove(cell);
       
   562 						} else {
       
   563 							setSpanVal(cell, 'rowSpan', rowSpan - 1);
       
   564 						}
       
   565 
       
   566 						lastCell = cell;
       
   567 					}
       
   568 				});
       
   569 			}
       
   570 
       
   571 			// Get selected rows and move selection out of scope
       
   572 			rows = getSelectedRows();
       
   573 
       
   574 			// Delete all selected rows
       
   575 			each(rows.reverse(), function(tr) {
       
   576 				deleteRow(tr);
       
   577 			});
       
   578 
       
   579 			cleanup();
       
   580 		}
       
   581 
       
   582 		function cutRows() {
       
   583 			var rows = getSelectedRows();
       
   584 
       
   585 			dom.remove(rows);
       
   586 			cleanup();
       
   587 
       
   588 			return rows;
       
   589 		}
       
   590 
       
   591 		function copyRows() {
       
   592 			var rows = getSelectedRows();
       
   593 
       
   594 			each(rows, function(row, i) {
       
   595 				rows[i] = cloneNode(row, true);
       
   596 			});
       
   597 
       
   598 			return rows;
       
   599 		}
       
   600 
       
   601 		function pasteRows(rows, before) {
       
   602 			var selectedRows = getSelectedRows(),
       
   603 				targetRow = selectedRows[before ? 0 : selectedRows.length - 1],
       
   604 				targetCellCount = targetRow.cells.length;
       
   605 
       
   606 			// Nothing to paste
       
   607 			if (!rows) {
       
   608 				return;
       
   609 			}
       
   610 
       
   611 			// Calc target cell count
       
   612 			each(grid, function(row) {
       
   613 				var match;
       
   614 
       
   615 				targetCellCount = 0;
       
   616 				each(row, function(cell) {
       
   617 					if (cell.real) {
       
   618 						targetCellCount += cell.colspan;
       
   619 					}
       
   620 
       
   621 					if (cell.elm.parentNode == targetRow) {
       
   622 						match = 1;
       
   623 					}
       
   624 				});
       
   625 
       
   626 				if (match) {
       
   627 					return false;
       
   628 				}
       
   629 			});
       
   630 
       
   631 			if (!before) {
       
   632 				rows.reverse();
       
   633 			}
       
   634 
       
   635 			each(rows, function(row) {
       
   636 				var i, cellCount = row.cells.length, cell;
       
   637 
       
   638 				// Remove col/rowspans
       
   639 				for (i = 0; i < cellCount; i++) {
       
   640 					cell = row.cells[i];
       
   641 					setSpanVal(cell, 'colSpan', 1);
       
   642 					setSpanVal(cell, 'rowSpan', 1);
       
   643 				}
       
   644 
       
   645 				// Needs more cells
       
   646 				for (i = cellCount; i < targetCellCount; i++) {
       
   647 					row.appendChild(cloneCell(row.cells[cellCount - 1]));
       
   648 				}
       
   649 
       
   650 				// Needs less cells
       
   651 				for (i = targetCellCount; i < cellCount; i++) {
       
   652 					dom.remove(row.cells[i]);
       
   653 				}
       
   654 
       
   655 				// Add before/after
       
   656 				if (before) {
       
   657 					targetRow.parentNode.insertBefore(row, targetRow);
       
   658 				} else {
       
   659 					dom.insertAfter(row, targetRow);
       
   660 				}
       
   661 			});
       
   662 
       
   663 			// Remove current selection
       
   664 			dom.removeClass(dom.select('td.mce-item-selected,th.mce-item-selected'), 'mce-item-selected');
       
   665 		}
       
   666 
       
   667 		function getPos(target) {
       
   668 			var pos;
       
   669 
       
   670 			each(grid, function(row, y) {
       
   671 				each(row, function(cell, x) {
       
   672 					if (cell.elm == target) {
       
   673 						pos = {x: x, y: y};
       
   674 						return false;
       
   675 					}
       
   676 				});
       
   677 
       
   678 				return !pos;
       
   679 			});
       
   680 
       
   681 			return pos;
       
   682 		}
       
   683 
       
   684 		function setStartCell(cell) {
       
   685 			startPos = getPos(cell);
       
   686 		}
       
   687 
       
   688 		function findEndPos() {
       
   689 			var maxX, maxY;
       
   690 
       
   691 			maxX = maxY = 0;
       
   692 
       
   693 			each(grid, function(row, y) {
       
   694 				each(row, function(cell, x) {
       
   695 					var colSpan, rowSpan;
       
   696 
       
   697 					if (isCellSelected(cell)) {
       
   698 						cell = grid[y][x];
       
   699 
       
   700 						if (x > maxX) {
       
   701 							maxX = x;
       
   702 						}
       
   703 
       
   704 						if (y > maxY) {
       
   705 							maxY = y;
       
   706 						}
       
   707 
       
   708 						if (cell.real) {
       
   709 							colSpan = cell.colspan - 1;
       
   710 							rowSpan = cell.rowspan - 1;
       
   711 
       
   712 							if (colSpan) {
       
   713 								if (x + colSpan > maxX) {
       
   714 									maxX = x + colSpan;
       
   715 								}
       
   716 							}
       
   717 
       
   718 							if (rowSpan) {
       
   719 								if (y + rowSpan > maxY) {
       
   720 									maxY = y + rowSpan;
       
   721 								}
       
   722 							}
       
   723 						}
       
   724 					}
       
   725 				});
       
   726 			});
       
   727 
       
   728 			return {x: maxX, y: maxY};
       
   729 		}
       
   730 
       
   731 		function setEndCell(cell) {
       
   732 			var startX, startY, endX, endY, maxX, maxY, colSpan, rowSpan, x, y;
       
   733 
       
   734 			endPos = getPos(cell);
       
   735 
       
   736 			if (startPos && endPos) {
       
   737 				// Get start/end positions
       
   738 				startX = Math.min(startPos.x, endPos.x);
       
   739 				startY = Math.min(startPos.y, endPos.y);
       
   740 				endX = Math.max(startPos.x, endPos.x);
       
   741 				endY = Math.max(startPos.y, endPos.y);
       
   742 
       
   743 				// Expand end positon to include spans
       
   744 				maxX = endX;
       
   745 				maxY = endY;
       
   746 
       
   747 				// Expand startX
       
   748 				for (y = startY; y <= maxY; y++) {
       
   749 					cell = grid[y][startX];
       
   750 
       
   751 					if (!cell.real) {
       
   752 						if (startX - (cell.colspan - 1) < startX) {
       
   753 							startX -= cell.colspan - 1;
       
   754 						}
       
   755 					}
       
   756 				}
       
   757 
       
   758 				// Expand startY
       
   759 				for (x = startX; x <= maxX; x++) {
       
   760 					cell = grid[startY][x];
       
   761 
       
   762 					if (!cell.real) {
       
   763 						if (startY - (cell.rowspan - 1) < startY) {
       
   764 							startY -= cell.rowspan - 1;
       
   765 						}
       
   766 					}
       
   767 				}
       
   768 
       
   769 				// Find max X, Y
       
   770 				for (y = startY; y <= endY; y++) {
       
   771 					for (x = startX; x <= endX; x++) {
       
   772 						cell = grid[y][x];
       
   773 
       
   774 						if (cell.real) {
       
   775 							colSpan = cell.colspan - 1;
       
   776 							rowSpan = cell.rowspan - 1;
       
   777 
       
   778 							if (colSpan) {
       
   779 								if (x + colSpan > maxX) {
       
   780 									maxX = x + colSpan;
       
   781 								}
       
   782 							}
       
   783 
       
   784 							if (rowSpan) {
       
   785 								if (y + rowSpan > maxY) {
       
   786 									maxY = y + rowSpan;
       
   787 								}
       
   788 							}
       
   789 						}
       
   790 					}
       
   791 				}
       
   792 
       
   793 				// Remove current selection
       
   794 				dom.removeClass(dom.select('td.mce-item-selected,th.mce-item-selected'), 'mce-item-selected');
       
   795 
       
   796 				// Add new selection
       
   797 				for (y = startY; y <= maxY; y++) {
       
   798 					for (x = startX; x <= maxX; x++) {
       
   799 						if (grid[y][x]) {
       
   800 							dom.addClass(grid[y][x].elm, 'mce-item-selected');
       
   801 						}
       
   802 					}
       
   803 				}
       
   804 			}
       
   805 		}
       
   806 
       
   807 		function moveRelIdx(cellElm, delta) {
       
   808 			var pos, index, cell;
       
   809 
       
   810 			pos = getPos(cellElm);
       
   811 			index = pos.y * gridWidth + pos.x;
       
   812 
       
   813 			do {
       
   814 				index += delta;
       
   815 				cell = getCell(index % gridWidth, Math.floor(index / gridWidth));
       
   816 
       
   817 				if (!cell) {
       
   818 					break;
       
   819 				}
       
   820 
       
   821 				if (cell.elm != cellElm) {
       
   822 					selection.select(cell.elm, true);
       
   823 
       
   824 					if (dom.isEmpty(cell.elm)) {
       
   825 						selection.collapse(true);
       
   826 					}
       
   827 
       
   828 					return true;
       
   829 				}
       
   830 			} while (cell.elm == cellElm);
       
   831 
       
   832 			return false;
       
   833 		}
       
   834 
       
   835 		table = table || dom.getParent(selection.getStart(), 'table');
       
   836 
       
   837 		buildGrid();
       
   838 
       
   839 		selectedCell = dom.getParent(selection.getStart(), 'th,td');
       
   840 		if (selectedCell) {
       
   841 			startPos = getPos(selectedCell);
       
   842 			endPos = findEndPos();
       
   843 			selectedCell = getCell(startPos.x, startPos.y);
       
   844 		}
       
   845 
       
   846 		Tools.extend(this, {
       
   847 			deleteTable: deleteTable,
       
   848 			split: split,
       
   849 			merge: merge,
       
   850 			insertRow: insertRow,
       
   851 			insertCol: insertCol,
       
   852 			deleteCols: deleteCols,
       
   853 			deleteRows: deleteRows,
       
   854 			cutRows: cutRows,
       
   855 			copyRows: copyRows,
       
   856 			pasteRows: pasteRows,
       
   857 			getPos: getPos,
       
   858 			setStartCell: setStartCell,
       
   859 			setEndCell: setEndCell,
       
   860 			moveRelIdx: moveRelIdx,
       
   861 			refresh: buildGrid
       
   862 		});
       
   863 	};
       
   864 });