Conversation
Removes _selectedImage.
selection = wym.selection(); | ||
imageRange = rangy.createRangyRange(); | ||
imageRange.selectNode(evt.target); | ||
selection.setSingleRange(imageRange); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The crux of this PR.
Simply selects, using Rangy, the image that was clicked upon.
No more unsetting & setting wym._selectedImage
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a simple mechanism for selecting images. It could be more pretty, but it is simple and works in all browsers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous mechanism didn't work well enough, because selection wasn't set to the image and there was otherwise no indication that the image was indeed selected. Users don't look at the debugger and put a watch on jQuery.wymeditors(0)._selectedImage
, ya know?
$container = jQuery(wym.selectedContainer()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means that toggling the class of an img
isn't possible.
Does it seem important? Regression?
I notice that we don't have tests for toggleClass
.
"Image is the only selected node" | ||
); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A test for image selection, instead of the test for _selectedImage
.
manipulationTestHelper({ | ||
startHtml: noChangeHtml, | ||
manipulationFunc: function (wymeditor) { | ||
wymeditor.$body().find("img").mousedown(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The manipulation in this test is a jQuery.fn.mousedown()
on the image.
Conflicts: src/test/unit/specific_feature_tests/images.js src/wymeditor/editor/base.js
Conflicts: src/wymeditor/editor/base.js
If selection encompasses exactly a single image, returns that image. | ||
Otherwise returns `false`. | ||
*/ | ||
WYMeditor.editor.prototype.getSelectedImage = function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New!
wymeditor = jQuery.wymeditors(0); | ||
|
||
// Stop QUnit from running the next test | ||
stop(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than the deprecated stop()
and start()
, why not use assert.async and done()
? This line would be var done = assert.async();
and the start()
line would be done()
I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would require an upgrade of QUnit, I believe.
module("images-selection", {setup: prepareUnitTestModule}); | ||
|
||
test("Image is selected via `mouseup` in non pre-7 Trident", function () { | ||
manipulationTestHelper({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@winhamwr you're right. The async: true
here was just left by me here. This serves to show that turning that switch on by mistake could result in a false passing test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hrm. What can we do about that? Seems like expect()
is the thing designed to avoid those kind of false positives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See fix in manipulationTestHelper
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
* assertions. This can only be used when a single manipulation cause | ||
* is provided (for example, only `manipulationFunc`). | ||
* For example: | ||
* ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@winhamwr replaced resume
with resumeManipulationTestHelper
in the example below. Not runAssertions
because it is more than assertions—it is also undo/redo operations. And also, by the time it returns, some assertions were already executed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
} | ||
|
||
return asyncResumeFunc; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@winhamwr you're right. This is much cleaner. I thought about writing it like this, at first, but setting it inside a while
loop statement didn't seem reasonable, until now that I realized that in the case when a.async === true
, that loop will execute once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this code functions correctly, it reads poorly. There's no reason to return here if we're not using async
(even though the caller should just ignore the return result). Let's put it inside an if (a.async === true)
block.
return; | ||
// Expectancy incremented here in order to fail tests that specify | ||
// `async` but do not call the `asyncResumeFunc`. | ||
expect(expect() + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the fix for the case where a test specifies async: true
but does not call the returned function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clever fix!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
// Unwrap | ||
wymeditor._selectSingleNode = _selectSingleNode; | ||
// Resume `manipulationTestHelper` | ||
resumeManipulationTestHelper(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The resume() function when called in your wrapped _selectSingleNode isn't yet set to the return value of manipulationTestHelper because that function hasn't yet returned. Having a hard time making heads or tails of this.
- The scope of this call is a function definition, an assignment to
wymeditor._selectSingleNode
.wymeditor._selectSingleNode
will be called later, triggered by pre–7 Trident'smouseup
. - It is ok that
resumeManipulationTestHelper()
, which is not defined (undefined
) at the time of this function's definition, is included inside, because it is currently set toundefined
. If you try to call it now, it would be like doingundefined()
and that will probably yield something like "undefined does not have propertycall
" or something like that. - Since the call to
wymeditor._selectSingleNode
is inside asetTimeout
(with zero time) it will run only (and probably immediately) afterresumeManipulationTestHelper
had returned. Thus, in the scope of the anonymous function of thistest
, by that time, when it does run, in this scoperesumeManipulationTestHelper
would have already been the return value ofmanipulationTestHelper
.
I hope this is clear enough :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// we test things that have to do with the browser's DOM API. Removing this | ||
// or setting it to `true` will almost certainly cause all tests of | ||
// asynchronous things to fail or the test runner to stop and not continue. | ||
sinon.config.useFakeTimers = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@winhamwr this explains it, right?
// undesired resize handles. | ||
// Wrapping the selection call in an immediate `setTimeout` makes | ||
// reasonably certain that the "control selection" will be very quickly | ||
// replaced by our desired, regular selection. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@winhamwr at your request, a comment here.
// https://github.com/wymeditor/wymeditor/pull/641 | ||
window.setTimeout(function () { | ||
wym._selectSingleNode(evt.target); | ||
}, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@winhamwr regarding zero–time setTimeout
, I highly recommend this talk.
In short, the browser holds a sequential stack of calls and it runs them ASAP, sequentially. When one call ends, the next can start.
setTimeout
adds a call on top of that stack, after the specified time. A zero time means that it gets throw on that stack immediately.
In our case, when an image gets a mouse event, some various named and unnamed events occur. Amongst them is mouseup
, which we have a handler for. In pre–7 Trident, that handler throws a selection action on top of the call queue stack, with setTimeout
with zero time. After that, the controlselect
is triggered and the resize handles appear. After that, synchronous code had finished running and the browser's event loop executes the next call in the queue stack, which is almost certainly the selection call which we had previously thrown there from mouseup
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Man, that sounds like bad design. But it seems solid.
Other than the two minor tweaks I suggested, this looks ready to merge! |
@@ -64,6 +64,33 @@ | |||
* ``` | |||
* This example uses the `jquery.browser` plugin | |||
* https://github.com/gabceb/jquery-browser-plugin | |||
* `async` | |||
* Optional; If this is `true` then after the manipulation is | |||
* performed, assertions regarding the results are not synchronously |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added comma here.
} | ||
|
||
if (a.async === true) { | ||
return asyncResumeFunc; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@winhamwr you asked for this return to be conditional. Here it is. Thanks.
Great! Thank you! |
Removes _selectedImage.