Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛Fix deleting from the middle of dates #20656

Merged
merged 1 commit into from
Feb 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions third_party/inputmask/bundle.js

Large diffs are not rendered by default.

92 changes: 82 additions & 10 deletions third_party/inputmask/inputmask.date.extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export function factory(Inputmask) {
}
return result;
},
onKeyDown: function(e, buffer, caretPos, opts) {
onKeyDown: function(e, buffer, caretPos, opts, initial, previousPos) {
var input = this;
if (e.ctrlKey && e.keyCode === Inputmask.keyCode.RIGHT) {
var today = new Date(), match, date = "";
Expand All @@ -253,21 +253,77 @@ export function factory(Inputmask) {
$(input).trigger("setvalue");
}

const {begin, end} = previousPos;
// If the user presses backspace in the middle of the input
// value, stop masking until the field is cleared.
if (e.keyCode === Inputmask.keyCode.BACKSPACE) {
if (e.keyCode === Inputmask.keyCode.BACKSPACE ||
e.keyCode === Inputmask.keyCode.DELETE ||
((e.metaKey || e.ctrlKey) && e.keyCode === 'X'.charCodeAt(0))) {
const length = input.value.length;
const cursorIsInMiddle = input.selectionStart != length &&
input.selectionEnd != length;
if (length > 0 && cursorIsInMiddle) {
const cachedController = input.inputmask;
input.inputmask.remove();
input.addEventListener('input', function onclear() {
if (input.value.length == 0) {
input.removeEventListener('input', onclear);
cachedController.mask(input);
}
});
disableMaskingUntilClear(input);

// The library tries to be smart about moving the cursor
// and autofilling values, but here we want to just
// delete the text that was selected like a normal
// input.
// So we take the original selection and the origina
// value before the library auto-changed them,
// and apply the edits ourselves here.
const difference = end - begin;
if (e.keyCode === Inputmask.keyCode.BACKSPACE) {
const selectionBegin =
difference == 0 ? begin - 1 : begin;
input.value =
initial.slice(0, selectionBegin) +
initial.slice(end);
input.setSelectionRange(
selectionBegin, selectionBegin);
} else if (e.keyCode === Inputmask.keyCode.DELETE) { // delete
const selectionEnd =
difference == 0 ? end + 1 : end;
input.value =
initial.slice(0, begin) +
initial.slice(selectionEnd);
input.setSelectionRange(begin, begin);
}
}
return;
}

// If the user enters text with a selection and causes delete...
if (previousPos.begin != previousPos.end) {
// ... via paste
if ((e.metaKey || e.ctrlKey) && e.keyCode === 'V'.charCodeAt(0)) {
disableMaskingUntilClear(input);
return;
}

// Don't handle CTRL+C/CMD+C or CTRL or CMD alone
if (e.metaKey || e.ctrlKey) {
return;
}

// ... via typing a char
const fromCharCode = String.fromCharCode(e.keyCode);
const char = e.shiftKey ?
fromCharCode.toLocaleUpperCase() :
fromCharCode.toLocaleLowerCase();
if (!/\s/.test(char)) {
disableMaskingUntilClear(input);
// The library still sets the value after the keydown
// handler returns, so we need to asynchronously
// set the value here.
setTimeout(() => {
input.value =
initial.slice(0, begin) +
char +
initial.slice(end);
input.setSelectionRange(
begin + 1, begin + 1);
}, 0);
}
}
},
Expand All @@ -283,5 +339,21 @@ export function factory(Inputmask) {
shiftPositions: false
}
});

/**
* When the user clears the input, reapply the
* inputmask behavior.
* @param {!Element} input
*/
function disableMaskingUntilClear(input) {
const cachedController = input.inputmask;
input.inputmask.remove();
input.addEventListener('input', function onclear() {
if (input.value.length == 0) {
input.removeEventListener('input', onclear);
cachedController.mask(input);
}
});
}
return Inputmask;
}
4 changes: 3 additions & 1 deletion third_party/inputmask/inputmask.js
Original file line number Diff line number Diff line change
Expand Up @@ -1699,6 +1699,8 @@ export function factory($, window, document, undefined) {
var EventHandlers = {
keydownEvent: function(e) {
var input = this, $input = $(input), k = e.keyCode, pos = caret(input);
const originalPos = {begin: input.selectionStart, end: input.selectionEnd};
const initial = input.value;
if (k === Inputmask.keyCode.BACKSPACE || k === Inputmask.keyCode.DELETE || iphone && k === Inputmask.keyCode.BACKSPACE_SAFARI || (e.ctrlKey || e.metaKey) && k === Inputmask.keyCode.X && !isInputEventSupported("cut")) {
e.preventDefault();

Expand Down Expand Up @@ -1745,7 +1747,7 @@ export function factory($, window, document, undefined) {
caret(input, pos.begin, pos.end);
}
}
opts.onKeyDown.call(this, e, getBuffer(), caret(input).begin, opts);
opts.onKeyDown.call(this, e, getBuffer(), caret(input).begin, opts, initial, originalPos);
ignorable = $.inArray(k, opts.ignorables) !== -1;
},
keypressEvent: function(e, checkval, writeOut, strict, ndx) {
Expand Down
144 changes: 144 additions & 0 deletions third_party/inputmask/patches/0014-delete-middle-date.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
diff --git a/third_party/inputmask/inputmask.date.extensions.js b/third_party/inputmask/inputmask.date.extensions.js
index ea5f730fb..fb45ed966 100755
--- a/third_party/inputmask/inputmask.date.extensions.js
+++ b/third_party/inputmask/inputmask.date.extensions.js
@@ -234,7 +234,7 @@ export function factory(Inputmask) {
}
return result;
},
- onKeyDown: function(e, buffer, caretPos, opts) {
+ onKeyDown: function(e, buffer, caretPos, opts, initial, previousPos) {
var input = this;
if (e.ctrlKey && e.keyCode === Inputmask.keyCode.RIGHT) {
var today = new Date(), match, date = "";
@@ -253,21 +253,77 @@ export function factory(Inputmask) {
$(input).trigger("setvalue");
}

+ const {begin, end} = previousPos;
// If the user presses backspace in the middle of the input
// value, stop masking until the field is cleared.
- if (e.keyCode === Inputmask.keyCode.BACKSPACE) {
+ if (e.keyCode === Inputmask.keyCode.BACKSPACE ||
+ e.keyCode === Inputmask.keyCode.DELETE ||
+ ((e.metaKey || e.ctrlKey) && e.keyCode === 'X'.charCodeAt(0))) {
const length = input.value.length;
const cursorIsInMiddle = input.selectionStart != length &&
input.selectionEnd != length;
if (length > 0 && cursorIsInMiddle) {
- const cachedController = input.inputmask;
- input.inputmask.remove();
- input.addEventListener('input', function onclear() {
- if (input.value.length == 0) {
- input.removeEventListener('input', onclear);
- cachedController.mask(input);
- }
- });
+ disableMaskingUntilClear(input);
+
+ // The library tries to be smart about moving the cursor
+ // and autofilling values, but here we want to just
+ // delete the text that was selected like a normal
+ // input.
+ // So we take the original selection and the origina
+ // value before the library auto-changed them,
+ // and apply the edits ourselves here.
+ const difference = end - begin;
+ if (e.keyCode === Inputmask.keyCode.BACKSPACE) {
+ const selectionBegin =
+ difference == 0 ? begin - 1 : begin;
+ input.value =
+ initial.slice(0, selectionBegin) +
+ initial.slice(end);
+ input.setSelectionRange(
+ selectionBegin, selectionBegin);
+ } else if (e.keyCode === Inputmask.keyCode.DELETE) { // delete
+ const selectionEnd =
+ difference == 0 ? end + 1 : end;
+ input.value =
+ initial.slice(0, begin) +
+ initial.slice(selectionEnd);
+ input.setSelectionRange(begin, begin);
+ }
+ }
+ return;
+ }
+
+ // If the user enters text with a selection and causes delete...
+ if (previousPos.begin != previousPos.end) {
+ // ... via paste
+ if ((e.metaKey || e.ctrlKey) && e.keyCode === 'V'.charCodeAt(0)) {
+ disableMaskingUntilClear(input);
+ return;
+ }
+
+ // Don't handle CTRL+C/CMD+C or CTRL or CMD alone
+ if (e.metaKey || e.ctrlKey) {
+ return;
+ }
+
+ // ... via typing a char
+ const fromCharCode = String.fromCharCode(e.keyCode);
+ const char = e.shiftKey ?
+ fromCharCode.toLocaleUpperCase() :
+ fromCharCode.toLocaleLowerCase();
+ if (!/\s/.test(char)) {
+ disableMaskingUntilClear(input);
+ // The library still sets the value after the keydown
+ // handler returns, so we need to asynchronously
+ // set the value here.
+ setTimeout(() => {
+ input.value =
+ initial.slice(0, begin) +
+ char +
+ initial.slice(end);
+ input.setSelectionRange(
+ begin + 1, begin + 1);
+ }, 0);
}
}
},
@@ -283,5 +339,21 @@ export function factory(Inputmask) {
shiftPositions: false
}
});
+
+ /**
+ * When the user clears the input, reapply the
+ * inputmask behavior.
+ * @param {!Element} input
+ */
+ function disableMaskingUntilClear(input) {
+ const cachedController = input.inputmask;
+ input.inputmask.remove();
+ input.addEventListener('input', function onclear() {
+ if (input.value.length == 0) {
+ input.removeEventListener('input', onclear);
+ cachedController.mask(input);
+ }
+ });
+ }
return Inputmask;
}
diff --git a/third_party/inputmask/inputmask.js b/third_party/inputmask/inputmask.js
index 6ab796f7e..7539512c5 100644
--- a/third_party/inputmask/inputmask.js
+++ b/third_party/inputmask/inputmask.js
@@ -1699,6 +1699,8 @@ export function factory($, window, document, undefined) {
var EventHandlers = {
keydownEvent: function(e) {
var input = this, $input = $(input), k = e.keyCode, pos = caret(input);
+ const originalPos = {begin: input.selectionStart, end: input.selectionEnd};
+ const initial = input.value;
if (k === Inputmask.keyCode.BACKSPACE || k === Inputmask.keyCode.DELETE || iphone && k === Inputmask.keyCode.BACKSPACE_SAFARI || (e.ctrlKey || e.metaKey) && k === Inputmask.keyCode.X && !isInputEventSupported("cut")) {
e.preventDefault();

@@ -1745,7 +1747,7 @@ export function factory($, window, document, undefined) {
caret(input, pos.begin, pos.end);
}
}
- opts.onKeyDown.call(this, e, getBuffer(), caret(input).begin, opts);
+ opts.onKeyDown.call(this, e, getBuffer(), caret(input).begin, opts, initial, originalPos);
ignorable = $.inArray(k, opts.ignorables) !== -1;
},
keypressEvent: function(e, checkval, writeOut, strict, ndx) {