forked from siege-media/contrast-ratio
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathincrementable.js
138 lines (106 loc) · 3.66 KB
/
incrementable.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
* Script for making multiple numbers in a textfield incrementable/decrementable (like Firebug's CSS values)
* @author Lea Verou
* @version 1.1
*/
(function(){
/**
* Constructor
* @param textField {HTMLElement} An input or textarea element
* @param multiplier {Function} A function that accepts the event object and returns the multiplier or 0 for nothing to happen.
*/
var _ = window.Incrementable = function(textField, multiplier, units) {
var me = this;
this.textField = textField;
this.step = +textField.getAttribute('step') ||
+textField.getAttribute('data-step') || 1;
this.multiplier = multiplier || function(evt) {
if (evt.shiftKey) { return 10; }
if (evt.ctrlKey) { return .1; }
return 1;
}
if (units) {
this.units = units;
}
this.changed = false;
this.textField.addEventListener('keydown', function(evt) {
var multiplier = me.multiplier(evt);
if (multiplier && (evt.keyCode == 38 || evt.keyCode == 40)) {
me.changed = false;
// Up or down arrow pressed, check if there's something
// increment/decrement-able where the caret is
var caret = this.selectionStart,
text = this.value,
regex = new RegExp('^([\\s\\S]{0,' + caret + '}[^-0-9\\.])?(-?[0-9]*(?:\\.?[0-9]+)(?:' + me.units + '))\\b', 'i'),
property = 'value' in this? 'value' : 'textContent';
this[property] = this[property].replace(regex, function($0, $1, $2) {
$1 = $1 || '';
if ($1.length <= caret && $1.length + $2.length >= caret) {
var stepValue = me.stepValue($2, evt.keyCode == 40, multiplier);
caret = caret + (stepValue.length - $2.length);
me.changed = {
add: stepValue,
del: $2,
start: $1.length
};
return $1 + stepValue;
}
else {
return $1 + $2;
}
});
if (me.changed) {
this.setSelectionRange(caret, caret);
evt.preventDefault();
evt.stopPropagation();
// Fire input event
var evt = document.createEvent("HTMLEvents");
evt.initEvent('input', true, true );
evt.add = me.changed.add;
evt.del = me.changed.del;
evt.start = me.changed.start;
evt.incrementable = true;
this.dispatchEvent(evt);
}
}
}, false);
this.textField.addEventListener('keypress', function(evt) {
if (me.changed && (evt.keyCode == 38 || evt.keyCode == 40))
evt.preventDefault();
evt.stopPropagation();
me.changed = false;
}, false);
}
_.prototype = {
/**
* Gets a <length> and increments or decrements it
*/
stepValue: function(length, decrement, multiplier) {
var val = parseFloat(length),
offset = (decrement? -1 : 1) * (multiplier || 1) * this.step,
valPrecision = precision(val),
offsetPrecision = precision(offset);
// Prevent rounding errors
var newVal = (parseFloat((val + offset).toPrecision(
Math.max(valPrecision.integer, offsetPrecision.integer) +
Math.max(valPrecision.decimals, offsetPrecision.decimals)
)));
return newVal + length.replace(/^-|[0-9]+|\./g, '');
},
units: '|%|deg|px|r?em|ex|ch|in|cm|mm|pt|pc|vmin|vmax|vw|vh|gd|m?s'
};
function precision(number) {
number = (number + '').replace(/^0+/, '');
var dot = number.indexOf('.');
if (dot === -1) {
return {
integer: number.length,
decimals: 0
};
}
return {
integer: dot,
decimals: number.length - 1 - dot
};
}
})();