Skip to content

Instantly share code, notes, and snippets.

@grapho
Last active February 10, 2016 23:01
Show Gist options
  • Save grapho/4db2aa08244afa8a0321 to your computer and use it in GitHub Desktop.
Save grapho/4db2aa08244afa8a0321 to your computer and use it in GitHub Desktop.
One-Way Number Input
import Ember from 'ember';
export default Ember.Controller.extend({
appName:'Ember Twiddle'
});
<h1>One Way Number Input</h1>
<br>
<br>
{{one-way-numeral}}
<br>
<br>
<input type="number" inputmode="numeric" step="0.01">
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')
});
{
"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