diff --git a/tests/qunit/editor/external-plugins/table/plugin.js b/tests/qunit/editor/external-plugins/table/plugin.js
deleted file mode 100644
index 729f48212a..0000000000
--- a/tests/qunit/editor/external-plugins/table/plugin.js
+++ /dev/null
@@ -1,2259 +0,0 @@
-/**
- * Compiled inline version. (Library mode)
- */
-
-/*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
-/*globals $code */
-
-(function(exports, undefined) {
- "use strict";
-
- var modules = {};
-
- function require(ids, callback) {
- var module, defs = [];
-
- for (var i = 0; i < ids.length; ++i) {
- module = modules[ids[i]] || resolve(ids[i]);
- if (!module) {
- throw 'module definition dependecy not found: ' + ids[i];
- }
-
- defs.push(module);
- }
-
- callback.apply(null, defs);
- }
-
- function define(id, dependencies, definition) {
- if (typeof id !== 'string') {
- throw 'invalid module definition, module id must be defined and be a string';
- }
-
- if (dependencies === undefined) {
- throw 'invalid module definition, dependencies must be specified';
- }
-
- if (definition === undefined) {
- throw 'invalid module definition, definition function must be specified';
- }
-
- require(dependencies, function() {
- modules[id] = definition.apply(null, arguments);
- });
- }
-
- function defined(id) {
- return !!modules[id];
- }
-
- function resolve(id) {
- var target = exports;
- var fragments = id.split(/[.\/]/);
-
- for (var fi = 0; fi < fragments.length; ++fi) {
- if (!target[fragments[fi]]) {
- return;
- }
-
- target = target[fragments[fi]];
- }
-
- return target;
- }
-
- function expose(ids) {
- for (var i = 0; i < ids.length; i++) {
- var target = exports;
- var id = ids[i];
- var fragments = id.split(/[.\/]/);
-
- for (var fi = 0; fi < fragments.length - 1; ++fi) {
- if (target[fragments[fi]] === undefined) {
- target[fragments[fi]] = {};
- }
-
- target = target[fragments[fi]];
- }
-
- target[fragments[fragments.length - 1]] = modules[id];
- }
- }
-
-// Included from: js/tinymce/plugins/table/classes/TableGrid.js
-
-/**
- * TableGrid.js
- *
- * Copyright, Moxiecode Systems AB
- * Released under LGPL License.
- *
- * License: http://www.tinymce.com/license
- * Contributing: http://www.tinymce.com/contributing
- */
-
-/**
- * This class creates a grid out of a table element. This
- * makes it a whole lot easier to handle complex tables with
- * col/row spans.
- *
- * @class tinymce.tableplugin.TableGrid
- * @private
- */
-define("tinymce/tableplugin/TableGrid", [
- "tinymce/util/Tools",
- "tinymce/Env"
-], function(Tools, Env) {
- var each = Tools.each;
-
- function getSpanVal(td, name) {
- return parseInt(td.getAttribute(name) || 1, 10);
- }
-
- return function(editor, table) {
- var grid, startPos, endPos, selectedCell, selection = editor.selection, dom = selection.dom;
-
- function buildGrid() {
- var startY = 0;
-
- grid = [];
-
- each(['thead', 'tbody', 'tfoot'], function(part) {
- var rows = dom.select('> ' + part + ' tr', table);
-
- each(rows, function(tr, y) {
- y += startY;
-
- each(dom.select('> td, > th', tr), function(td, x) {
- var x2, y2, rowspan, colspan;
-
- // Skip over existing cells produced by rowspan
- if (grid[y]) {
- while (grid[y][x]) {
- x++;
- }
- }
-
- // Get col/rowspan from cell
- rowspan = getSpanVal(td, 'rowspan');
- colspan = getSpanVal(td, 'colspan');
-
- // Fill out rowspan/colspan right and down
- for (y2 = y; y2 < y + rowspan; y2++) {
- if (!grid[y2]) {
- grid[y2] = [];
- }
-
- for (x2 = x; x2 < x + colspan; x2++) {
- grid[y2][x2] = {
- part: part,
- real: y2 == y && x2 == x,
- elm: td,
- rowspan: rowspan,
- colspan: colspan
- };
- }
- }
- });
- });
-
- startY += rows.length;
- });
- }
-
- function cloneNode(node, children) {
- node = node.cloneNode(children);
- node.removeAttribute('id');
-
- return node;
- }
-
- function getCell(x, y) {
- var row;
-
- row = grid[y];
- if (row) {
- return row[x];
- }
- }
-
- function setSpanVal(td, name, val) {
- if (td) {
- val = parseInt(val, 10);
-
- if (val === 1) {
- td.removeAttribute(name, 1);
- } else {
- td.setAttribute(name, val, 1);
- }
- }
- }
-
- function isCellSelected(cell) {
- return cell && (dom.hasClass(cell.elm, 'mce-item-selected') || cell == selectedCell);
- }
-
- function getSelectedRows() {
- var rows = [];
-
- each(table.rows, function(row) {
- each(row.cells, function(cell) {
- if (dom.hasClass(cell, 'mce-item-selected') || (selectedCell && cell == selectedCell.elm)) {
- rows.push(row);
- return false;
- }
- });
- });
-
- return rows;
- }
-
- function deleteTable() {
- var rng = dom.createRng();
-
- rng.setStartAfter(table);
- rng.setEndAfter(table);
-
- selection.setRng(rng);
-
- dom.remove(table);
- }
-
- function cloneCell(cell) {
- var formatNode, cloneFormats = {};
-
- if (editor.settings.table_clone_elements !== false) {
- cloneFormats = Tools.makeMap(
- (editor.settings.table_clone_elements || 'strong em b i span font h1 h2 h3 h4 h5 h6 p div').toUpperCase(),
- /[ ,]/
- );
- }
-
- // Clone formats
- Tools.walk(cell, function(node) {
- var curNode;
-
- if (node.nodeType == 3) {
- each(dom.getParents(node.parentNode, null, cell).reverse(), function(node) {
- if (!cloneFormats[node.nodeName]) {
- return;
- }
-
- node = cloneNode(node, false);
-
- if (!formatNode) {
- formatNode = curNode = node;
- } else if (curNode) {
- curNode.appendChild(node);
- }
-
- curNode = node;
- });
-
- // Add something to the inner node
- if (curNode) {
- curNode.innerHTML = Env.ie ? ' ' : '
';
- }
-
- return false;
- }
- }, 'childNodes');
-
- cell = cloneNode(cell, false);
- setSpanVal(cell, 'rowSpan', 1);
- setSpanVal(cell, 'colSpan', 1);
-
- if (formatNode) {
- cell.appendChild(formatNode);
- } else {
- if (!Env.ie) {
- cell.innerHTML = '
';
- }
- }
-
- return cell;
- }
-
- function cleanup() {
- var rng = dom.createRng(), row;
-
- // Empty rows
- each(dom.select('tr', table), function(tr) {
- if (tr.cells.length === 0) {
- dom.remove(tr);
- }
- });
-
- // Empty table
- if (dom.select('tr', table).length === 0) {
- rng.setStartBefore(table);
- rng.setEndBefore(table);
- selection.setRng(rng);
- dom.remove(table);
- return;
- }
-
- // Empty header/body/footer
- each(dom.select('thead,tbody,tfoot', table), function(part) {
- if (part.rows.length === 0) {
- dom.remove(part);
- }
- });
-
- // Restore selection to start position if it still exists
- buildGrid();
-
- // If we have a valid startPos object
- if (startPos) {
- // Restore the selection to the closest table position
- row = grid[Math.min(grid.length - 1, startPos.y)];
- if (row) {
- selection.select(row[Math.min(row.length - 1, startPos.x)].elm, true);
- selection.collapse(true);
- }
- }
- }
-
- function fillLeftDown(x, y, rows, cols) {
- var tr, x2, r, c, cell;
-
- tr = grid[y][x].elm.parentNode;
- for (r = 1; r <= rows; r++) {
- tr = dom.getNext(tr, 'tr');
-
- if (tr) {
- // Loop left to find real cell
- for (x2 = x; x2 >= 0; x2--) {
- cell = grid[y + r][x2].elm;
-
- if (cell.parentNode == tr) {
- // Append clones after
- for (c = 1; c <= cols; c++) {
- dom.insertAfter(cloneCell(cell), cell);
- }
-
- break;
- }
- }
-
- if (x2 == -1) {
- // Insert nodes before first cell
- for (c = 1; c <= cols; c++) {
- tr.insertBefore(cloneCell(tr.cells[0]), tr.cells[0]);
- }
- }
- }
- }
- }
-
- function split() {
- each(grid, function(row, y) {
- each(row, function(cell, x) {
- var colSpan, rowSpan, i;
-
- if (isCellSelected(cell)) {
- cell = cell.elm;
- colSpan = getSpanVal(cell, 'colspan');
- rowSpan = getSpanVal(cell, 'rowspan');
-
- if (colSpan > 1 || rowSpan > 1) {
- setSpanVal(cell, 'rowSpan', 1);
- setSpanVal(cell, 'colSpan', 1);
-
- // Insert cells right
- for (i = 0; i < colSpan - 1; i++) {
- dom.insertAfter(cloneCell(cell), cell);
- }
-
- fillLeftDown(x, y, rowSpan - 1, colSpan);
- }
- }
- });
- });
- }
-
- function merge(cell, cols, rows) {
- var pos, startX, startY, endX, endY, x, y, startCell, endCell, children, count;
-
- // Use specified cell and cols/rows
- if (cell) {
- pos = getPos(cell);
- startX = pos.x;
- startY = pos.y;
- endX = startX + (cols - 1);
- endY = startY + (rows - 1);
- } else {
- startPos = endPos = null;
-
- // Calculate start/end pos by checking for selected cells in grid works better with context menu
- each(grid, function(row, y) {
- each(row, function(cell, x) {
- if (isCellSelected(cell)) {
- if (!startPos) {
- startPos = {x: x, y: y};
- }
-
- endPos = {x: x, y: y};
- }
- });
- });
-
- // Use selection, but make sure startPos is valid before accessing
- if (startPos) {
- startX = startPos.x;
- startY = startPos.y;
- endX = endPos.x;
- endY = endPos.y;
- }
- }
-
- // Find start/end cells
- startCell = getCell(startX, startY);
- endCell = getCell(endX, endY);
-
- // Check if the cells exists and if they are of the same part for example tbody = tbody
- if (startCell && endCell && startCell.part == endCell.part) {
- // Split and rebuild grid
- split();
- buildGrid();
-
- // Set row/col span to start cell
- startCell = getCell(startX, startY).elm;
- setSpanVal(startCell, 'colSpan', (endX - startX) + 1);
- setSpanVal(startCell, 'rowSpan', (endY - startY) + 1);
-
- // Remove other cells and add it's contents to the start cell
- for (y = startY; y <= endY; y++) {
- for (x = startX; x <= endX; x++) {
- if (!grid[y] || !grid[y][x]) {
- continue;
- }
-
- cell = grid[y][x].elm;
-
- /*jshint loopfunc:true */
- if (cell != startCell) {
- // Move children to startCell
- children = Tools.grep(cell.childNodes);
- each(children, function(node) {
- startCell.appendChild(node);
- });
-
- // Remove bogus nodes if there is children in the target cell
- if (children.length) {
- children = Tools.grep(startCell.childNodes);
- count = 0;
- each(children, function(node) {
- if (node.nodeName == 'BR' && dom.getAttrib(node, 'data-mce-bogus') && count++ < children.length - 1) {
- startCell.removeChild(node);
- }
- });
- }
-
- dom.remove(cell);
- }
- }
- }
-
- // Remove empty rows etc and restore caret location
- cleanup();
- }
- }
-
- function insertRow(before) {
- var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell, rowSpan;
-
- // Find first/last row
- each(grid, function(row, y) {
- each(row, function(cell) {
- if (isCellSelected(cell)) {
- cell = cell.elm;
- rowElm = cell.parentNode;
- newRow = cloneNode(rowElm, false);
- posY = y;
-
- if (before) {
- return false;
- }
- }
- });
-
- if (before) {
- return !posY;
- }
- });
-
- // If posY is undefined there is nothing for us to do here...just return to avoid crashing below
- if (posY === undefined) {
- return;
- }
-
- for (x = 0; x < grid[0].length; x++) {
- // Cell not found could be because of an invalid table structure
- if (!grid[posY][x]) {
- continue;
- }
-
- cell = grid[posY][x].elm;
-
- if (cell != lastCell) {
- if (!before) {
- rowSpan = getSpanVal(cell, 'rowspan');
- if (rowSpan > 1) {
- setSpanVal(cell, 'rowSpan', rowSpan + 1);
- continue;
- }
- } else {
- // Check if cell above can be expanded
- if (posY > 0 && grid[posY - 1][x]) {
- otherCell = grid[posY - 1][x].elm;
- rowSpan = getSpanVal(otherCell, 'rowSpan');
- if (rowSpan > 1) {
- setSpanVal(otherCell, 'rowSpan', rowSpan + 1);
- continue;
- }
- }
- }
-
- // Insert new cell into new row
- newCell = cloneCell(cell);
- setSpanVal(newCell, 'colSpan', cell.colSpan);
-
- newRow.appendChild(newCell);
-
- lastCell = cell;
- }
- }
-
- if (newRow.hasChildNodes()) {
- if (!before) {
- dom.insertAfter(newRow, rowElm);
- } else {
- rowElm.parentNode.insertBefore(newRow, rowElm);
- }
- }
- }
-
- function insertCol(before) {
- var posX, lastCell;
-
- // Find first/last column
- each(grid, function(row) {
- each(row, function(cell, x) {
- if (isCellSelected(cell)) {
- posX = x;
-
- if (before) {
- return false;
- }
- }
- });
-
- if (before) {
- return !posX;
- }
- });
-
- each(grid, function(row, y) {
- var cell, rowSpan, colSpan;
-
- if (!row[posX]) {
- return;
- }
-
- cell = row[posX].elm;
- if (cell != lastCell) {
- colSpan = getSpanVal(cell, 'colspan');
- rowSpan = getSpanVal(cell, 'rowspan');
-
- if (colSpan == 1) {
- if (!before) {
- dom.insertAfter(cloneCell(cell), cell);
- fillLeftDown(posX, y, rowSpan - 1, colSpan);
- } else {
- cell.parentNode.insertBefore(cloneCell(cell), cell);
- fillLeftDown(posX, y, rowSpan - 1, colSpan);
- }
- } else {
- setSpanVal(cell, 'colSpan', cell.colSpan + 1);
- }
-
- lastCell = cell;
- }
- });
- }
-
- function deleteCols() {
- var cols = [];
-
- // Get selected column indexes
- each(grid, function(row) {
- each(row, function(cell, x) {
- if (isCellSelected(cell) && Tools.inArray(cols, x) === -1) {
- each(grid, function(row) {
- var cell = row[x].elm, colSpan;
-
- colSpan = getSpanVal(cell, 'colSpan');
-
- if (colSpan > 1) {
- setSpanVal(cell, 'colSpan', colSpan - 1);
- } else {
- dom.remove(cell);
- }
- });
-
- cols.push(x);
- }
- });
- });
-
- cleanup();
- }
-
- function deleteRows() {
- var rows;
-
- function deleteRow(tr) {
- var nextTr, pos, lastCell;
-
- nextTr = dom.getNext(tr, 'tr');
-
- // Move down row spanned cells
- each(tr.cells, function(cell) {
- var rowSpan = getSpanVal(cell, 'rowSpan');
-
- if (rowSpan > 1) {
- setSpanVal(cell, 'rowSpan', rowSpan - 1);
- pos = getPos(cell);
- fillLeftDown(pos.x, pos.y, 1, 1);
- }
- });
-
- // Delete cells
- pos = getPos(tr.cells[0]);
- each(grid[pos.y], function(cell) {
- var rowSpan;
-
- cell = cell.elm;
-
- if (cell != lastCell) {
- rowSpan = getSpanVal(cell, 'rowSpan');
-
- if (rowSpan <= 1) {
- dom.remove(cell);
- } else {
- setSpanVal(cell, 'rowSpan', rowSpan - 1);
- }
-
- lastCell = cell;
- }
- });
- }
-
- // Get selected rows and move selection out of scope
- rows = getSelectedRows();
-
- // Delete all selected rows
- each(rows.reverse(), function(tr) {
- deleteRow(tr);
- });
-
- cleanup();
- }
-
- function cutRows() {
- var rows = getSelectedRows();
-
- dom.remove(rows);
- cleanup();
-
- return rows;
- }
-
- function copyRows() {
- var rows = getSelectedRows();
-
- each(rows, function(row, i) {
- rows[i] = cloneNode(row, true);
- });
-
- return rows;
- }
-
- function pasteRows(rows, before) {
- var selectedRows = getSelectedRows(),
- targetRow = selectedRows[before ? 0 : selectedRows.length - 1],
- targetCellCount = targetRow.cells.length;
-
- // Nothing to paste
- if (!rows) {
- return;
- }
-
- // Calc target cell count
- each(grid, function(row) {
- var match;
-
- targetCellCount = 0;
- each(row, function(cell) {
- if (cell.real) {
- targetCellCount += cell.colspan;
- }
-
- if (cell.elm.parentNode == targetRow) {
- match = 1;
- }
- });
-
- if (match) {
- return false;
- }
- });
-
- if (!before) {
- rows.reverse();
- }
-
- each(rows, function(row) {
- var i, cellCount = row.cells.length, cell;
-
- // Remove col/rowspans
- for (i = 0; i < cellCount; i++) {
- cell = row.cells[i];
- setSpanVal(cell, 'colSpan', 1);
- setSpanVal(cell, 'rowSpan', 1);
- }
-
- // Needs more cells
- for (i = cellCount; i < targetCellCount; i++) {
- row.appendChild(cloneCell(row.cells[cellCount - 1]));
- }
-
- // Needs less cells
- for (i = targetCellCount; i < cellCount; i++) {
- dom.remove(row.cells[i]);
- }
-
- // Add before/after
- if (before) {
- targetRow.parentNode.insertBefore(row, targetRow);
- } else {
- dom.insertAfter(row, targetRow);
- }
- });
-
- // Remove current selection
- dom.removeClass(dom.select('td.mce-item-selected,th.mce-item-selected'), 'mce-item-selected');
- }
-
- function getPos(target) {
- var pos;
-
- each(grid, function(row, y) {
- each(row, function(cell, x) {
- if (cell.elm == target) {
- pos = {x : x, y : y};
- return false;
- }
- });
-
- return !pos;
- });
-
- return pos;
- }
-
- function setStartCell(cell) {
- startPos = getPos(cell);
- }
-
- function findEndPos() {
- var maxX, maxY;
-
- maxX = maxY = 0;
-
- each(grid, function(row, y) {
- each(row, function(cell, x) {
- var colSpan, rowSpan;
-
- if (isCellSelected(cell)) {
- cell = grid[y][x];
-
- if (x > maxX) {
- maxX = x;
- }
-
- if (y > maxY) {
- maxY = y;
- }
-
- if (cell.real) {
- colSpan = cell.colspan - 1;
- rowSpan = cell.rowspan - 1;
-
- if (colSpan) {
- if (x + colSpan > maxX) {
- maxX = x + colSpan;
- }
- }
-
- if (rowSpan) {
- if (y + rowSpan > maxY) {
- maxY = y + rowSpan;
- }
- }
- }
- }
- });
- });
-
- return {x : maxX, y : maxY};
- }
-
- function setEndCell(cell) {
- var startX, startY, endX, endY, maxX, maxY, colSpan, rowSpan, x, y;
-
- endPos = getPos(cell);
-
- if (startPos && endPos) {
- // Get start/end positions
- startX = Math.min(startPos.x, endPos.x);
- startY = Math.min(startPos.y, endPos.y);
- endX = Math.max(startPos.x, endPos.x);
- endY = Math.max(startPos.y, endPos.y);
-
- // Expand end positon to include spans
- maxX = endX;
- maxY = endY;
-
- // Expand startX
- for (y = startY; y <= maxY; y++) {
- cell = grid[y][startX];
-
- if (!cell.real) {
- if (startX - (cell.colspan - 1) < startX) {
- startX -= cell.colspan - 1;
- }
- }
- }
-
- // Expand startY
- for (x = startX; x <= maxX; x++) {
- cell = grid[startY][x];
-
- if (!cell.real) {
- if (startY - (cell.rowspan - 1) < startY) {
- startY -= cell.rowspan - 1;
- }
- }
- }
-
- // Find max X, Y
- for (y = startY; y <= endY; y++) {
- for (x = startX; x <= endX; x++) {
- cell = grid[y][x];
-
- if (cell.real) {
- colSpan = cell.colspan - 1;
- rowSpan = cell.rowspan - 1;
-
- if (colSpan) {
- if (x + colSpan > maxX) {
- maxX = x + colSpan;
- }
- }
-
- if (rowSpan) {
- if (y + rowSpan > maxY) {
- maxY = y + rowSpan;
- }
- }
- }
- }
- }
-
- // Remove current selection
- dom.removeClass(dom.select('td.mce-item-selected,th.mce-item-selected'), 'mce-item-selected');
-
- // Add new selection
- for (y = startY; y <= maxY; y++) {
- for (x = startX; x <= maxX; x++) {
- if (grid[y][x]) {
- dom.addClass(grid[y][x].elm, 'mce-item-selected');
- }
- }
- }
- }
- }
-
- table = table || dom.getParent(selection.getStart(), 'table');
-
- buildGrid();
-
- selectedCell = dom.getParent(selection.getStart(), 'th,td');
- if (selectedCell) {
- startPos = getPos(selectedCell);
- endPos = findEndPos();
- selectedCell = getCell(startPos.x, startPos.y);
- }
-
- Tools.extend(this, {
- deleteTable: deleteTable,
- split: split,
- merge: merge,
- insertRow: insertRow,
- insertCol: insertCol,
- deleteCols: deleteCols,
- deleteRows: deleteRows,
- cutRows: cutRows,
- copyRows: copyRows,
- pasteRows: pasteRows,
- getPos: getPos,
- setStartCell: setStartCell,
- setEndCell: setEndCell
- });
- };
-});
-
-// Included from: js/tinymce/plugins/table/classes/Quirks.js
-
-/**
- * Quirks.js
- *
- * Copyright, Moxiecode Systems AB
- * Released under LGPL License.
- *
- * License: http://www.tinymce.com/license
- * Contributing: http://www.tinymce.com/contributing
- */
-
-/**
- * This class includes fixes for various browser quirks.
- *
- * @class tinymce.tableplugin.Quirks
- * @private
- */
-define("tinymce/tableplugin/Quirks", [
- "tinymce/util/VK",
- "tinymce/Env",
- "tinymce/util/Tools"
-], function(VK, Env, Tools) {
- var each = Tools.each;
-
- function getSpanVal(td, name) {
- return parseInt(td.getAttribute(name) || 1, 10);
- }
-
- return function(editor) {
- /**
- * Fixed caret movement around tables on WebKit.
- */
- function moveWebKitSelection() {
- function eventHandler(e) {
- var key = e.keyCode;
-
- function handle(upBool, sourceNode) {
- var siblingDirection = upBool ? 'previousSibling' : 'nextSibling';
- var currentRow = editor.dom.getParent(sourceNode, 'tr');
- var siblingRow = currentRow[siblingDirection];
-
- if (siblingRow) {
- moveCursorToRow(editor, sourceNode, siblingRow, upBool);
- e.preventDefault();
- return true;
- } else {
- var tableNode = editor.dom.getParent(currentRow, 'table');
- var middleNode = currentRow.parentNode;
- var parentNodeName = middleNode.nodeName.toLowerCase();
- if (parentNodeName === 'tbody' || parentNodeName === (upBool ? 'tfoot' : 'thead')) {
- var targetParent = getTargetParent(upBool, tableNode, middleNode, 'tbody');
- if (targetParent !== null) {
- return moveToRowInTarget(upBool, targetParent, sourceNode);
- }
- }
- return escapeTable(upBool, currentRow, siblingDirection, tableNode);
- }
- }
-
- function getTargetParent(upBool, topNode, secondNode, nodeName) {
- var tbodies = editor.dom.select('>' + nodeName, topNode);
- var position = tbodies.indexOf(secondNode);
- if (upBool && position === 0 || !upBool && position === tbodies.length - 1) {
- return getFirstHeadOrFoot(upBool, topNode);
- } else if (position === -1) {
- var topOrBottom = secondNode.tagName.toLowerCase() === 'thead' ? 0 : tbodies.length - 1;
- return tbodies[topOrBottom];
- } else {
- return tbodies[position + (upBool ? -1 : 1)];
- }
- }
-
- function getFirstHeadOrFoot(upBool, parent) {
- var tagName = upBool ? 'thead' : 'tfoot';
- var headOrFoot = editor.dom.select('>' + tagName, parent);
- return headOrFoot.length !== 0 ? headOrFoot[0] : null;
- }
-
- function moveToRowInTarget(upBool, targetParent, sourceNode) {
- var targetRow = getChildForDirection(targetParent, upBool);
-
- if (targetRow) {
- moveCursorToRow(editor, sourceNode, targetRow, upBool);
- }
-
- e.preventDefault();
- return true;
- }
-
- function escapeTable(upBool, currentRow, siblingDirection, table) {
- var tableSibling = table[siblingDirection];
-
- if (tableSibling) {
- moveCursorToStartOfElement(tableSibling);
- return true;
- } else {
- var parentCell = editor.dom.getParent(table, 'td,th');
- if (parentCell) {
- return handle(upBool, parentCell, e);
- } else {
- var backUpSibling = getChildForDirection(currentRow, !upBool);
- moveCursorToStartOfElement(backUpSibling);
- e.preventDefault();
- return false;
- }
- }
- }
-
- function getChildForDirection(parent, up) {
- var child = parent && parent[up ? 'lastChild' : 'firstChild'];
- // BR is not a valid table child to return in this case we return the table cell
- return child && child.nodeName === 'BR' ? editor.dom.getParent(child, 'td,th') : child;
- }
-
- function moveCursorToStartOfElement(n) {
- editor.selection.setCursorLocation(n, 0);
- }
-
- function isVerticalMovement() {
- return key == VK.UP || key == VK.DOWN;
- }
-
- function isInTable(editor) {
- var node = editor.selection.getNode();
- var currentRow = editor.dom.getParent(node, 'tr');
- return currentRow !== null;
- }
-
- function columnIndex(column) {
- var colIndex = 0;
- var c = column;
- while (c.previousSibling) {
- c = c.previousSibling;
- colIndex = colIndex + getSpanVal(c, "colspan");
- }
- return colIndex;
- }
-
- function findColumn(rowElement, columnIndex) {
- var c = 0, r = 0;
-
- each(rowElement.children, function(cell, i) {
- c = c + getSpanVal(cell, "colspan");
- r = i;
- if (c > columnIndex) {
- return false;
- }
- });
- return r;
- }
-
- function moveCursorToRow(ed, node, row, upBool) {
- var srcColumnIndex = columnIndex(editor.dom.getParent(node, 'td,th'));
- var tgtColumnIndex = findColumn(row, srcColumnIndex);
- var tgtNode = row.childNodes[tgtColumnIndex];
- var rowCellTarget = getChildForDirection(tgtNode, upBool);
- moveCursorToStartOfElement(rowCellTarget || tgtNode);
- }
-
- function shouldFixCaret(preBrowserNode) {
- var newNode = editor.selection.getNode();
- var newParent = editor.dom.getParent(newNode, 'td,th');
- var oldParent = editor.dom.getParent(preBrowserNode, 'td,th');
-
- return newParent && newParent !== oldParent && checkSameParentTable(newParent, oldParent);
- }
-
- function checkSameParentTable(nodeOne, NodeTwo) {
- return editor.dom.getParent(nodeOne, 'TABLE') === editor.dom.getParent(NodeTwo, 'TABLE');
- }
-
- if (isVerticalMovement() && isInTable(editor)) {
- var preBrowserNode = editor.selection.getNode();
- setTimeout(function() {
- if (shouldFixCaret(preBrowserNode)) {
- handle(!e.shiftKey && key === VK.UP, preBrowserNode, e);
- }
- }, 0);
- }
- }
-
- editor.on('KeyDown', function(e) {
- eventHandler(e);
- });
- }
-
- function fixBeforeTableCaretBug() {
- // Checks if the selection/caret is at the start of the specified block element
- function isAtStart(rng, par) {
- var doc = par.ownerDocument, rng2 = doc.createRange(), elm;
-
- rng2.setStartBefore(par);
- rng2.setEnd(rng.endContainer, rng.endOffset);
-
- elm = doc.createElement('body');
- elm.appendChild(rng2.cloneContents());
-
- // Check for text characters of other elements that should be treated as content
- return elm.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi, '-').replace(/<[^>]+>/g, '').length === 0;
- }
-
- // Fixes an bug where it's impossible to place the caret before a table in Gecko
- // this fix solves it by detecting when the caret is at the beginning of such a table
- // and then manually moves the caret infront of the table
- editor.on('KeyDown', function(e) {
- var rng, table, dom = editor.dom;
-
- // On gecko it's not possible to place the caret before a table
- if (e.keyCode == 37 || e.keyCode == 38) {
- rng = editor.selection.getRng();
- table = dom.getParent(rng.startContainer, 'table');
-
- if (table && editor.getBody().firstChild == table) {
- if (isAtStart(rng, table)) {
- rng = dom.createRng();
-
- rng.setStartBefore(table);
- rng.setEndBefore(table);
-
- editor.selection.setRng(rng);
-
- e.preventDefault();
- }
- }
- }
- });
- }
-
- // Fixes an issue on Gecko where it's impossible to place the caret behind a table
- // This fix will force a paragraph element after the table but only when the forced_root_block setting is enabled
- function fixTableCaretPos() {
- editor.on('KeyDown SetContent VisualAid', function() {
- var last;
-
- // Skip empty text nodes from the end
- for (last = editor.getBody().lastChild; last; last = last.previousSibling) {
- if (last.nodeType == 3) {
- if (last.nodeValue.length > 0) {
- break;
- }
- } else if (last.nodeType == 1 && !last.getAttribute('data-mce-bogus')) {
- break;
- }
- }
-
- if (last && last.nodeName == 'TABLE') {
- if (editor.settings.forced_root_block) {
- editor.dom.add(
- editor.getBody(),
- editor.settings.forced_root_block,
- editor.settings.forced_root_block_attrs,
- Env.ie && Env.ie < 11 ? ' ' : '
'
- );
- } else {
- editor.dom.add(editor.getBody(), 'br', {'data-mce-bogus': '1'});
- }
- }
- });
-
- editor.on('PreProcess', function(o) {
- var last = o.node.lastChild;
-
- if (last && (last.nodeName == "BR" || (last.childNodes.length == 1 &&
- (last.firstChild.nodeName == 'BR' || last.firstChild.nodeValue == '\u00a0'))) &&
- last.previousSibling && last.previousSibling.nodeName == "TABLE") {
- editor.dom.remove(last);
- }
- });
- }
-
- // this nasty hack is here to work around some WebKit selection bugs.
- function fixTableCellSelection() {
- function tableCellSelected(ed, rng, n, currentCell) {
- // The decision of when a table cell is selected is somewhat involved. The fact that this code is
- // required is actually a pointer to the root cause of this bug. A cell is selected when the start
- // and end offsets are 0, the start container is a text, and the selection node is either a TR (most cases)
- // or the parent of the table (in the case of the selection containing the last cell of a table).
- var TEXT_NODE = 3, table = ed.dom.getParent(rng.startContainer, 'TABLE');
- var tableParent, allOfCellSelected, tableCellSelection;
-
- if (table) {
- tableParent = table.parentNode;
- }
-
- allOfCellSelected =rng.startContainer.nodeType == TEXT_NODE &&
- rng.startOffset === 0 &&
- rng.endOffset === 0 &&
- currentCell &&
- (n.nodeName == "TR" || n == tableParent);
-
- tableCellSelection = (n.nodeName == "TD" || n.nodeName == "TH") && !currentCell;
-
- return allOfCellSelected || tableCellSelection;
- }
-
- function fixSelection() {
- var rng = editor.selection.getRng();
- var n = editor.selection.getNode();
- var currentCell = editor.dom.getParent(rng.startContainer, 'TD,TH');
-
- if (!tableCellSelected(editor, rng, n, currentCell)) {
- return;
- }
-
- if (!currentCell) {
- currentCell=n;
- }
-
- // Get the very last node inside the table cell
- var end = currentCell.lastChild;
- while (end.lastChild) {
- end = end.lastChild;
- }
-
- // Select the entire table cell. Nothing outside of the table cell should be selected.
- rng.setEnd(end, end.nodeValue.length);
- editor.selection.setRng(rng);
- }
-
- editor.on('KeyDown', function() {
- fixSelection();
- });
-
- editor.on('MouseDown', function(e) {
- if (e.button != 2) {
- fixSelection();
- }
- });
- }
-
- /**
- * Delete table if all cells are selected.
- */
- function deleteTable() {
- editor.on('keydown', function(e) {
- if ((e.keyCode == VK.DELETE || e.keyCode == VK.BACKSPACE) && !e.isDefaultPrevented()) {
- var table = editor.dom.getParent(editor.selection.getStart(), 'table');
-
- if (table) {
- var cells = editor.dom.select('td,th', table), i = cells.length;
- while (i--) {
- if (!editor.dom.hasClass(cells[i], 'mce-item-selected')) {
- return;
- }
- }
-
- e.preventDefault();
- editor.execCommand('mceTableDelete');
- }
- }
- });
- }
-
- deleteTable();
-
- if (Env.webkit) {
- moveWebKitSelection();
- fixTableCellSelection();
- }
-
- if (Env.gecko) {
- fixBeforeTableCaretBug();
- fixTableCaretPos();
- }
-
- if (Env.ie > 10) {
- fixBeforeTableCaretBug();
- fixTableCaretPos();
- }
- };
-});
-
-// Included from: js/tinymce/plugins/table/classes/CellSelection.js
-
-/**
- * CellSelection.js
- *
- * Copyright, Moxiecode Systems AB
- * Released under LGPL License.
- *
- * License: http://www.tinymce.com/license
- * Contributing: http://www.tinymce.com/contributing
- */
-
-/**
- * This class handles table cell selection by faking it using a css class that gets applied
- * to cells when dragging the mouse from one cell to another.
- *
- * @class tinymce.tableplugin.CellSelection
- * @private
- */
-define("tinymce/tableplugin/CellSelection", [
- "tinymce/tableplugin/TableGrid",
- "tinymce/dom/TreeWalker",
- "tinymce/util/Tools"
-], function(TableGrid, TreeWalker, Tools) {
- return function(editor) {
- var dom = editor.dom, tableGrid, startCell, startTable, hasCellSelection = true;
-
- function clear() {
- // Restore selection possibilities
- editor.getBody().style.webkitUserSelect = '';
-
- if (hasCellSelection) {
- editor.dom.removeClass(
- editor.dom.select('td.mce-item-selected,th.mce-item-selected'),
- 'mce-item-selected'
- );
-
- hasCellSelection = false;
- }
- }
-
- function cellSelectionHandler(e) {
- var sel, table, target = e.target;
-
- if (startCell && (tableGrid || target != startCell) && (target.nodeName == 'TD' || target.nodeName == 'TH')) {
- table = dom.getParent(target, 'table');
- if (table == startTable) {
- if (!tableGrid) {
- tableGrid = new TableGrid(editor, table);
- tableGrid.setStartCell(startCell);
-
- editor.getBody().style.webkitUserSelect = 'none';
- }
-
- tableGrid.setEndCell(target);
- hasCellSelection = true;
- }
-
- // Remove current selection
- sel = editor.selection.getSel();
-
- try {
- if (sel.removeAllRanges) {
- sel.removeAllRanges();
- } else {
- sel.empty();
- }
- } catch (ex) {
- // IE9 might throw errors here
- }
-
- e.preventDefault();
- }
- }
-
- // Add cell selection logic
- editor.on('MouseDown', function(e) {
- if (e.button != 2) {
- clear();
-
- startCell = dom.getParent(e.target, 'td,th');
- startTable = dom.getParent(startCell, 'table');
- }
- });
-
- dom.bind(editor.getDoc(), 'mouseover', cellSelectionHandler);
-
- editor.on('remove', function() {
- dom.unbind(editor.getDoc(), 'mouseover', cellSelectionHandler);
- });
-
- editor.on('MouseUp', function() {
- var rng, sel = editor.selection, selectedCells, walker, node, lastNode, endNode;
-
- function setPoint(node, start) {
- var walker = new TreeWalker(node, node);
-
- do {
- // Text node
- if (node.nodeType == 3 && Tools.trim(node.nodeValue).length !== 0) {
- if (start) {
- rng.setStart(node, 0);
- } else {
- rng.setEnd(node, node.nodeValue.length);
- }
-
- return;
- }
-
- // BR element
- if (node.nodeName == 'BR') {
- if (start) {
- rng.setStartBefore(node);
- } else {
- rng.setEndBefore(node);
- }
-
- return;
- }
- } while ((node = (start ? walker.next() : walker.prev())));
- }
-
- // Move selection to startCell
- if (startCell) {
- if (tableGrid) {
- editor.getBody().style.webkitUserSelect = '';
- }
-
- // Try to expand text selection as much as we can only Gecko supports cell selection
- selectedCells = dom.select('td.mce-item-selected,th.mce-item-selected');
- if (selectedCells.length > 0) {
- rng = dom.createRng();
- node = selectedCells[0];
- endNode = selectedCells[selectedCells.length - 1];
- rng.setStartBefore(node);
- rng.setEndAfter(node);
-
- setPoint(node, 1);
- walker = new TreeWalker(node, dom.getParent(selectedCells[0], 'table'));
-
- do {
- if (node.nodeName == 'TD' || node.nodeName == 'TH') {
- if (!dom.hasClass(node, 'mce-item-selected')) {
- break;
- }
-
- lastNode = node;
- }
- } while ((node = walker.next()));
-
- setPoint(lastNode);
-
- sel.setRng(rng);
- }
-
- editor.nodeChanged();
- startCell = tableGrid = startTable = null;
- }
- });
-
- editor.on('KeyUp', function() {
- clear();
- });
-
- return {
- clear: clear
- };
- };
-});
-
-// Included from: js/tinymce/plugins/table/classes/Plugin.js
-
-/**
- * Plugin.js
- *
- * Copyright, Moxiecode Systems AB
- * Released under LGPL License.
- *
- * License: http://www.tinymce.com/license
- * Contributing: http://www.tinymce.com/contributing
- */
-
-/**
- * This class contains all core logic for the table plugin.
- *
- * @class tinymce.tableplugin.Plugin
- * @private
- */
-define("tinymce/tableplugin/Plugin", [
- "tinymce/tableplugin/TableGrid",
- "tinymce/tableplugin/Quirks",
- "tinymce/tableplugin/CellSelection",
- "tinymce/util/Tools",
- "tinymce/dom/TreeWalker",
- "tinymce/Env",
- "tinymce/PluginManager"
-], function(TableGrid, Quirks, CellSelection, Tools, TreeWalker, Env, PluginManager) {
- var each = Tools.each;
-
- function Plugin(editor) {
- var winMan, clipboardRows, self = this; // Might be selected cells on reload
-
- function removePxSuffix(size) {
- return size ? size.replace(/px$/, '') : "";
- }
-
- function addSizeSuffix(size) {
- if (/^[0-9]+$/.test(size)) {
- size += "px";
- }
-
- return size;
- }
-
- function unApplyAlign(elm) {
- each('left center right'.split(' '), function(name) {
- editor.formatter.remove('align' + name, {}, elm);
- });
- }
-
- function tableDialog() {
- var dom = editor.dom, tableElm, data;
-
- tableElm = dom.getParent(editor.selection.getStart(), 'table');
-
- data = {
- width: removePxSuffix(dom.getStyle(tableElm, 'width') || dom.getAttrib(tableElm, 'width')),
- height: removePxSuffix(dom.getStyle(tableElm, 'height') || dom.getAttrib(tableElm, 'height')),
- cellspacing: dom.getAttrib(tableElm, 'cellspacing'),
- cellpadding: dom.getAttrib(tableElm, 'cellpadding'),
- border: dom.getAttrib(tableElm, 'border'),
- caption: !!dom.select('caption', tableElm)[0]
- };
-
- each('left center right'.split(' '), function(name) {
- if (editor.formatter.matchNode(tableElm, 'align' + name)) {
- data.align = name;
- }
- });
-
- editor.windowManager.open({
- title: "Table properties",
- items: {
- type: 'form',
- layout: 'grid',
- columns: 2,
- data: data,
- defaults: {
- type: 'textbox',
- maxWidth: 50
- },
- items: [
- {label: 'Width', name: 'width'},
- {label: 'Height', name: 'height'},
- {label: 'Cell spacing', name: 'cellspacing'},
- {label: 'Cell padding', name: 'cellpadding'},
- {label: 'Border', name: 'border'},
- {label: 'Caption', name: 'caption', type: 'checkbox'},
- {
- label: 'Alignment',
- minWidth: 90,
- name: 'align',
- type: 'listbox',
- text: 'None',
- maxWidth: null,
- values: [
- {text: 'None', value: ''},
- {text: 'Left', value: 'left'},
- {text: 'Center', value: 'center'},
- {text: 'Right', value: 'right'}
- ]
- }
- ]
- },
-
- onsubmit: function() {
- var data = this.toJSON(), captionElm;
-
- editor.undoManager.transact(function() {
- editor.dom.setAttribs(tableElm, {
- cellspacing: data.cellspacing,
- cellpadding: data.cellpadding,
- border: data.border
- });
-
- editor.dom.setStyles(tableElm, {
- width: addSizeSuffix(data.width),
- height: addSizeSuffix(data.height)
- });
-
- // Toggle caption on/off
- captionElm = dom.select('caption', tableElm)[0];
-
- if (captionElm && !data.caption) {
- dom.remove(captionElm);
- }
-
- if (!captionElm && data.caption) {
- captionElm = dom.create('caption');
- captionElm.innerHTML = !Env.ie ? '
' : '\u00a0';
- tableElm.insertBefore(captionElm, tableElm.firstChild);
- }
-
- unApplyAlign(tableElm);
- if (data.align) {
- editor.formatter.apply('align' + data.align, {}, tableElm);
- }
-
- editor.focus();
- editor.addVisual();
- });
- }
- });
- }
-
- function mergeDialog(grid, cell) {
- editor.windowManager.open({
- title: "Merge cells",
- body: [
- {label: 'Cols', name: 'cols', type: 'textbox', size: 10},
- {label: 'Rows', name: 'rows', type: 'textbox', size: 10}
- ],
- onsubmit: function() {
- var data = this.toJSON();
-
- editor.undoManager.transact(function() {
- grid.merge(cell, data.cols, data.rows);
- });
- }
- });
- }
-
- function cellDialog() {
- var dom = editor.dom, cellElm, data, cells = [];
-
- // Get selected cells or the current cell
- cells = editor.dom.select('td.mce-item-selected,th.mce-item-selected');
- cellElm = editor.dom.getParent(editor.selection.getStart(), 'td,th');
- if (!cells.length && cellElm) {
- cells.push(cellElm);
- }
-
- cellElm = cellElm || cells[0];
-
- if (!cellElm) {
- // If this element is null, return now to avoid crashing.
- return;
- }
-
- data = {
- width: removePxSuffix(dom.getStyle(cellElm, 'width') || dom.getAttrib(cellElm, 'width')),
- height: removePxSuffix(dom.getStyle(cellElm, 'height') || dom.getAttrib(cellElm, 'height')),
- scope: dom.getAttrib(cellElm, 'scope')
- };
-
- data.type = cellElm.nodeName.toLowerCase();
-
- each('left center right'.split(' '), function(name) {
- if (editor.formatter.matchNode(cellElm, 'align' + name)) {
- data.align = name;
- }
- });
-
- editor.windowManager.open({
- title: "Cell properties",
- items: {
- type: 'form',
- data: data,
- layout: 'grid',
- columns: 2,
- defaults: {
- type: 'textbox',
- maxWidth: 50
- },
- items: [
- {label: 'Width', name: 'width'},
- {label: 'Height', name: 'height'},
- {
- label: 'Cell type',
- name: 'type',
- type: 'listbox',
- text: 'None',
- minWidth: 90,
- maxWidth: null,
- menu: [
- {text: 'Cell', value: 'td'},
- {text: 'Header cell', value: 'th'}
- ]
- },
- {
- label: 'Scope',
- name: 'scope',
- type: 'listbox',
- text: 'None',
- minWidth: 90,
- maxWidth: null,
- menu: [
- {text: 'None', value: ''},
- {text: 'Row', value: 'row'},
- {text: 'Column', value: 'col'},
- {text: 'Row group', value: 'rowgroup'},
- {text: 'Column group', value: 'colgroup'}
- ]
- },
- {
- label: 'Alignment',
- name: 'align',
- type: 'listbox',
- text: 'None',
- minWidth: 90,
- maxWidth: null,
- values: [
- {text: 'None', value: ''},
- {text: 'Left', value: 'left'},
- {text: 'Center', value: 'center'},
- {text: 'Right', value: 'right'}
- ]
- }
- ]
- },
-
- onsubmit: function() {
- var data = this.toJSON();
-
- editor.undoManager.transact(function() {
- each(cells, function(cellElm) {
- editor.dom.setAttrib(cellElm, 'scope', data.scope);
-
- editor.dom.setStyles(cellElm, {
- width: addSizeSuffix(data.width),
- height: addSizeSuffix(data.height)
- });
-
- // Switch cell type
- if (data.type && cellElm.nodeName.toLowerCase() != data.type) {
- cellElm = dom.rename(cellElm, data.type);
- }
-
- // Apply/remove alignment
- unApplyAlign(cellElm);
- if (data.align) {
- editor.formatter.apply('align' + data.align, {}, cellElm);
- }
- });
-
- editor.focus();
- });
- }
- });
- }
-
- function rowDialog() {
- var dom = editor.dom, tableElm, cellElm, rowElm, data, rows = [];
-
- tableElm = editor.dom.getParent(editor.selection.getStart(), 'table');
- cellElm = editor.dom.getParent(editor.selection.getStart(), 'td,th');
-
- each(tableElm.rows, function(row) {
- each(row.cells, function(cell) {
- if (dom.hasClass(cell, 'mce-item-selected') || cell == cellElm) {
- rows.push(row);
- return false;
- }
- });
- });
-
- rowElm = rows[0];
- if (!rowElm) {
- // If this element is null, return now to avoid crashing.
- return;
- }
-
- data = {
- height: removePxSuffix(dom.getStyle(rowElm, 'height') || dom.getAttrib(rowElm, 'height')),
- scope: dom.getAttrib(rowElm, 'scope')
- };
-
- data.type = rowElm.parentNode.nodeName.toLowerCase();
-
- each('left center right'.split(' '), function(name) {
- if (editor.formatter.matchNode(rowElm, 'align' + name)) {
- data.align = name;
- }
- });
-
- editor.windowManager.open({
- title: "Row properties",
- items: {
- type: 'form',
- data: data,
- columns: 2,
- defaults: {
- type: 'textbox'
- },
- items: [
- {
- type: 'listbox',
- name: 'type',
- label: 'Row type',
- text: 'None',
- maxWidth: null,
- menu: [
- {text: 'Header', value: 'thead'},
- {text: 'Body', value: 'tbody'},
- {text: 'Footer', value: 'tfoot'}
- ]
- },
- {
- type: 'listbox',
- name: 'align',
- label: 'Alignment',
- text: 'None',
- maxWidth: null,
- menu: [
- {text: 'None', value: ''},
- {text: 'Left', value: 'left'},
- {text: 'Center', value: 'center'},
- {text: 'Right', value: 'right'}
- ]
- },
- {label: 'Height', name: 'height'}
- ]
- },
-
- onsubmit: function() {
- var data = this.toJSON(), tableElm, oldParentElm, parentElm;
-
- editor.undoManager.transact(function() {
- var toType = data.type;
-
- each(rows, function(rowElm) {
- editor.dom.setAttrib(rowElm, 'scope', data.scope);
-
- editor.dom.setStyles(rowElm, {
- height: addSizeSuffix(data.height)
- });
-
- if (toType != rowElm.parentNode.nodeName.toLowerCase()) {
- tableElm = dom.getParent(rowElm, 'table');
-
- oldParentElm = rowElm.parentNode;
- parentElm = dom.select(toType, tableElm)[0];
- if (!parentElm) {
- parentElm = dom.create(toType);
- if (tableElm.firstChild) {
- tableElm.insertBefore(parentElm, tableElm.firstChild);
- } else {
- tableElm.appendChild(parentElm);
- }
- }
-
- parentElm.appendChild(rowElm);
-
- if (!oldParentElm.hasChildNodes()) {
- dom.remove(oldParentElm);
- }
- }
-
- // Apply/remove alignment
- unApplyAlign(rowElm);
- if (data.align) {
- editor.formatter.apply('align' + data.align, {}, rowElm);
- }
- });
-
- editor.focus();
- });
- }
- });
- }
-
- function cmd(command) {
- return function() {
- editor.execCommand(command);
- };
- }
-
- function insertTable(cols, rows) {
- var y, x, html;
-
- html = '
| ' + (Env.ie ? " " : ' ') + ' | ';
- }
-
- html += '
| '; - } - - html += ' |
| "+(o.ie?" ":" ")+" | ";a+="
| ";e+=" |
| Expected: | " + expected + " |
|---|---|
| Result: | " + actual + " |
| Diff: | " + QUnit.diff( expected, actual ) + " |
| Source: | " + escapeText( source ) + " |
| Result: | " + escapeText( actual ) + " |
|---|---|
| Source: | " + escapeText( source ) + " |
| Source: | " + + escapeText( source ) + + " |
|---|
<\/p>\n?/, '').replace(/\n?
<\/p>$/, ''); -} + if (offset === 'after') { + if (start) { + rng.setStartAfter(container); + } else { + rng.setEndAfter(container); + } + return; + } else if (offset === 'afterNextCharacter') { + container = container.nextSibling; + offset = 1; + } + if (start) { + rng.setStart(container, offset); + } else { + rng.setEnd(container, offset); + } + } -/** - * Fakes a key event. - * - * @param {Element/String} e DOM element object or element id to send fake event to. - * @param {String} na Event name to fake like "keydown". - * @param {Object} o Optional object with data to send with the event like keyCode and charCode. - */ -function fakeKeyEvent(e, na, o) { - var ev; - - o = tinymce.extend({ - keyCode : 13, - charCode : 0 - }, o); - - e = tinymce.DOM.get(e); - - if (e.fireEvent) { - ev = document.createEventObject(); - tinymce.extend(ev, o); - e.fireEvent('on' + na, ev); - return; + setRange(startContainer, startOffset, true); + setRange(endContainer, endOffset, false); + editor.selection.setRng(rng); } - if (document.createEvent) { - try { - // Fails in Safari - ev = document.createEvent('KeyEvents'); - ev.initKeyEvent(na, true, true, window, false, false, false, false, o.keyCode, o.charCode); - } catch (ex) { - ev = document.createEvent('Events'); - ev.initEvent(na, true, true); + function trimContent(content) { + return content.replace(/^
<\/p>\n?/, '').replace(/\n?
<\/p>$/, ''); + } + + /** + * Fakes a key event. + * + * @param {Element/String} e DOM element object or element id to send fake event to. + * @param {String} na Event name to fake like "keydown". + * @param {Object} o Optional object with data to send with the event like keyCode and charCode. + */ + function fakeKeyEvent(e, na, o) { + var ev; + + o = tinymce.extend({ + keyCode : 13, + charCode : 0 + }, o); + + e = tinymce.DOM.get(e); + + if (e.fireEvent) { + ev = document.createEventObject(); + tinymce.extend(ev, o); + e.fireEvent('on' + na, ev); + return; + } + + if (document.createEvent) { + try { + // Fails in Safari + ev = document.createEvent('KeyEvents'); + ev.initKeyEvent(na, true, true, window, false, false, false, false, o.keyCode, o.charCode); + } catch (ex) { + ev = document.createEvent('Events'); + ev.initEvent(na, true, true); + + ev.keyCode = o.keyCode; + ev.charCode = o.charCode; + } + } else { + ev = document.createEvent('UIEvents'); + + if (ev.initUIEvent) + ev.initUIEvent(na, true, true, window, 1); ev.keyCode = o.keyCode; ev.charCode = o.charCode; } - } else { - ev = document.createEvent('UIEvents'); - if (ev.initUIEvent) - ev.initUIEvent(na, true, true, window, 1); - - ev.keyCode = o.keyCode; - ev.charCode = o.charCode; + e.dispatchEvent(ev); } - e.dispatchEvent(ev); -} + function normalizeRng(rng) { + if (rng.startContainer.nodeType == 3) { + if (rng.startOffset == 0) + rng.setStartBefore(rng.startContainer); + else if (rng.startOffset >= rng.startContainer.nodeValue.length - 1) + rng.setStartAfter(rng.startContainer); + } -function normalizeRng(rng) { - if (rng.startContainer.nodeType == 3) { - if (rng.startOffset == 0) - rng.setStartBefore(rng.startContainer); - else if (rng.startOffset >= rng.startContainer.nodeValue.length - 1) - rng.setStartAfter(rng.startContainer); + if (rng.endContainer.nodeType == 3) { + if (rng.endOffset == 0) + rng.setEndBefore(rng.endContainer); + else if (rng.endOffset >= rng.endContainer.nodeValue.length - 1) + rng.setEndAfter(rng.endContainer); + } + + return rng; } - if (rng.endContainer.nodeType == 3) { - if (rng.endOffset == 0) - rng.setEndBefore(rng.endContainer); - else if (rng.endOffset >= rng.endContainer.nodeValue.length - 1) - rng.setEndAfter(rng.endContainer); - } + // TODO: Replace this with the new event logic in 3.5 + function type(chr) { + var editor = tinymce.activeEditor, keyCode, charCode, event = tinymce.dom.Event, evt, startElm, rng; - return rng; -} + function fakeEvent(target, type, evt) { + editor.dom.fire(target, type, evt); + } -// TODO: Replace this with the new event logic in 3.5 -function type(chr) { - var editor = tinymce.activeEditor, keyCode, charCode, event = tinymce.dom.Event, evt, startElm, rng; - - function fakeEvent(target, type, evt) { - editor.dom.fire(target, type, evt); - } - - // Numeric keyCode - if (typeof(chr) == "number") { - charCode = keyCode = chr; - } else if (typeof(chr) == "string") { - // String value - if (chr == '\b') { - keyCode = 8; - charCode = chr.charCodeAt(0); - } else if (chr == '\n') { - keyCode = 13; - charCode = chr.charCodeAt(0); + // Numeric keyCode + if (typeof(chr) == "number") { + charCode = keyCode = chr; + } else if (typeof(chr) == "string") { + // String value + if (chr == '\b') { + keyCode = 8; + charCode = chr.charCodeAt(0); + } else if (chr == '\n') { + keyCode = 13; + charCode = chr.charCodeAt(0); + } else { + charCode = chr.charCodeAt(0); + keyCode = charCode; + } } else { - charCode = chr.charCodeAt(0); - keyCode = charCode; + evt = chr; } - } else { - evt = chr; - } - evt = evt || {keyCode: keyCode, charCode: charCode}; + evt = evt || {keyCode: keyCode, charCode: charCode}; - startElm = editor.selection.getStart(); - fakeEvent(startElm, 'keydown', evt); - fakeEvent(startElm, 'keypress', evt); + startElm = editor.selection.getStart(); + fakeEvent(startElm, 'keydown', evt); + fakeEvent(startElm, 'keypress', evt); - if (!evt.isDefaultPrevented()) { - if (keyCode == 8) { - if (editor.getDoc().selection) { - rng = editor.getDoc().selection.createRange(); + if (!evt.isDefaultPrevented()) { + if (keyCode == 8) { + if (editor.getDoc().selection) { + rng = editor.getDoc().selection.createRange(); - if (rng.text.length === 0) { - rng.moveStart('character', -1); - rng.select(); - } - - rng.execCommand('Delete', false, null); - } else { - rng = editor.selection.getRng(); - - if (rng.startContainer.nodeType == 1 && rng.collapsed) { - var nodes = rng.startContainer.childNodes, lastNode = nodes[nodes.length - 1]; - - // If caret is at
abc|
and after the abc text node then move it to the end of the text node - // Expand the range to include the last charab[c]
since IE 11 doesn't delete otherwise - if (rng.startOffset >= nodes.length - 1 && lastNode && lastNode.nodeType == 3 && lastNode.data.length > 0) { - rng.setStart(lastNode, lastNode.data.length - 1); - rng.setEnd(lastNode, lastNode.data.length); - editor.selection.setRng(rng); + if (rng.text.length === 0) { + rng.moveStart('character', -1); + rng.select(); } + + rng.execCommand('Delete', false, null); + } else { + rng = editor.selection.getRng(); + + if (rng.startContainer.nodeType == 1 && rng.collapsed) { + var nodes = rng.startContainer.childNodes, lastNode = nodes[nodes.length - 1]; + + // If caret is atabc|
and after the abc text node then move it to the end of the text node + // Expand the range to include the last charab[c]
since IE 11 doesn't delete otherwise + if (rng.startOffset >= nodes.length - 1 && lastNode && lastNode.nodeType == 3 && lastNode.data.length > 0) { + rng.setStart(lastNode, lastNode.data.length - 1); + rng.setEnd(lastNode, lastNode.data.length); + editor.selection.setRng(rng); + } + } + + editor.getDoc().execCommand('Delete', false, null); } + } else if (typeof(chr) == 'string') { + rng = editor.selection.getRng(true); - editor.getDoc().execCommand('Delete', false, null); - } - } else if (typeof(chr) == 'string') { - rng = editor.selection.getRng(true); - - if (rng.startContainer.nodeType == 3 && rng.collapsed) { - rng.startContainer.insertData(rng.startOffset, chr); - rng.setStart(rng.startContainer, rng.startOffset + 1); - rng.collapse(true); - editor.selection.setRng(rng); - } else { - rng.insertNode(editor.getDoc().createTextNode(chr)); + if (rng.startContainer.nodeType == 3 && rng.collapsed) { + rng.startContainer.insertData(rng.startOffset, chr); + rng.setStart(rng.startContainer, rng.startOffset + 1); + rng.collapse(true); + editor.selection.setRng(rng); + } else { + rng.insertNode(editor.getDoc().createTextNode(chr)); + } } } + + fakeEvent(startElm, 'keyup', evt); } - fakeEvent(startElm, 'keyup', evt); -} + function cleanHtml(html) { + html = html.toLowerCase().replace(/[\r\n]+/gi, ''); + html = html.replace(/ (sizcache[0-9]+|sizcache|nodeindex|sizset[0-9]+|sizset|data\-mce\-expando|data\-mce\-selected)="[^"]*"/gi, ''); + html = html.replace(/]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\/span>|Editor 1
'); + + equal($('#elm1').html(), 'Editor 1
'); + equal($('#elm1').val(), 'Editor 1
'); + equal($('#elm1').attr('value'), 'Editor 1
'); + equal($('#elm1').text(), 'Editor 1'); +}); + +test("Set contents using jQuery", function() { + expect(4); + + $('#elm1').html('Test 1'); + equal($('#elm1').html(), 'Test 1
'); + + $('#elm1').val('Test 2'); + equal($('#elm1').html(), 'Test 2
'); + + $('#elm1').text('Test 3'); + equal($('#elm1').html(), 'Test 3
'); + + $('#elm1').attr('value', 'Test 4'); + equal($('#elm1').html(), 'Test 4
'); +}); + +test("append/prepend contents using jQuery", function() { + expect(2); + + tinymce.get('elm1').setContent('Editor 1
'); + + $('#elm1').append('Test 1
'); + equal($('#elm1').html(), 'Editor 1
\nTest 1
'); + + $('#elm1').prepend('Test 2
'); + equal($('#elm1').html(), 'Test 2
\nEditor 1
\nTest 1
'); +}); + +test("Find using :tinymce selector", function() { + expect(1); + + equal($('textarea:tinymce').length, 2); +}); + +test("Set contents using :tinymce selector", function() { + expect(3); + + $('textarea:tinymce').val('Test 1'); + equal($('#elm1').val(), 'Test 1
'); + equal($('#elm2').val(), 'Test 1
'); + equal($('#elm3').val(), 'Textarea'); +}); + +test("Get contents using :tinymce selector", function() { + expect(1); + + $('textarea:tinymce').val('Test get'); + equal($('textarea:tinymce').val(), 'Test get
'); +}); diff --git a/tests/qunit/editor/plugins/js/autolink.actions.js b/tests/qunit/editor/plugins/js/autolink.actions.js deleted file mode 100644 index 53d50e32c8..0000000000 --- a/tests/qunit/editor/plugins/js/autolink.actions.js +++ /dev/null @@ -1,52 +0,0 @@ -function fakeTypeAURL(url) { - return function(callback) { - // type the URL and then press the space bar - tinymce.execCommand('mceInsertContent', false, url); - window.robot.type(32, false, callback, editor.selection.getNode()); - }; -} - -function fakeTypeAnEclipsedURL(url) { - return function(callback) { - // type the URL and then type ')' - tinymce.execCommand('mceInsertContent', false, '(' + url); - window.robot.typeSymbol(")", function() { - window.robot.type(32, false, callback, editor.selection.getNode()); - }, editor.selection.getNode()); - }; -} - -function fakeTypeANewlineURL(url) { - return function(callback) { - // type the URL and then press the enter key - tinymce.execCommand('mceInsertContent', false, url); - window.robot.type('\n', false, callback, editor.selection.getNode()); - }; -} - -createAction('Typing HTTP URL', fakeTypeAURL('http://www.ephox.com')); -createAction('Typing HTTPS URL', fakeTypeAURL('https://www.ephox.com')); -createAction('Typing SSH URL', fakeTypeAURL('ssh://www.ephox.com')); -createAction('Typing FTP URL', fakeTypeAURL('ftp://www.ephox.com')); -createAction('Typing WWW URL', fakeTypeAURL('www.ephox.com')); -createAction('Typing WWW URL With End Dot', fakeTypeAURL('www.site.com.')); -createAction('Typing Mail Addr', fakeTypeAURL('user@domain.com')); -createAction('Typing Mail Addr With Protocol', fakeTypeAURL('mailto:user@domain.com')); -createAction('Typing Dashed Mail Addr', fakeTypeAURL('first-last@domain.com')); -createAction('Typing Eclipsed HTTP URL', fakeTypeAnEclipsedURL('http://www.ephox.com')); -createAction('Typing Eclipsed HTTPS URL', fakeTypeAnEclipsedURL('https://www.ephox.com')); -createAction('Typing Eclipsed SSH URL', fakeTypeAnEclipsedURL('ssh://www.ephox.com')); -createAction('Typing Eclipsed FTP URL', fakeTypeAnEclipsedURL('ftp://www.ephox.com')); -createAction('Typing Eclipsed WWW URL', fakeTypeAnEclipsedURL('www.ephox.com')); -createAction('Typing HTTP URL And Newline', fakeTypeANewlineURL('http://www.ephox.com')); -createAction('Typing HTTPS URL And Newline', fakeTypeANewlineURL('https://www.ephox.com')); -createAction('Typing SSH URL And Newline', fakeTypeANewlineURL('ssh://www.ephox.com')); -createAction('Typing FTP URL And Newline', fakeTypeANewlineURL('ftp://www.ephox.com')); -createAction('Typing WWW URL And Newline', fakeTypeANewlineURL('www.ephox.com')); -createAction('Applying OL', 'InsertOrderedList'); -createAction('Applying UL', 'InsertUnorderedList'); -createAction('Indenting', 'Indent'); -createAction('Outdenting', 'Outdent'); -createAction('Typing Enter', fakeKeyPressAction('\n')); -createAction('Typing Tab', fakeKeyPressAction('\t')); -createAction('Typing Shift Tab', fakeKeyPressAction('\t', true)); diff --git a/tests/qunit/editor/plugins/js/dsl.js b/tests/qunit/editor/plugins/js/dsl.js deleted file mode 100644 index 742321572e..0000000000 --- a/tests/qunit/editor/plugins/js/dsl.js +++ /dev/null @@ -1,138 +0,0 @@ -var editor; - -function getFunctionName(func) { - if (func.name && func.name != "") { - return func.name; - } else if (typeof func == "function" || typeof func == "object") { - var fName = ("" + func).match(/function\s*([\w\$]+)\s*\(/); - if (fName !== null && fName != "") { - return fName[1]; - } else { - for (var v in window) { - if (window[v] === func) { - func.name = v; - return v; - } - } - } - } -} - -function assertState(expected, message) { - var content = editor.getContent().replace(/[\n\r]/g, ''); - if (expected && expected.replace) expected = expected.replace(/[\n\r]/g, ''); - // Safari reports "function", while Firefox and IE report "object" - if (typeof expected == "function" || typeof expected == "object") { - if (expected.test(content)) - equal(content, content, message); - else - equal(content, expected.toString(), message); - } else { - equal(content, expected, message); - } -} - -tinymce.create('dsl.Queue', { - Queue: function() { - this.queue = []; - }, - - add: function(task) { - this.queue.push(task); - }, - - next: function() { - if (this.queue.length > 0) { - var task = this.queue.shift(); - task(); - return true; - } else { - QUnit.start(); - return false; - } - }, - - done: function() { - expect(this.queue.length); - this.next(); - } -}); - -tinymce.create('dsl.Action', { - Action: function(name, action) { - this.name = name; - this.a = this.curryPreposition('a'); - this.inA = this.curryPreposition('in a'); - this.to = this.curryPreposition('to'); - if (tinymce.is(action, 'string')) { - this.action = function(callback) { - editor.execCommand(action); - callback(); - }; - } else { - this.action = action; - } - }, - - curryPreposition: function(preposition) { - return function(state) { - return this.go(state, preposition); - }; - }, - - go: function(state, preposition) { - var message = this.name + " " + preposition + " " + getFunctionName(state); - var action = this.action; - var actionPerformed = false; - function defer(callback) { - return function() { - var args = arguments; - queue.add(function() { - if (actionPerformed) { - callback.apply(undefined, args); - queue.next(); - return; - } - editor.focus(); - state(); - action(function() { - actionPerformed = true; - callback.apply(undefined, args); - queue.next(); - }); - }); - return this; - }; - } - - var dslState = { - gives: defer(function(expected) { - assertState(expected, message); - }), - - enablesState: defer(function(state) { - ok(editor.queryCommandState(state), message + " enables " + state + " command"); - }), - - disablesState: defer(function(state) { - ok(!editor.queryCommandState(state), message + " disables " + state + " command"); - }) - }; - dslState.andGives = dslState.gives; - return dslState; - } -}); - - -// Action Utilities -function fakeKeyPressAction(keyCode, shiftKey) { - return function(callback) { - setTimeout(function() { - window.robot.type(keyCode, shiftKey, callback, editor.selection.getNode()); - }, 1); - }; -} - -function createAction(name, action) { - window[name.replace(/\s+/g, '')] = new dsl.Action(name, action); -} \ No newline at end of file diff --git a/tests/qunit/editor/plugins/js/states.js b/tests/qunit/editor/plugins/js/states.js deleted file mode 100644 index 611ffdee8e..0000000000 --- a/tests/qunit/editor/plugins/js/states.js +++ /dev/null @@ -1,176 +0,0 @@ -function createState(content, startSelector, startOffset, endSelector, endOffset) { - return function() { - editor.setContent(content); - setSelection(startSelector, startOffset, endSelector, endOffset); - }; -} - -/** Collapsed Selection States **/ -function EmptyParagraph() { - var body = editor.getBody(); - while (body.firstChild) { - editor.dom.remove(body.firstChild); - } - var p = body.ownerDocument.createElement('p'); - p.appendChild(body.ownerDocument.createTextNode('')); - body.appendChild(p, body); - setSelection(p.firstChild, 0); -} - -function EmptyHeading() { - EmptyParagraph(); - editor.dom.rename(editor.getBody().firstChild, 'h1'); - setSelection(editor.getBody().firstChild.firstChild, 0); -} - -function TextAfterUL() { - editor.setContent('Test
', 'p', 0); -ParagraphWithMarginLeft = createState('Test
', 'p', 0); -ParagraphWithPaddingLeft = createState('Test
', 'p', 0); -ParagraphWithMarginAndPaddingLeft = createState('Test
', 'p', 0); - -CenteredListItem = createState('Test
Test
Test
| Test |
| Test |
| Test Line 2 |
| Test Line 2 |
| Test Line 2 Line 3 |
| Test Line 2 |
| Test Line 2 |
Content
After
Test
Test
Test
Test
', 'p', 1); -StartOfParagraphAfterUL = createState('Test
', 'p', 1); -StartOfParagraphAfterOLWithListType = createState('Test
', 'p', 1); -EmptyOrderedListItem = createState('Item
This is a test
', 'p', 5, 'p', 7); -MultipleParagraphSelection = createState('This is a test
Second paragraph
', 'p:nth-child(1)', 5, 'p:nth-child(2)', 6); -SingleHeadingSelection = createState('| Cell 1 |
| Cell 1 Line 2 |
| Cell 1 Line 2 |
Line2 Line3 Line4 |
This is a test
This is a test
Second paragraph
', 'h1', 5, 'p', 6); -BlockToParagraphSelection = createState('Second paragraph
', 'div', 5, 'p', 6); -MultipleParagraphAndHeadingSelection = createState('This is a test
Before
Before
After
', 'li', 4, 'p', 3); -ParagraphAfterUlSelection = createState('After
', 'li', 4, 'p', 3); -ParagraphBeforeAndAfterOlSelection = createState('Before
After
', 'p', 4, '#after', 3); -ParagraphBeforeAndAfterUlSelection = createState('Before
After
', 'p', 4, '#after', 3); - -SelectionEndingAtBr = createState('Item
After
Before
Item
My sentence is this.
'); + var result = editor.plugins.wordcount.getCount(); + equal(result, 4); +}); + +test("Does not count dashes", function() { + expect(1); + + editor.setContent('Something -- ok
'); + var result = editor.plugins.wordcount.getCount(); + equal(result, 2); +}); + +test("Does not count asterisks, non-word characters", function() { + expect(1); + + editor.setContent('* something\n\u00b7 something else
'); + var result = editor.plugins.wordcount.getCount(); + equal(result, 3); +}); + +test("Does not count numbers", function() { + expect(1); + + editor.setContent('Something 123 ok
'); + var result = editor.plugins.wordcount.getCount(); + equal(result, 2); +}); + +test("Does not count htmlentities", function() { + expect(1); + + editor.setContent('It’s my life – – – don\'t you forget.
'); + var result = editor.plugins.wordcount.getCount(); + equal(result, 6); +}); + +test("Counts hyphenated words as one word", function() { + expect(1); + + editor.setContent('Hello some-word here.
'); + var result = editor.plugins.wordcount.getCount(); + equal(result, 3); +}); + +test("Counts words between blocks as two words", function() { + expect(1); + + editor.setContent('Hello
world
'); + var result = editor.plugins.wordcount.getCount(); + equal(result, 2); +}); diff --git a/tests/qunit/editor/test.gif b/tests/qunit/editor/test.gif deleted file mode 100644 index e565824aaf..0000000000 Binary files a/tests/qunit/editor/test.gif and /dev/null differ diff --git a/tests/qunit/editor/tinymce/Editor.html b/tests/qunit/editor/tinymce/Editor.js similarity index 75% rename from tests/qunit/editor/tinymce/Editor.html rename to tests/qunit/editor/tinymce/Editor.js index 76ac3bd358..f555bd26d0 100644 --- a/tests/qunit/editor/tinymce/Editor.html +++ b/tests/qunit/editor/tinymce/Editor.js @@ -1,20 +1,24 @@ - - - -abcd
'); + equal(editor.selection.getNode().nodeName, 'P'); +}); + +test('Wrap single root text node in P with attrs', function() { + editor.settings.forced_root_block_attrs = {"class": "class1"}; + editor.getBody().innerHTML = 'abcd'; + Utils.setSelection('body', 2); + Utils.pressArrowKey(); + equal(editor.getContent(), 'abcd
'); + equal(editor.selection.getNode().nodeName, 'P'); +}); + +test('Wrap single root text node in P but not table sibling', function() { + editor.getBody().innerHTML = 'abcd| x |
abcd
| x |
| x |
abcd
| x |
');
ok(!DOM.isEmpty(DOM.get('test')), 'Non empty html with img element');
DOM.setHTML('test', '');
diff --git a/tests/qunit/editor/tinymce/dom/DOMUtils_jquery.html b/tests/qunit/editor/tinymce/dom/DOMUtils_jquery.html
deleted file mode 100644
index f9086822e6..0000000000
--- a/tests/qunit/editor/tinymce/dom/DOMUtils_jquery.html
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-first strong strong second em strong.
-bar
-some textem textmore text
-| 1 | -abc | -
| 3 | -4 | -
textabcspan
-first strong strong second em strong.
' + + 'bar
' + + 'some textem textmore text
' + + '| 1 | ' + + 'abc | ' + + '
| 3 | ' + + '4 | ' + + '
textabcspan
' + + 'irst strong strong second em strong.
bar
some textem textmore text
| 1 | ab |
| bc | |
| 3 | 4 |
te
'); + equal(r.startContainer.nodeType, 3); + equal(r.startOffset, 1); + equal(r.endContainer.nodeType, 3); + equal(r.endOffset, 2); + equal(r.collapsed, false); + equal(r.commonAncestorContainer.nodeName, 'DIV'); + + r.setStart(document.getElementById('first').firstChild, 1); + r.setEnd(document.getElementById('first').lastChild, 4); + + equal(getHTML(r.cloneContents()), 'irst strong strong second em str'); + equal(r.startContainer.nodeType, 3); + equal(r.startOffset, 1); + equal(r.endContainer.nodeType, 3); + equal(r.endOffset, 4); + equal(r.collapsed, false); + equal(r.commonAncestorContainer.nodeName, 'P'); + + r.setStart(document.getElementById('first').firstChild, 1); + r.setEnd(document.getElementById('first').firstChild, 4); + + equal(getHTML(r.cloneContents()), 'irs'); + equal(r.startContainer.nodeType, 3); + equal(r.startOffset, 1); + equal(r.endContainer.nodeType, 3); + equal(r.endOffset, 4); + equal(r.collapsed, false); + equal(r.commonAncestorContainer.nodeType, 3); + + r.setStart(document.getElementById('first'), 0); + r.setEnd(document.getElementById('last'), 0); + + equal(getHTML(r.cloneContents()), 'first strong strong second em strong.
bar
some textem textmore text
| 1 | abc |
| 3 | 4 |
strong strong second em strong.
bar
some textem textmore text
| 1 | abc |
| 3 | 4 |
textabc
'); + equal(r.startContainer.nodeType, 1); + equal(r.startOffset, 1); + equal(r.endContainer.nodeType, 1); + equal(r.endOffset, 1); + equal(r.collapsed, false); + equal(r.commonAncestorContainer.nodeType, 1); + + r.setStart(document.getElementById('sample'), 0); + r.setEnd(document.getElementById('sample'), document.getElementById('sample').childNodes.length - 1); + + equal(getHTML(r.cloneContents()), 'first strong strong second em strong.
bar
some textem textmore text
| 1 | abc |
| 3 | 4 |
first strong strong second em strong.
bar
some textem textmore text
| 1 | abc |
| 3 | 4 |
t
'); + equal(r.startContainer.nodeType, 1); + equal(r.startOffset, 0); + equal(r.endContainer.nodeType, 3); + equal(r.endOffset, 1); + equal(r.collapsed, false); + equal(r.commonAncestorContainer.nodeType, 1); + + r.setStart(document.getElementById('first').firstChild, 1); + r.setEnd(document.getElementById('last'), 0); + + equal(getHTML(r.cloneContents()), 'irst strong strong second em strong.
bar
some textem textmore text
| 1 | abc |
| 3 | 4 |
first strong strong second em strong.
bar
some textem text
'); + equal(r.startContainer.nodeType, 1); + equal(r.startOffset, 0); + equal(r.endContainer.nodeType, 1); + equal(r.endOffset, 2); + equal(r.collapsed, false); + equal(r.commonAncestorContainer.nodeType, 1); + + r.setStart(document.getElementById('sample'), 0); + r.setEnd(document.getElementById('traverse'), 1); + + equal(getHTML(r.cloneContents()), 'first strong strong second em strong.
bar
some text
'); + equal(r.startContainer.nodeType, 1); + equal(r.startOffset, 0); + equal(r.endContainer.nodeType, 1); + equal(r.endOffset, 1); + equal(r.collapsed, false); + equal(r.commonAncestorContainer.nodeType, 1); + }); + } + + test("test_extractContents1", function() { + var r = createRng(); + + expect(10); + + r.setStart(document.getElementById('first').firstChild, 1); + r.setEnd(document.getElementById('first').firstChild, 4); + + equal(getHTML(r.extractContents()), 'irs'); + equal(r.startContainer.nodeType, 3); + equal(r.startOffset, 1); + equal(r.endContainer.nodeType, 3); + equal(r.endOffset, 1); + equal(r.collapsed, true); + equal(r.startContainer == r.endContainer, true); + equal(r.startOffset == r.endOffset, true); + equal(r.commonAncestorContainer.nodeType, 3); + equal(getHTML(document.getElementById('first')), 'ft strong strong second em strong.
'); + }); + + test("test_extractContents2", function() { + var r = createRng(); + + expect(9); + + r.setStart(document.getElementById('two').firstChild, 1); + r.setEnd(document.getElementById('last').firstChild, 2); + + equal(getHTML(r.extractContents()), '| bc | |
| 3 | 4 |
te
'); + equal(r.startContainer.nodeType, 1); + equal(getHTML(r.startContainer), 'first strong strong second em strong.
bar
some textem textmore text
| 1 | a |
xtabcspan
first strong strong second em strong.
bar
some textem textmore text
| 1 | a |
xtabcspan
first strong strong second em strong.
bar
some textem text
'); + equal(getHTML(r.startContainer), 'more text
| 1 | abc |
| 3 | 4 |
textabcspan
more text
| 1 | abc |
| 3 | 4 |
textabcspan
more text
| 1 | abc |
| 3 | 4 |
textabcspan
first strong strong second em strong.
bar
some textem textmore text
| 1 | a |
xtabcspan
first strong strong second em strong.
bar
some textem textmore text
| 1 | a |
xtabcspan
first strong strong second em strong.
bar
some textem textmore text
| 1 | a |
xtabcspan
fong.
'); + equal(r.startOffset, 1); + equal(r.endContainer.nodeType, 1); + equal(r.endOffset, 1); + equal(getHTML(r.endContainer), 'fong.
'); + equal(getHTML(document.getElementById('sample')), 'fong.
bar
some textem textmore text
| 1 | abc |
| 3 | 4 |
textabcspan
some textem textmore text
| 1 | abc |
| 3 | 4 |
textabcspan
some textem textmore text
| 1 | abc |
| 3 | 4 |
textabcspan
some textem textmore text
| 1 | abc |
| 3 | 4 |
textabcspan
more text
| 1 | abc |
| 3 | 4 |
textabcspan
more text
| 1 | abc |
| 3 | 4 |
textabcspan
more text
| 1 | abc |
| 3 | 4 |
textabcspan