Skip to content

Instantly share code, notes, and snippets.

@megamaddu
Created July 15, 2016 22:40
Show Gist options
  • Save megamaddu/303ff39c10af8a5933e56279794453a6 to your computer and use it in GitHub Desktop.
Save megamaddu/303ff39c10af8a5933e56279794453a6 to your computer and use it in GitHub Desktop.
module = angular.module 'inputHelpers', []
if not Bacon?
console.log 'window.Bacon not found, skipping input validator initialization'
return
module.constant 'bacon', Bacon
_masks =
ssn: 'ddd-dd-dddd'
ien: 'dd-ddddddd' #ien: 'dd-ddddddd,d-ddddddd'
phone: '(ddd) ddd-dddd'
zip: 'ddddd' #zip: 'ddddd,ddddd-dddd'
module.directive 'input', ['bacon', (Bacon) ->
restrict: 'E'
require: '?ngModel'
link: (scope, element, attr, ctrl) ->
masks = (attr.masks or _masks[attr.type])?.split ','
return if not ctrl or not masks or not masks.length
getVal = -> element.val()
setVal = (val) -> element.val val
getPos = getCursorPositionBinder element
setPos = setCursorPositionBinder element
[lastMask] = masks
if getPos? and setPos?
_keyups = element.asEventStream 'keypress'
.filter isNonMetaCharKeyEvent
.map (e) ->
e.preventDefault()
value = getVal()
key: getKey e
value: value
originalValue: value
position: getPos()
done: false
.map (e) ->
masks.map (mask) -> handlForMask e, mask
.map (results) ->
result = results.reduce (best, result) ->
if not best?
result
else if result.suggestion.value and result.mask is lastMask
result
else if result.suggestion.rating > best.suggestion.rating
result
else
best
lastMask = result.mask
result
.onValue (result) ->
{event, suggestion} = result
if suggestion.value? and suggestion.position?
setVal suggestion.value
setPos suggestion.position
ctrl.$setViewValue suggestion.value
]
anyMaskMatch = (masks, value) ->
masks.some (mask) ->
value? and value.length == mask.length and
isPartialMatchForMask mask, value
isPartialMatchForMask = (mask, value) ->
chars = value.split ''
chars.every (char, i) ->
m = mask[i]
valueCharIsValid char, m
getKey = (e) ->
if e.charCode? then e.charCode else e.keyCode
isNonMetaCharKeyEvent = (e) ->
not e.metaKey and not e.ctrlKey and
isCharKey getKey e
isCharKey = (k) ->
k >= 32 and k <= 126
isNumericKey = (k) ->
k >= 47 and k <= 57
isNonNumericKey = (k) ->
isCharKey k and not isNumericKey k
handlForMask = (e, mask) ->
e = hash e, mask: mask
m = mask[e.position]
suggestion = switch
when not m then suggestCancel e
when m == 'd' then handleNumeric e
when m == 'w' then handleNonNumeric e
when m == '_' then handleWild e
else checkConstantsAndRecurse e
event: e
suggestion: suggestion
done = (e) ->
rating: 1
value: e.value
position: e.position
suggestCancel = (e) ->
rating: 0
value: null
position: null
handleNumeric = (e) ->
if not isNumericKey e.key
suggestCancel e
else
checkConstantsAndInsert e
handleNonNumeric = (e) ->
if not isNonNumericKey e.key
suggestCancel e
else
checkConstantsAndInsert e
handleWild = (e) ->
if not isNumericKey e.key and not isNonNumericKey e.key
suggestCancel e
else
checkConstantsAndInsert e
checkConstantsAndInsert = (e) ->
e = insertOrOverwrite e
e = cleanConstants e
rating: 10 - Math.abs(e.value.length - e.originalValue.length)
value: e.value
position: e.position
checkConstantsAndRecurse = (e) ->
e = cleanConstants e
if e.done
done e
else
handlForMask e, e.mask
.suggestion
cleanConstants = (e) ->
val = e.value
for m, i in e.mask.split '' when i < val.length or i is e.position
do (m, i) ->
return if e.done
isConst = m isnt 'd' and m isnt 'w' and m isnt '_'
e = cleanPos m, isConst, i, e
e
cleanPos = (m, isConst, i, e) ->
{value, position} = e
c = value[i]
if isConst
if position < i and c isnt m or position == i
position += 1
if c isnt m
value = splice value, i, 0, m
else if c? and not valueCharIsValid c, m
position -= i < position ? 1 : 0
value = splice value, i, 1
hash e,
value: value
position: position
done: m is value[i] is String.fromCharCode e.key
splice = (s, i, n, c) ->
s = s.split ''
s.splice i, n, c
s.join ''
valueCharIsValid = (v, m) ->
k = v.charCodeAt 0
switch
when not m then false
when m == 'd' then isNumericKey k
when m == 'w' then isNonNumericKey k
when m == '_' then isNumericKey k or isNonNumericKey k
else m == char
insertOrOverwrite = (e) ->
{value, position, mask} = e
overwrite = value? and mask? and value.length >= mask.length
value = splice value, position, overwrite ? 1 : 0, String.fromCharCode e.key
hash e,
value: value
position: position + 1
hash = (e, v) ->
key: if v.key? then v.key else e.key
value: if v.value? then v.value else e.value
originalValue: if v.originalValue? then v.originalValue else e.originalValue
position: if v.position? then v.position else e.position
mask: if v.mask? then v.mask else e.mask
done: if v.done? then v.done else e.done
getCursorPositionBinder = (element) ->
el = element.get 0
if el?.selectionStart?
() ->
el.selectionStart
else if document.selection?.createRange?
() ->
el.focus()
Sel = document.selection.createRange()
SelLength = document.selection.createRange().text.length
Sel.moveStart 'character', -el.value.length
pos = Sel.text.length - SelLength
pos
else null
setCursorPositionBinder = (element) ->
el = element.get 0
if el.setSelectionRange?
(pos) ->
el.setSelectionRange pos, pos
else if el.createTextRange?
(pos) ->
range = el.createTextRange()
range.collapse true
range.moveEnd 'character', pos
range.moveStart 'character', pos
range.select()
else null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment