-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Fix for #6742: Handle invalid timing function params #6757
Conversation
* @param {Array} def The default params to replace invalid values with | ||
* @return { isNumber: boolean, value: number } | ||
*/ | ||
function getValidBezierParams(match, def) { |
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.
I think we can do this easier, but how to?
Any ideas are very appreciated.
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.
Instead of having "easy" and "complex" regexes, rename them to be "strict" and "lax". Then follow the pattern of "strict" versus "lax" used by the other regexes. "lax" is used when initially parsing user data in page and "strict" is used by editor reparsing strings it already knows to be valid (to get offsets for replacing in doc).
For the getValidBezierParams(match, def)
function, I think match[1]
will contain the parameter string, so you'll need to do something like the following to separate them into an array:
var params = match[1].split(",");
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.
I'll rename the regexes, but we need to do a little bit more repositioning to do that.
Even if we do .split()
, we still need for loop to loop through the entries, don't we?
Btw, match[0]
contains the parameter string, but the cubic-bezier(
prefix is included, too.
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.
Yes, you have to loop through the parameters. Use match[1]
because you want only parameters.
} else { | ||
return TimingFunctionUtils.getValidBezierParams(match, [ ".42", "0", ".58", "1" ]); | ||
// take ease-in-out as default value in case there are no params yet (or they are invalid) | ||
} | ||
} else { |
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.
You shouldn't need to do any of this here because this only gets called once we're already in editor, so data should already be valid.
The initial validation gets done in main.js in prepareEditorForProvider()
in the call to TimingFunctionUtils.timingFunctionMatch()
. The conversion to default parameters should get done on TimingFunctionUtils.timingFunctionMatch()
.
Also, I think values for ease-in-out
should only be used when there are no parameters supplied to cubic-bezier()
. For values out-of-range, they should be moved to closest value in range. For example, the range of valid values for x values (first and third parameters) is 0-1, so values less than 0 should be set to 0 and values greater than 1 should be set to 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.
- The
.valid
flag is set if_validateCubicBezierParams()
passes. Should I change the name of the flag? - That's a good idea, but we still need to insert some values in case there are non-numeric values (e.g. "invalid").
@redmunds Just added another commit with the requested changes included, could you please take a look, too? |
No, I don't think the match array should change. All of your changes should be in Starting with these regexes:
First make the original call:
If that fails, then try with BEZIER_CURVE_LAX_REGEX. If that matches, then call Then run the new string that was created against the strict regex pattern:
Then This keeps all processing in |
s# Please enter the commit message for your changes. Lines starting
@redmunds Just pushed changes again. I hope this was what you meant, |
*/ | ||
function _getValidBezierParams(match, def) { | ||
var param, | ||
old_index = match.index, // we need to store the old match.index to re-set the index afterwards |
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.
Is there a better way to store and re-set this later?
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.
I don't think this is necessary since the string will be re-matched against BEZIER_CURVE_STRICT_REGEX at the end.
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.
But the index is set to another value because there is no more context (first few chars in row), and the index is used to set the cm selection later on.
Without re-setting, the selection will be on the wrong place (this is the reason why this code is there).
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.
Gotcha. If we update the index, then I think the replacement string will be wrong when we make first update to doc after an edit.
Is that correct? If so, then I see 2 options:
- Immediately update doc with updated params. I was hoping to avoid this, but I guess it's not so bad since user can undo.
- Keep
match
object that same but add on a new property which is an array ofvalidatedParams
.
Other suggestions?
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.
What do you mean? An update caused by the Inline Editor, or an update while the Inline Editor is open?
The first one seemed to work (with the match.index set like this), even if I did not test that much. Haven't tested the latter yet.
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.
My concern is with the first edit after the params have been inserted/corrected. If that works, then I think it's OK.
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.
Works - just tested.
@redmunds I hope this is finally the right one. |
match = str.match(BEZIER_CURVE_VALID_REGEX); | ||
if (match && _validateCubicBezierParams(match)) { // cubic-bezier() with valid params | ||
return _tagMatch(match, BEZIER); | ||
} else { |
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.
You don't need this else statement since the if clause returns.
@SAplayer This works pretty great! I made a couple minor comments about the code. I am able to see the "length" error I was concerned about. Start with Works great for initial value of |
Thanks for the repro steps. I may know where this comes from. Btw, this may also happen with multiple css values in a line: |
Adding a new property is good, but the term "real" can be confusing. Maybe |
Hey @redmunds, just pushed a few commits to finally fix the nasty selection bugs and implement this feature for |
One way to |
Ah thanks, that works (but is a bit laggy when doing further changes). |
@@ -491,6 +491,7 @@ define({ | |||
"INLINE_TIMING_EDITOR_PROGRESSION" : "Progression", | |||
"BEZIER_EDITOR_INFO" : "<kbd>↑</kbd><kbd>↓</kbd><kbd>←</kbd><kbd>→</kbd> Move selected point<br><kbd class='text'>Shift</kbd> Move by ten units<br><kbd class='text'>Tab</kbd> Switch points", | |||
"STEPS_EDITOR_INFO" : "<kbd>↑</kbd><kbd>↓</kbd> Increase or decrease steps<br><kbd>←</kbd><kbd>→</kbd> 'Start' or 'End'", | |||
"INLINE_TIMING_EDITOR_INVALID" : "The old value <code>{0}</code> is not valid, so the displayed one was changed to <code>{1}</code>. The document will be updated with the first edit.", |
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.
One more wording tweak: change "the displayed one" to be a little more descriptive such as "the displayed function".
@@ -530,6 +530,9 @@ define(function (require, exports, module) { | |||
// current cubic-bezier() function params | |||
this._cubicBezierCoords = this._getCubicBezierCoords(bezierCurve); | |||
|
|||
this.hint = $(".hint", this.$element); | |||
TimingFunctionUtils.showHideHint(this, bezierCurve.originalLength, bezierCurve.originalString, "cubic-bezier(" + this._cubicBezierCoords.join(", ") + ")"); |
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 usage of originalLength
is a bit too magical here. It needs to be easier for the next dev to figure out this code. I'm thinking of something like:
// If function was auto-corrected, then originalString and originalLength hold the original function,
// and an informational message need to be shown
var show = (bezierCurve.originalLength ? true : false);
if (show) {
TimingFunctionUtils.showHideHint(this, true, bezierCurve.originalString, "cubic-bezier(" + this._cubicBezierCoords.join(", ") + ")");
} else {
TimingFunctionUtils.showHideHint(this, false);
}
This also has the benefit of only building and passing the display strings in the correction case (which is not the usual case).
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.
Actually, if you are storing originalString
then there's no need to store originalLength
, is there? Can't you just use originalString.length
?
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.
Will do. Can I do show = !!bezierCurve.originalString.length
as well (is this accepted within the coding standard?)
I thought about removing originalLength
as well.
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.
It's not necessary to create the show
var, and originalString
may not exist so maybe:
// If function was auto-corrected, then originalString holds the original function,
// and an informational message need to be shown
if (bezierCurve.originalString) {
TimingFunctionUtils.showHideHint(this, true, bezierCurve.originalString, "cubic-bezier(" + this._cubicBezierCoords.join(", ") + ")");
} else {
TimingFunctionUtils.showHideHint(this, false);
}
Done with first final review :) |
Pushed changes, tests still passing. |
Fixed the animation to not use Maybe @couzteau for the german translation? |
@@ -107,12 +234,44 @@ define(function (require, exports, module) { | |||
function _validateStepsParams(match) { | |||
var count = _convertToNumber(match[1]); | |||
|
|||
if (!count.isNumber || count.value <= 0) { | |||
if (!count.isNumber || count.value < 1 || Math.floor(count.value) !== count.value) { |
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.
Is there a better method to check for an integer?
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.
You could probably use Lo-Dash http://lodash.com/docs#isFinite?
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.
Have you tried this? I think it will accept values like 2.0
as they are still finite...
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.
Just tested it - it's not testing for an integer, but for a (finite) number. So 2.0
or even 2.1
are still valid.
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.
No, I haven't tested it... I just read the documentation I thought it might work. I am not sure why it does that, doesn't seem that useful, but there isn't an isInteger function.
So I guess your method is fine. Here are other methods if you want to check http://stackoverflow.com/questions/3885817/how-to-check-if-a-number-is-float-or-integer
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.
No problem.
It's just checking that the input is not ±Infinity, "", true, false, null or undefined.
But I can't believe it's that difficult in JavaScript to check for an integer - it's a built-in function in most other languages.
I will still use this solution as it should still work for things like 1.0
(some on StackOverflow don't).
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.
I know... But is comming: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
Too bad is not supported in Brackets yet :(
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.
Ah yes, they are using the same check with floor
as I do ;) (we don't need the other checks).
It would be cool if we had NumberUtils
in Brackets to do such checks.
@ingorichter or @pthiess - could review the German strings? |
This looks good. After the German strings are reviewed, then just need to rebase. |
This PR is for #6742 @redmunds
Todo:
cubic-bezier()
(according to @redmunds, we should useease-in-out
here)cubic-bezier(-5, 0.7, 8, "invalid")
(only replace the invalid ones with default values)cubic-bezier(0, 0, 0)
with an invalid number of params (only replace the missing/invalid ones with default values)steps()
steps("invalid", 2)
(only replace the invalid ones with default values)steps(1, 2, 3)
with an invalid number of params (only replace the missing/invalid ones with default values)