Merge pull request #3042 from storytouch/fix_dnd
Perform DnD in one changeset, so UNDO works properly. Fix #3041pull/3048/head
commit
45266f90a3
|
@ -1455,16 +1455,6 @@ function Ace2Inner(){
|
||||||
var selection = getSelection();
|
var selection = getSelection();
|
||||||
p.end();
|
p.end();
|
||||||
|
|
||||||
function topLevel(n)
|
|
||||||
{
|
|
||||||
if ((!n) || n == root) return null;
|
|
||||||
while (n.parentNode != root)
|
|
||||||
{
|
|
||||||
n = n.parentNode;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selection)
|
if (selection)
|
||||||
{
|
{
|
||||||
var node1 = topLevel(selection.startPoint.node);
|
var node1 = topLevel(selection.startPoint.node);
|
||||||
|
@ -1486,12 +1476,8 @@ function Ace2Inner(){
|
||||||
var nds = root.getElementsByTagName("style");
|
var nds = root.getElementsByTagName("style");
|
||||||
for (var i = 0; i < nds.length; i++)
|
for (var i = 0; i < nds.length; i++)
|
||||||
{
|
{
|
||||||
var n = nds[i];
|
var n = topLevel(nds[i]);
|
||||||
while (n.parentNode && n.parentNode != root)
|
if (n && n.parentNode == root)
|
||||||
{
|
|
||||||
n = n.parentNode;
|
|
||||||
}
|
|
||||||
if (n.parentNode == root)
|
|
||||||
{
|
{
|
||||||
observeChangesAroundNode(n);
|
observeChangesAroundNode(n);
|
||||||
}
|
}
|
||||||
|
@ -5034,6 +5020,23 @@ function Ace2Inner(){
|
||||||
if(e.target.a || e.target.localName === "a"){
|
if(e.target.a || e.target.localName === "a"){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bug fix: when user drags some content and drop it far from its origin, we
|
||||||
|
// need to merge the changes into a single changeset. So mark origin with <style>,
|
||||||
|
// in order to make content be observed by incorporateUserChanges() (see
|
||||||
|
// observeSuspiciousNodes() for more info)
|
||||||
|
var selection = getSelection();
|
||||||
|
if (selection){
|
||||||
|
var firstLineSelected = topLevel(selection.startPoint.node);
|
||||||
|
var lastLineSelected = topLevel(selection.endPoint.node);
|
||||||
|
|
||||||
|
var lineBeforeSelection = firstLineSelected.previousSibling;
|
||||||
|
var lineAfterSelection = lastLineSelected.nextSibling;
|
||||||
|
|
||||||
|
var neighbor = lineBeforeSelection || lineAfterSelection;
|
||||||
|
neighbor.appendChild(document.createElement('style'));
|
||||||
|
}
|
||||||
|
|
||||||
// Call drop hook
|
// Call drop hook
|
||||||
hooks.callAll('aceDrop', {
|
hooks.callAll('aceDrop', {
|
||||||
editorInfo: editorInfo,
|
editorInfo: editorInfo,
|
||||||
|
@ -5051,6 +5054,16 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function topLevel(n)
|
||||||
|
{
|
||||||
|
if ((!n) || n == root) return null;
|
||||||
|
while (n.parentNode != root)
|
||||||
|
{
|
||||||
|
n = n.parentNode;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
function handleIEOuterClick(evt)
|
function handleIEOuterClick(evt)
|
||||||
{
|
{
|
||||||
if ((evt.target.tagName || '').toLowerCase() != "html")
|
if ((evt.target.tagName || '').toLowerCase() != "html")
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
// WARNING: drag and drop is only simulated on these tests, so manual testing might also be necessary
|
||||||
|
describe('drag and drop', function() {
|
||||||
|
before(function(done) {
|
||||||
|
helper.newPad(function() {
|
||||||
|
createScriptWithSeveralLines(done);
|
||||||
|
});
|
||||||
|
this.timeout(60000);
|
||||||
|
});
|
||||||
|
|
||||||
|
context('when user drags part of one line and drops it far form its original place', function() {
|
||||||
|
before(function(done) {
|
||||||
|
selectPartOfSourceLine();
|
||||||
|
dragSelectedTextAndDropItIntoMiddleOfLine(TARGET_LINE);
|
||||||
|
|
||||||
|
// make sure DnD was correctly simulated
|
||||||
|
helper.waitFor(function() {
|
||||||
|
var $targetLine = getLine(TARGET_LINE);
|
||||||
|
var sourceWasMovedToTarget = $targetLine.text() === 'Target line [line 1]';
|
||||||
|
return sourceWasMovedToTarget;
|
||||||
|
}).done(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
context('and user triggers UNDO', function() {
|
||||||
|
before(function() {
|
||||||
|
var $undoButton = helper.padChrome$(".buttonicon-undo");
|
||||||
|
$undoButton.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('moves text back to its original place', function(done) {
|
||||||
|
// test text was removed from drop target
|
||||||
|
var $targetLine = getLine(TARGET_LINE);
|
||||||
|
expect($targetLine.text()).to.be('Target line []');
|
||||||
|
|
||||||
|
// test text was added back to original place
|
||||||
|
var $firstSourceLine = getLine(FIRST_SOURCE_LINE);
|
||||||
|
var $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1);
|
||||||
|
expect($firstSourceLine.text()).to.be('Source line 1.');
|
||||||
|
expect($lastSourceLine.text()).to.be('Source line 2.');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('when user drags some lines far form its original place', function() {
|
||||||
|
before(function(done) {
|
||||||
|
selectMultipleSourceLines();
|
||||||
|
dragSelectedTextAndDropItIntoMiddleOfLine(TARGET_LINE);
|
||||||
|
|
||||||
|
// make sure DnD was correctly simulated
|
||||||
|
helper.waitFor(function() {
|
||||||
|
var $lineAfterTarget = getLine(TARGET_LINE + 1);
|
||||||
|
var sourceWasMovedToTarget = $lineAfterTarget.text() !== '...';
|
||||||
|
return sourceWasMovedToTarget;
|
||||||
|
}).done(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
context('and user triggers UNDO', function() {
|
||||||
|
before(function() {
|
||||||
|
var $undoButton = helper.padChrome$(".buttonicon-undo");
|
||||||
|
$undoButton.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('moves text back to its original place', function(done) {
|
||||||
|
// test text was removed from drop target
|
||||||
|
var $targetLine = getLine(TARGET_LINE);
|
||||||
|
expect($targetLine.text()).to.be('Target line []');
|
||||||
|
|
||||||
|
// test text was added back to original place
|
||||||
|
var $firstSourceLine = getLine(FIRST_SOURCE_LINE);
|
||||||
|
var $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1);
|
||||||
|
expect($firstSourceLine.text()).to.be('Source line 1.');
|
||||||
|
expect($lastSourceLine.text()).to.be('Source line 2.');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ********************* Helper functions/constants ********************* */
|
||||||
|
var TARGET_LINE = 2;
|
||||||
|
var FIRST_SOURCE_LINE = 5;
|
||||||
|
|
||||||
|
var getLine = function(lineNumber) {
|
||||||
|
var $lines = helper.padInner$('div');
|
||||||
|
return $lines.slice(lineNumber, lineNumber + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var createScriptWithSeveralLines = function(done) {
|
||||||
|
// create some lines to be used on the tests
|
||||||
|
var $firstLine = helper.padInner$('div').first();
|
||||||
|
$firstLine.html('...<br>...<br>Target line []<br>...<br>...<br>Source line 1.<br>Source line 2.<br>');
|
||||||
|
|
||||||
|
// wait for lines to be split
|
||||||
|
helper.waitFor(function(){
|
||||||
|
var $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1);
|
||||||
|
return $lastSourceLine.text() === 'Source line 2.';
|
||||||
|
}).done(done);
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectPartOfSourceLine = function() {
|
||||||
|
var $sourceLine = getLine(FIRST_SOURCE_LINE);
|
||||||
|
|
||||||
|
// select 'line 1' from 'Source line 1.'
|
||||||
|
var start = 'Source '.length;
|
||||||
|
var end = start + 'line 1'.length;
|
||||||
|
helper.selectLines($sourceLine, $sourceLine, start, end);
|
||||||
|
}
|
||||||
|
var selectMultipleSourceLines = function() {
|
||||||
|
var $firstSourceLine = getLine(FIRST_SOURCE_LINE);
|
||||||
|
var $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1);
|
||||||
|
|
||||||
|
helper.selectLines($firstSourceLine, $lastSourceLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dragSelectedTextAndDropItIntoMiddleOfLine = function(targetLineNumber) {
|
||||||
|
// dragstart: start dragging content
|
||||||
|
triggerEvent('dragstart');
|
||||||
|
|
||||||
|
// drop: get HTML data from selected text
|
||||||
|
var draggedHtml = getHtmlFromSelectedText();
|
||||||
|
triggerEvent('drop');
|
||||||
|
|
||||||
|
// dragend: remove original content + insert HTML data into target
|
||||||
|
moveSelectionIntoTarget(draggedHtml, targetLineNumber);
|
||||||
|
triggerEvent('dragend');
|
||||||
|
}
|
||||||
|
|
||||||
|
var getHtmlFromSelectedText = function() {
|
||||||
|
var innerDocument = helper.padInner$.document;
|
||||||
|
|
||||||
|
var range = innerDocument.getSelection().getRangeAt(0);
|
||||||
|
var clonedSelection = range.cloneContents();
|
||||||
|
var span = innerDocument.createElement('span');
|
||||||
|
span.id = 'buffer';
|
||||||
|
span.appendChild(clonedSelection);
|
||||||
|
var draggedHtml = span.outerHTML;
|
||||||
|
|
||||||
|
return draggedHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
var triggerEvent = function(eventName) {
|
||||||
|
var event = helper.padInner$.Event(eventName);
|
||||||
|
helper.padInner$('#innerdocbody').trigger(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
var moveSelectionIntoTarget = function(draggedHtml, targetLineNumber) {
|
||||||
|
var innerDocument = helper.padInner$.document;
|
||||||
|
|
||||||
|
// delete original content
|
||||||
|
innerDocument.execCommand('delete');
|
||||||
|
|
||||||
|
// set position to insert content on target line
|
||||||
|
var $target = getLine(targetLineNumber);
|
||||||
|
$target.sendkeys('{selectall}{rightarrow}{leftarrow}');
|
||||||
|
|
||||||
|
// insert content
|
||||||
|
innerDocument.execCommand('insertHTML', false, draggedHtml);
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in New Issue