Skip to content
Open
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
110 changes: 73 additions & 37 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ Pattern.prototype.formatValue = function format(value) {
var valueBuffer = new Array(this.length)
var valueIndex = 0

if (this.isRevealingMask && value.length === 0) {
return valueBuffer
}

for (var i = 0, l = this.length; i < l; i++) {
if (this.isEditableIndex(i)) {
if (this.isRevealingMask &&
Expand Down Expand Up @@ -233,40 +237,44 @@ InputMask.prototype.input = function input(char) {

var inputIndex = this.selection.start

// If the cursor or selection is prior to the first editable character, make
// sure any input given is applied to it.
if (inputIndex < this.pattern.firstEditableIndex) {
inputIndex = this.pattern.firstEditableIndex
// Find next editable index
var nextEditableIndex = inputIndex
while (!this.pattern.isEditableIndex(nextEditableIndex)) {
if (nextEditableIndex > this.pattern.lastEditableIndex) {
return false
}
nextEditableIndex++
}

// Bail out or add the character to input
if (this.pattern.isEditableIndex(inputIndex)) {
if (!this.pattern.isValidAtIndex(char, inputIndex)) {
return false
if (!this.pattern.isValidAtIndex(char, nextEditableIndex)) {
return false
}

// Add statics until next editable
while (inputIndex < nextEditableIndex) {
this.value[inputIndex] = this.pattern.pattern[inputIndex]
inputIndex++
}

this.value[inputIndex] = this.pattern.transform(char, inputIndex)

// If this is the last editable index fill with the rest
if (inputIndex === this.pattern.lastEditableIndex) {
while (inputIndex + 1 < this.pattern.length - 1) {
inputIndex++
this.value[inputIndex] = this.pattern.pattern[inputIndex]
}
this.value[inputIndex] = this.pattern.transform(char, inputIndex)
}

// If multiple characters were selected, blank the remainder out based on the
// pattern.
var end = this.selection.end - 1
while (end > inputIndex) {
if (this.pattern.isEditableIndex(end)) {
this.value[end] = this.placeholderChar
}
end--
if (inputIndex + 1 < this.selection.end) {
this.remove(inputIndex + 1, this.selection.end - 1)
}

// Advance the cursor to the next character
this.selection.start = this.selection.end = inputIndex + 1

// Skip over any subsequent static characters
while (this.pattern.length > this.selection.start &&
!this.pattern.isEditableIndex(this.selection.start)) {
this.selection.start++
this.selection.end++
}

// History
if (this._historyIndex != null) {
// Took more input after undoing, so blow any subsequent history away
Expand Down Expand Up @@ -299,26 +307,26 @@ InputMask.prototype.backspace = function backspace() {
var selectionBefore = copy(this.selection)
var valueBefore = this.getValue()

// No range selected - work on the character preceding the cursor
// No range selected
if (this.selection.start === this.selection.end) {
if (this.pattern.isEditableIndex(this.selection.start - 1)) {
this.value[this.selection.start - 1] = this.placeholderChar
var previousEditableIndex = this.selection.start - 1

while (!this.pattern.isEditableIndex(previousEditableIndex)) {
if (previousEditableIndex === 0) {
break
}
previousEditableIndex--
}
this.selection.start--
this.selection.end--

this.remove(previousEditableIndex, this.selection.end)
this.selection.start = previousEditableIndex
}
// Range selected - delete characters and leave the cursor at the start of the selection
else {
var end = this.selection.end - 1
while (end >= this.selection.start) {
if (this.pattern.isEditableIndex(end)) {
this.value[end] = this.placeholderChar
}
end--
}
this.selection.end = this.selection.start
this.remove(this.selection.start, this.selection.end - 1)
}

this.selection.end = this.selection.start

// History
if (this._historyIndex != null) {
// Took more input after undoing, so blow any subsequent history away
Expand Down Expand Up @@ -381,7 +389,7 @@ InputMask.prototype.paste = function paste(input) {
if (!valid) {
if (this.selection.start > 0) {
// XXX This only allows for one static character to be skipped
var patternIndex = this.selection.start - 1
var patternIndex = this.selection.start
if (!this.pattern.isEditableIndex(patternIndex) &&
input.charAt(i) === this.pattern.pattern[patternIndex]) {
continue
Expand All @@ -395,6 +403,34 @@ InputMask.prototype.paste = function paste(input) {
return true
}

InputMask.prototype.remove = function remove(start, end) {
if (this.pattern.isRevealingMask) {
this.value.splice(start, end - start)

var index = start
while (index < this.value.length) {
if (!this.pattern.isEditableIndex(index)) {
this.value.splice(index, 0, this.pattern.pattern[index])
index++
}
else if (this.pattern.isValidAtIndex(this.value[index], index)) {
index++
}
else {
this.value.splice(index, 1)
}
}
}
else {
while (end >= start) {
if (this.pattern.isEditableIndex(end)) {
this.value[end] = this.placeholderChar
}
end--
}
}
}

// History

InputMask.prototype.undo = function undo() {
Expand Down
21 changes: 10 additions & 11 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ test('Escaping placeholder characters', function(t) {
})

test('Basic input', function(t) {
t.plan(23)
t.plan(26)

var mask = new InputMask({
pattern: '1111 1111 1111 1111'
Expand All @@ -156,18 +156,21 @@ test('Basic input', function(t) {
t.true(mask.input('2'), 'Valid input accepted')
t.true(mask.input('3'), 'Valid input accepted')
t.true(mask.input('4'), 'Valid input accepted')
t.deepEqual(mask.selection, {start: 5, end: 5}, 'Skipped over blank')
t.deepEqual(mask.selection, {start: 4, end: 4}, 'Keep in position')
t.true(mask.input('1'), 'Valid input accepted')
t.deepEqual(mask.selection, {start: 6, end: 6}, 'Skipped over blank after input')
t.true(mask.input('2'), 'Valid input accepted')
t.true(mask.input('3'), 'Valid input accepted')
t.true(mask.input('4'), 'Valid input accepted')
t.deepEqual(mask.selection, {start: 10, end: 10}, 'Skipped over blank')
t.deepEqual(mask.selection, {start: 9, end: 9}, 'Keep in position')
t.true(mask.input('1'), 'Valid input accepted')
t.deepEqual(mask.selection, {start: 11, end: 11}, 'Skipped over blank')
t.true(mask.input('2'), 'Valid input accepted')
t.true(mask.input('3'), 'Valid input accepted')
t.true(mask.input('4'), 'Valid input accepted')
t.deepEqual(mask.selection, {start: 15, end: 15}, 'Skipped over blank')
t.deepEqual(mask.selection, {start: 14, end: 14}, 'Keep in position')
t.true(mask.input('1'), 'Valid input accepted')
t.deepEqual(mask.selection, {start: 16, end: 16}, 'Skipped over blank')
t.true(mask.input('2'), 'Valid input accepted')
t.true(mask.input('3'), 'Valid input accepted')
t.true(mask.input('4'), 'Valid input accepted')
Expand Down Expand Up @@ -256,7 +259,7 @@ test('Skipping multiple static characters', function(t) {
})

test('Basic backspacing', function(t) {
t.plan(24)
t.plan(21)

var mask = new InputMask({
pattern: '1111 1111 1111 1111',
Expand All @@ -268,22 +271,18 @@ test('Basic backspacing', function(t) {
t.true(mask.backspace(), 'Valid backspace accepted')
t.true(mask.backspace(), 'Valid backspace accepted')
t.true(mask.backspace(), 'Valid backspace accepted')
// Backspacking doesn't automatically skip characters, as we can't tell when
// the user intends to start making input again, so it just steps over static
// parts of the mask when you backspace with the cursor ahead of them.
t.true(mask.backspace(), 'Skipped over blank')
// Backspacking automatically skip characters, and goes to the
// previous valid editable character
t.true(mask.backspace(), 'Valid backspace accepted')
t.true(mask.backspace(), 'Valid backspace accepted')
t.true(mask.backspace(), 'Valid backspace accepted')
t.true(mask.backspace(), 'Valid backspace accepted')
t.equal(mask.getValue(), '1234 1234 ____ ____', 'Intermediate value')
t.deepEqual(mask.selection, {start: 10, end: 10}, 'Cursor remains in front of last deleted character')
t.true(mask.backspace(), 'Skipped over blank')
t.true(mask.backspace(), 'Valid backspace accepted')
t.true(mask.backspace(), 'Valid backspace accepted')
t.true(mask.backspace(), 'Valid backspace accepted')
t.true(mask.backspace(), 'Valid backspace accepted')
t.true(mask.backspace(), 'Skipped over blank')
t.true(mask.backspace(), 'Valid backspace accepted')
t.true(mask.backspace(), 'Valid backspace accepted')
t.true(mask.backspace(), 'Valid backspace accepted')
Expand Down