Created
January 23, 2010 06:45
-
-
Save grayrest/284468 to your computer and use it in GitHub Desktop.
This file contains 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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"" | |
"http://www.w3.org/TR/html4/strict.dtd"> | |
<html> | |
<head> | |
<meta http-equiv="content-type" content="text/html; charset=utf-8"> | |
<title>jQuery calculator demo</title> | |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.0/jquery.min.js" type="text/javascript" charset="utf-8"></script> | |
</head> | |
<body> | |
<div id="calculator"> | |
<input name="display" class="expr" id="expr" value="" size="25" /> | |
</div> | |
<script type="text/javascript"> | |
//I'm being lazy here and using a global for the text field. | |
var expr = $('#expr'); | |
// The list of operations we support in the calc. | |
var operations = { | |
'\u221A' : Math.sqrt, | |
'x\u00B2' : function(x){return x * x;}, | |
'\u00B1' : function(x){return x * -1;}, | |
'cos' : Math.cos, | |
'sin' : Math.sin, | |
'tan' : Math.tan, | |
'ln' : Math.log, | |
'exp' : Math.exp, | |
'AC' : function(){ expr.val('0'); }, | |
'Del\u2190': function(){ expr.val(expr.val().slice(0,-1)); }, | |
'=' : function(){ | |
try{ | |
expr.val(eval(expr.val() || 0)) | |
} catch (e) { | |
//clear if we have an evaluation error somehow | |
operations['AC'](); | |
} | |
} | |
}; | |
// given a character code, appends the appropriate char onto the | |
// end of the text input. | |
var key_literal = function(which){ | |
expr.val(expr.val() + String.fromCharCode(which)) | |
} | |
// keys bound to operations. This set is meant to be called | |
// using keyDown. The each loop does the actual binding. | |
var key_ops = { | |
'13' : '=', // Enter | |
'107' : '=', // = | |
'8' : 'Del\u2190', // BS | |
'27' : 'AC' // Esc | |
}; | |
$.each(key_ops, function(k,fn){ | |
key_ops[k] = operations[fn]; | |
}); | |
// The set of valid input characters to our text field. | |
// There are plugins to do this, but this is a key handling | |
// demo. | |
var key_map = $.extend(key_map,{ | |
'43' : key_literal, // + | |
'45' : key_literal, // - | |
'42' : key_literal, // * | |
'47' : key_literal, // / | |
'40' : key_literal, // ( | |
'41' : key_literal, // ) | |
'46' : key_literal // . | |
}); | |
// this loop tacks the digits onto the literals list | |
for(var i = 48; i < 58; i++){ | |
key_map[''+i] = key_literal; | |
} | |
// Okay, here's the main event. The important thing to know is | |
// that there are TWO types of key events in javascript: | |
// keydown/keyup and keypress. | |
// | |
// The keydown/keyup pair fires one time on the falling/rising | |
// edge of the keystroke, respectively. They use the RAW key | |
// codes from the keyboard and you check them (in jQuery) using | |
// keyCode. This raw keyCode makes them useful for checking | |
// for Escape, Arrow Keys, and anything else that isn't | |
// normally printable. | |
// | |
// The main reason you don't want to use this for printable | |
// characters is due to international keyboard maps. The | |
// shift+4 returns the same value as normal 4. If you're using | |
// a US keyboard layout you might naïvely get the code, check | |
// the shiftKey and assume the user typed $. This would cause | |
// the incorrect value to be entered for all the other peoples | |
// in the world. | |
// | |
// The other event is the keypress event. This will fire | |
// repeatedly if the user holds down a key and you check the | |
// value (in jQuery) using whcih. This attribute will contain | |
// the ASCII value for the associated character allowing you to | |
// use String.fromCharCode to look up the appropriate character | |
// and insert it in your doc. This won't work for most | |
// non-printable characters. | |
// This key handler inserts one of our (valid only) printable | |
// characters into our text area. Since it's on the document, | |
// it will actually work with the focus anywhere in the page. | |
$(document).keypress(function(e){ | |
if(!e.ctrlKey && !e.altKey && !e.metaKey){ | |
if(e.which in key_map) key_map[e.which](e.which); | |
} | |
}); | |
// This is the matching page-wide operations handler. It takes | |
// care of the non-printable characters we care about. | |
$(document).keydown(function(e){ | |
if(!e.ctrlKey && !e.altKey && !e.metaKey){ | |
if(e.keyCode in key_ops) key_ops[e.keyCode](); | |
} | |
}); | |
//This handler specially handles the case when we're in the | |
//text field. | |
expr.keypress(function(e){ | |
if(!e.ctrlKey && !e.altKey && !e.metaKey){ | |
if(!(e.which in key_map)){ | |
//don't allow non-valid keys | |
e.preventDefault(); | |
} else { | |
// prevent key_literal from moving the insertion | |
// point. It's annoying to click in the middle to | |
// edit and have your typed text jump to the end | |
e.stopPropagation(); | |
} | |
} | |
}); | |
// With all the setup out of the way, the calculator itself is | |
// super-easy. Instead of mucking about with all the markup, | |
// I'll build it programatically. | |
// | |
// Note that we have a functional keyboard calculator at this | |
// point without the need for buttons. The buttons provide us | |
// some extra functions but aren't at all necessary for the | |
// basic functioning of our calculator. | |
var calculator_layout = [ | |
['AC' , '(' , ')' , 'Del\u2190'], | |
['cos' , 'sin' , 'tan' ], | |
['\u221A' , 'x\u00B2' , 'exp' , 'ln'], | |
['7' , '8' , '9' , '/' ], | |
['4' , '5' , '6' , '*' ], | |
['1' , '2' , '3' , '-' ], | |
['0' , '.' , '\u00B1' , '+' ], | |
['=']]; | |
// Given the above layout, put each row in a div and turn each | |
// string into a button showing the corresponding text. | |
// | |
// Note that if you're inserting a large amount of nodes or | |
// have complex CSS rules, it's much faster do append to a | |
// documentFragment and then swap in/append the fragment.Doing | |
// it this way is slightly easier but forces a layout reflow | |
// every time we do the append to #calculator. | |
$.each(calculator_layout, function(i, row){ | |
var el_row = $('<div class="calc_row"></div>'); | |
$.each(row, function(j, key){ | |
el_row.append('<input type="button" class="calckey" value="'+key+'" />'); | |
}); | |
$('#calculator').append(el_row); | |
}); | |
// Now that our buttons are created, we select them all... | |
$('.calckey') | |
// style them to get them looking grid-like... | |
.css('width','45px') | |
// and set up the click behavior on each... | |
.click(function(e){ | |
var self = $(this), | |
val = self.val(); | |
if(val in operations){ | |
// This test is some javascript ninja magic. The | |
// .length property on a function is the number of | |
// arguments it expects. The operations just happen | |
// to be written so that all the mathematic | |
// operatons take one parameter. | |
if(operations[val].length == 1){ | |
operations['='](); //evaluate | |
expr.val(operations[val](parseFloat(expr.val()))); | |
} else { | |
operations[val](); | |
} | |
} else { | |
expr.val(expr.val() + val) | |
} | |
}) | |
// And I'll special case the equals button so it looks | |
// nice. You could also use .find('input[value="="]') | |
// instead of .last() | |
.last().css('width',45*4+'px'); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment