Last active
February 10, 2016 23:01
-
-
Save grapho/4db2aa08244afa8a0321 to your computer and use it in GitHub Desktop.
One-Way Number Input
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.Controller.extend({ | |
appName:'Ember Twiddle' | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.Component.extend({ | |
tagName: 'input', | |
type: "number", | |
attributeBindings: [ | |
'accept', | |
'autocomplete', | |
'autosave', | |
'checked', | |
'dir', | |
'disabled', | |
'formaction', | |
'formenctype', | |
'formmethod', | |
'formnovalidate', | |
'formtarget', | |
'height', | |
'inputmode', | |
'lang', | |
'list', | |
'max', | |
'maxlength', | |
'min', | |
'multiple', | |
'name', | |
'pattern', | |
'placeholder', | |
'size', | |
'step', | |
'type', | |
'value', | |
'width' | |
], | |
KEY_EVENTS: { | |
'13': 'onenter', | |
'27': 'onescape' | |
}, | |
_sanitizedValue: undefined, | |
separatorChar: ',', | |
decimalChar: '.', | |
allownegative: false, | |
decimalPlaces: 2, | |
_evaluateKeyPress(event) { | |
let element = this.$('input')[0], | |
control = this.$('input'), | |
// get decimal character and determine if negatives are allowed | |
decimal = this.get('decimal'), | |
negative = this.get('negative'), | |
decimalPlaces = this.get('decimalPlaces'), | |
// get the key that was pressed | |
key = e.charCode ? parseInt(e.charCode, 10) : e.keyCode ? parseInt(e.keyCode, 10) : 0; | |
// allow enter/return key (only when in an input box) | |
if (key === 13 && element.nodeName.toLowerCase() === "input") { | |
return true; | |
} | |
else if (key === 13) { | |
return false; | |
} | |
var allow = false; | |
// allow Ctrl+A | |
if ((e.ctrlKey && key === 97) /* firefox */ || (e.ctrlKey && key === 65) /* opera */) { return true; } | |
// allow Ctrl+X (cut) | |
if ((e.ctrlKey && key === 120) /* firefox */ || (e.ctrlKey && key === 88) /* opera */) { return true; } | |
// allow Ctrl+C (copy) | |
if ((e.ctrlKey && key === 99) /* firefox */ || (e.ctrlKey && key === 67) /* opera */) { return true; } | |
// allow Ctrl+Z (undo) | |
if ((e.ctrlKey && key === 122) /* firefox */ || (e.ctrlKey && key === 90) /* opera */) { return true; } | |
// allow or deny Ctrl+V (paste), Shift+Ins | |
if ((e.ctrlKey && key === 118) /* firefox */ || (e.ctrlKey && key === 86) /* opera */ || (e.shiftKey && key === 45)) { return true; } | |
// if a number was not pressed | |
if (key < 48 || key > 57) { | |
var value = control.val(); | |
/* '-' only allowed at start and if negative numbers allowed */ | |
if (parseInt(Ember.$.inArray('-', value.split('')), 10) !== 0 && negative && key === 45 && (value.length === 0 || parseInt(this.getSelectionStart(element), 10) === 0)) { return true; } | |
/* only one decimal separator allowed */ | |
if (decimal && key === parseInt(decimal.charCodeAt(0), 10) && parseInt(Ember.$.inArray(decimal, value.split('')), 10) !== -1) { | |
allow = false; | |
} | |
// check for other keys that have special purposes | |
if (key !== 8 /* backspace */ && | |
key !== 9 /* tab */ && | |
key !== 13 /* enter */ && | |
key !== 35 /* end */ && | |
key !== 36 /* home */ && | |
key !== 37 /* left */ && | |
key !== 39 /* right */ && | |
key !== 46 /* del */) { | |
allow = false; | |
} | |
else { | |
// for detecting special keys (listed above) | |
// IE does not support 'charCode' and ignores them in keypress anyway | |
if (typeof e.charCode !== "undefined") { | |
// special keys have 'keyCode' and 'which' the same (e.g. backspace) | |
if (parseInt(e.keyCode, 10) === parseInt(e.which, 10) && parseInt(e.which, 10) !== 0) { | |
allow = true; | |
// . and delete share the same code, don't allow . (will be set to true later if it is the decimal point) | |
if (parseInt(e.which, 10) === 46) { allow = false; } | |
} | |
// or keyCode != 0 and 'charCode'/'which' = 0 | |
else if (parseInt(e.keyCode, 10) !== 0 && parseInt(e.charCode, 10) === 0 && parseInt(e.which, 10) === 0) { | |
allow = true; | |
} | |
} | |
} | |
// if key pressed is the decimal and it is not already in the field | |
if (decimal && key === parseInt(decimal.charCodeAt(0), 10)) { | |
allow = parseInt(Ember.$.inArray(decimal, value.split('')), 10) === -1; | |
} | |
} | |
else { | |
allow = true; | |
// remove extra decimal places | |
if (decimal && decimalPlaces > 0) { | |
var selectionStart = this.getSelectionStart(element); | |
var selectionEnd = this.getSelectionEnd(element); | |
var dot = Ember.$.inArray(decimal, control.val().split('')); | |
if (selectionStart === selectionEnd && dot >= 0 && selectionStart > dot && control.val().length > dot + decimalPlaces) { | |
allow = false; | |
} | |
} | |
} | |
return allow; | |
}, | |
keyPress: function(e) { | |
return this._evaluateKeyPress(event); | |
}, | |
keyUp: function() { | |
var element = this.$('input')[0], | |
control = this.$('input'), | |
value = control.val(); | |
if (value && value.length > 0) { | |
// get carat (cursor) position | |
var carat = this.getSelectionStart(element); | |
var selectionEnd = this.getSelectionEnd(element); | |
// get decimal character and determine if negatives are allowed | |
var decimal = this.get('decimal'); | |
var negative = this.get('negative'); | |
var decimalPlaces = this.get('decimalPlaces'); | |
var dot; | |
// prepend a 0 if necessary | |
if (decimal !== "" && decimal !== null) { | |
// find decimal point | |
dot = parseInt(Ember.$.inArray(decimal, value.split('')), 10); | |
// if dot at start, add 0 before | |
if (dot === 0) { | |
element.value = "0" + value; | |
carat++; | |
selectionEnd++; | |
} | |
// if dot at position 1, check if there is a - symbol before it | |
if (dot === 1 && value.charAt(0) === "-") { | |
element.value = "-0" + value.substring(1); | |
carat++; | |
selectionEnd++; | |
} | |
value = element.value; | |
} | |
// if pasted in, only allow the following characters | |
var validChars = ['0','1','2','3','4','5','6','7','8','9','-',decimal]; | |
// get length of the value (to loop through) | |
var length = value.length; | |
// loop backwards (to prevent going out of bounds) | |
for (var i = length - 1; i >= 0; i--) { | |
var ch = value.charAt(i); | |
// remove '-' if it is in the wrong place | |
if (i !== 0 && ch === "-") { | |
value = value.substring(0, i) + value.substring(i + 1); | |
} | |
// remove character if it is at the start, a '-' and negatives aren't allowed | |
else if (i === 0 && !negative && ch === "-") { | |
value = value.substring(1); | |
} | |
var validChar = false; | |
// loop through validChars | |
for (var j = 0; j < validChars.length; j++) { | |
// if it is valid, break out the loop | |
if (ch === validChars[j]) { | |
validChar = true; | |
break; | |
} | |
} | |
// if not a valid character, or a space, remove | |
if (!validChar || ch === " ") { | |
value = value.substring(0, i) + value.substring(i + 1); | |
} | |
} | |
// remove extra decimal characters | |
var firstDecimal = parseInt(Ember.$.inArray(decimal, value.split('')), 10); | |
if (firstDecimal > 0) { | |
for (var k = length - 1; k > firstDecimal; k--) { | |
var chch = value.charAt(k); | |
// remove decimal character | |
if (chch === decimal) { | |
value = value.substring(0, k) + value.substring(k + 1); | |
} | |
} | |
} | |
// remove extra decimal places | |
if (decimal && decimalPlaces > 0) { | |
dot = parseInt(Ember.$.inArray(decimal, value.split('')), 10); | |
if (dot >= 0) { | |
value = value.substring(0, dot + decimalPlaces + 1); | |
selectionEnd = Math.min(value.length, selectionEnd); | |
} | |
} | |
// set the value and prevent the cursor moving to the end | |
element.value = value; | |
this.setSelection(element, [carat, selectionEnd]); | |
} | |
}, | |
focusOut: function() { | |
var decimal = this.get('decimal'), | |
//callback = this.get('callback'), | |
negative = this.get('negative'), | |
value = this.get('value'); | |
if (value !== "") { | |
var reg = new RegExp(negative?"-?":"" + "^\\d+$|^\\d*" + decimal + "\\d+$"); | |
if (!reg.exec(value)) { | |
//callback.apply(this); | |
} | |
} | |
this.set('hasFocus', false); | |
this.get('on-blur')(); | |
}, | |
// Based on code from http://javascript.nwbox.com/cursor_position/ (Diego Perini <[email protected]>) | |
getSelectionStart: function(o) { | |
if (o.type === "number") { | |
return undefined; | |
} | |
else if (o.createTextRange && document.selection) { | |
var r = document.selection.createRange().duplicate(); | |
r.moveEnd('character', o.value.length); | |
if (r.text === '') { return o.value.length; } | |
return Math.max(0, o.value.lastIndexOf(r.text)); | |
} | |
else { | |
try { return o.selectionStart; } | |
catch(e) { return 0; } | |
} | |
}, | |
// Based on code from http://javascript.nwbox.com/cursor_position/ (Diego Perini <[email protected]>) | |
getSelectionEnd: function(o) { | |
if (o.type === "number") { | |
return undefined; | |
} | |
else if (o.createTextRange && document.selection) { | |
var r = document.selection.createRange().duplicate(); | |
r.moveStart('character', -o.value.length); | |
return r.text.length; | |
} | |
else { | |
return o.selectionEnd; | |
} | |
}, | |
// set the selection, o is the object (input), p is the position ([start, end] or just start) | |
setSelection: function(o, p) { | |
// if p is number, start and end are the same | |
if (typeof p === "number") { p = [p, p]; } | |
// only set if p is an array of length 2 | |
if (p && p.constructor === Array && p.length === 2) { | |
if (o.type === "number") { | |
o.focus(); | |
} | |
else if (o.createTextRange) { | |
var r = o.createTextRange(); | |
r.collapse(true); | |
r.moveStart('character', p[0]); | |
r.moveEnd('character', p[1] - p[0]); | |
r.select(); | |
} | |
else { | |
o.focus(); | |
try { | |
if (o.setSelectionRange) { | |
o.setSelectionRange(p[0], p[1]); | |
} | |
} | |
catch(e) {} | |
} | |
} | |
}, | |
destroyCurrencyMask: function() { | |
this.setProperties({ | |
decimal: null, | |
negative: null, | |
callback: null, | |
decimalPlaces: null | |
}); | |
}.on('willDestroyElement') | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"version": "0.5.0", | |
"EmberENV": { | |
"FEATURES": {} | |
}, | |
"options": { | |
"enable-testing": false | |
}, | |
"dependencies": { | |
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js", | |
"ember": "https://cdnjs.cloudflare.com/ajax/libs/ember.js/2.2.0/ember.debug.js", | |
"ember-data": "https://cdnjs.cloudflare.com/ajax/libs/ember-data.js/2.2.0/ember-data.js", | |
"ember-template-compiler": "https://cdnjs.cloudflare.com/ajax/libs/ember.js/2.2.0/ember-template-compiler.js" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment