|
1 |
| -/** |
| 1 | +/** |
2 | 2 | * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
|
3 | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license
|
4 | 4 | */
|
|
1371 | 1371 | dataTransfer = data.dataTransfer;
|
1372 | 1372 |
|
1373 | 1373 | if ( dataTransfer.getTransferType( editor ) == CKEDITOR.DATA_TRANSFER_INTERNAL ) {
|
1374 |
| - internalDrop( dragRange, dropRange, dataTransfer ); |
| 1374 | + // Execute drop with a timeout because otherwise selection, after drop, |
| 1375 | + // on IE is in the drag position, instead of drop position. |
| 1376 | + setTimeout( function() { |
| 1377 | + clipboard.internalDrop( dragRange, dropRange, dataTransfer, editor ); |
| 1378 | + }, 0 ); |
1375 | 1379 | } else if ( dataTransfer.getTransferType( editor ) == CKEDITOR.DATA_TRANSFER_CROSS_EDITORS ) {
|
1376 | 1380 | crossEditorDrop( dragRange, dropRange, dataTransfer );
|
1377 | 1381 | } else {
|
1378 | 1382 | externalDrop( dropRange, dataTransfer );
|
1379 | 1383 | }
|
1380 | 1384 | }, null, null, 9999 );
|
1381 | 1385 |
|
1382 |
| - // Internal drag and drop (drag and drop in the same editor). |
1383 |
| - function internalDrop( dragRange, dropRange, dataTransfer ) { |
1384 |
| - // Execute drop with a timeout because otherwise selection, after drop, |
1385 |
| - // on IE is in the drag position, instead of drop position. |
1386 |
| - setTimeout( function() { |
1387 |
| - var dragBookmark, dropBookmark, isRangeBefore; |
1388 |
| - |
1389 |
| - // Save and lock snapshot so there will be only |
1390 |
| - // one snapshot for both remove and insert content. |
1391 |
| - editor.fire( 'saveSnapshot' ); |
1392 |
| - editor.fire( 'lockSnapshot', { dontUpdate: 1 } ); |
1393 |
| - |
1394 |
| - if ( CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) { |
1395 |
| - clipboard.fixIESplitNodesAfterDrop( dragRange, dropRange ); |
1396 |
| - } |
1397 |
| - |
1398 |
| - // Because we manipulate multiple ranges we need to do it carefully, |
1399 |
| - // changing one range (event creating a bookmark) may make other invalid. |
1400 |
| - // We need to change ranges into bookmarks so we can manipulate them easily in the future. |
1401 |
| - // We can change the range which is later in the text before we change the preceding range. |
1402 |
| - // We call isRangeBefore to test the order of ranges. |
1403 |
| - isRangeBefore = clipboard.isRangeBefore( dragRange, dropRange ); |
1404 |
| - if ( !isRangeBefore ) { |
1405 |
| - dragBookmark = dragRange.createBookmark( 1 ); |
1406 |
| - } |
1407 |
| - dropBookmark = dropRange.clone().createBookmark( 1 ); |
1408 |
| - if ( isRangeBefore ) { |
1409 |
| - dragBookmark = dragRange.createBookmark( 1 ); |
1410 |
| - } |
1411 |
| - |
1412 |
| - // No we can safely delete content for the drag range... |
1413 |
| - dragRange = editor.createRange(); |
1414 |
| - dragRange.moveToBookmark( dragBookmark ); |
1415 |
| - editable.extractHtmlFromRange( dragRange, 1 ); |
1416 |
| - |
1417 |
| - // ...and paste content into the drop position. |
1418 |
| - dropRange = editor.createRange(); |
1419 |
| - dropRange.moveToBookmark( dropBookmark ); |
1420 |
| - |
1421 |
| - // We do not select drop range, because of may be in the place we can not set the selection |
1422 |
| - // (e.g. between blocks, in case of block widget D&D). We put range to the paste event instead. |
1423 |
| - firePasteEvents( editor, { dataTransfer: dataTransfer, method: 'drop', range: dropRange }, 1 ); |
1424 |
| - |
1425 |
| - editor.fire( 'unlockSnapshot' ); |
1426 |
| - }, 0 ); |
1427 |
| - } |
1428 |
| - |
1429 | 1386 | // Cross editor drag and drop (drag in one Editor and drop in the other).
|
1430 | 1387 | function crossEditorDrop( dragRange, dropRange, dataTransfer ) {
|
1431 | 1388 | // Paste event should be fired before delete contents because otherwise
|
|
1582 | 1539 | },
|
1583 | 1540 |
|
1584 | 1541 | /**
|
1585 |
| - * Check if the beginning of the `firstRange` is before the beginning of the `secondRange` |
1586 |
| - * and modification of the content in the `firstRange` may break `secondRange`. |
| 1542 | + * Checkes whether turning drag range into bookmarks will invalidate the drop range. |
| 1543 | + * This usually happens when drop range shares the container with drag range and is located |
| 1544 | + * after the drag range, but there are countless edge cases. |
1587 | 1545 | *
|
1588 |
| - * Notify that this function returns `false` if these two ranges are in two |
1589 |
| - * separate nodes and do not affect each other (even if `firstRange` is before `secondRange`). |
| 1546 | + * This function is stricly related to {@link #internalDrop} which toggles |
| 1547 | + * order in which it creates bookmarks for both ranges based on a value returned |
| 1548 | + * by this method. In some cases this method returns a value which does not necessarily |
| 1549 | + * is true in terms of what it was meant to check, but it is convenient, because |
| 1550 | + * we know how it is interpretted in {@link #internalDrop}, so the correct |
| 1551 | + * behavior of the entire algorithm is assured. |
1590 | 1552 | *
|
1591 | 1553 | * **Note:** This function is in the public scope for tests usage only.
|
1592 | 1554 | *
|
1593 | 1555 | * @since 4.5
|
1594 | 1556 | * @private
|
1595 |
| - * @param {CKEDITOR.dom.range} firstRange The first range to compare. |
1596 |
| - * @param {CKEDITOR.dom.range} secondRange The second range to compare. |
| 1557 | + * @param {CKEDITOR.dom.range} dragRange The first range to compare. |
| 1558 | + * @param {CKEDITOR.dom.range} dropRange The second range to compare. |
1597 | 1559 | * @returns {Boolean} True if the first range in before the second range.
|
1598 | 1560 | */
|
1599 |
| - isRangeBefore: function( firstRange, secondRange ) { |
1600 |
| - // Both ranges has the same parent and the first has smaller offset. E.g.: |
1601 |
| - // |
1602 |
| - // "Lorem ipsum dolor sit[1] amet consectetur[2] adipiscing elit." |
1603 |
| - // "Lorem ipsum dolor sit" [1] "amet consectetur" [2] "adipiscing elit." |
1604 |
| - // |
1605 |
| - if ( firstRange.endContainer.equals( secondRange.startContainer ) && |
1606 |
| - firstRange.endOffset < secondRange.startOffset ) |
| 1561 | + isDropRangeAffectedByDragRange: function( dragRange, dropRange ) { |
| 1562 | + var dropContainer = dropRange.startContainer, |
| 1563 | + dropOffset = dropRange.endOffset; |
| 1564 | + |
| 1565 | + // Both containers are the same and drop offset is at the same position or later. |
| 1566 | + // " A L] A " " M A " |
| 1567 | + // ^ ^ |
| 1568 | + if ( dragRange.endContainer.equals( dropContainer ) && dragRange.endOffset <= dropOffset ) { |
1607 | 1569 | return true;
|
| 1570 | + } |
1608 | 1571 |
|
1609 |
| - // First range is inside a text node and the second is not, but if we change the |
1610 |
| - // first range into bookmark and split the text node then the seconds node offset |
1611 |
| - // will be no longer correct. |
1612 |
| - // |
1613 |
| - // "Lorem ipsum dolor sit [1] amet" "consectetur" [2] "adipiscing elit." |
1614 |
| - // |
1615 |
| - if ( firstRange.endContainer.getParent().equals( secondRange.startContainer ) && |
1616 |
| - firstRange.endContainer.getIndex() < secondRange.startOffset ) |
| 1572 | + // Bookmark for drag start container will mess up with offsets. |
| 1573 | + // " O [L A " " M A " |
| 1574 | + // ^ ^ |
| 1575 | + if ( |
| 1576 | + dragRange.startContainer.getParent().equals( dropContainer ) && |
| 1577 | + dragRange.startContainer.getIndex() < dropOffset |
| 1578 | + ) { |
| 1579 | + return true; |
| 1580 | + } |
| 1581 | + |
| 1582 | + // Bookmark for drag end container will mess up with offsets. |
| 1583 | + // " O] L A " " M A " |
| 1584 | + // ^ ^ |
| 1585 | + if ( |
| 1586 | + dragRange.endContainer.getParent().equals( dropContainer ) && |
| 1587 | + dragRange.endContainer.getIndex() < dropOffset |
| 1588 | + ) { |
1617 | 1589 | return true;
|
| 1590 | + } |
1618 | 1591 |
|
1619 | 1592 | return false;
|
1620 | 1593 | },
|
1621 | 1594 |
|
| 1595 | + /** |
| 1596 | + * Internal drag and drop (drag and drop in the same editor). |
| 1597 | + * |
| 1598 | + * **Note:** This function is in the public scope for tests usage only. |
| 1599 | + * |
| 1600 | + * @since 4.5 |
| 1601 | + * @private |
| 1602 | + * @param {CKEDITOR.dom.range} dragRange The first range to compare. |
| 1603 | + * @param {CKEDITOR.dom.range} dropRange The second range to compare. |
| 1604 | + * @param {CKEDITOR.plugins.clipboard.dataTransfer} dataTransfer |
| 1605 | + * @param {CKEDITOR.editor} editor |
| 1606 | + */ |
| 1607 | + internalDrop: function( dragRange, dropRange, dataTransfer, editor ) { |
| 1608 | + var editable = editor.editable(), |
| 1609 | + dragBookmark, dropBookmark, isDropRangeAffected; |
| 1610 | + |
| 1611 | + // Save and lock snapshot so there will be only |
| 1612 | + // one snapshot for both remove and insert content. |
| 1613 | + editor.fire( 'saveSnapshot' ); |
| 1614 | + editor.fire( 'lockSnapshot', { dontUpdate: 1 } ); |
| 1615 | + |
| 1616 | + if ( CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) { |
| 1617 | + this.fixIESplitNodesAfterDrop( dragRange, dropRange ); |
| 1618 | + } |
| 1619 | + |
| 1620 | + // Because we manipulate multiple ranges we need to do it carefully, |
| 1621 | + // changing one range (event creating a bookmark) may make other invalid. |
| 1622 | + // We need to change ranges into bookmarks so we can manipulate them easily in the future. |
| 1623 | + // We can change the range which is later in the text before we change the preceding range. |
| 1624 | + // We call isDropRangeAffectedByDragRange to test the order of ranges. |
| 1625 | + isDropRangeAffected = this.isDropRangeAffectedByDragRange( dragRange, dropRange ); |
| 1626 | + if ( !isDropRangeAffected ) { |
| 1627 | + dragBookmark = dragRange.createBookmark( 1 ); |
| 1628 | + } |
| 1629 | + dropBookmark = dropRange.clone().createBookmark( 1 ); |
| 1630 | + if ( isDropRangeAffected ) { |
| 1631 | + dragBookmark = dragRange.createBookmark( 1 ); |
| 1632 | + } |
| 1633 | + |
| 1634 | + // No we can safely delete content for the drag range... |
| 1635 | + dragRange = editor.createRange(); |
| 1636 | + dragRange.moveToBookmark( dragBookmark ); |
| 1637 | + editable.extractHtmlFromRange( dragRange, 1 ); |
| 1638 | + |
| 1639 | + // ...and paste content into the drop position. |
| 1640 | + dropRange = editor.createRange(); |
| 1641 | + dropRange.moveToBookmark( dropBookmark ); |
| 1642 | + |
| 1643 | + // We do not select drop range, because of may be in the place we can not set the selection |
| 1644 | + // (e.g. between blocks, in case of block widget D&D). We put range to the paste event instead. |
| 1645 | + firePasteEvents( editor, { dataTransfer: dataTransfer, method: 'drop', range: dropRange }, 1 ); |
| 1646 | + |
| 1647 | + editor.fire( 'unlockSnapshot' ); |
| 1648 | + }, |
| 1649 | + |
1622 | 1650 | /**
|
1623 | 1651 | * Get range from the `drop` event.
|
1624 | 1652 | *
|
|
0 commit comments