Posts Tagged ‘javascript’

Keyboard Event Handling with FBJS

Thursday, February 19th, 2009

FBJS event handling does not follow the default Facebook JavaScript event handling model exactly. Facebook re-writes the event object as a far more limited object, meaning you have access to less information than you would in a true Javascript environment. If you’re looking for information on how browsers handle the onKey* events, quirksmode is a fantastic reference, albeit Windows centric. I ran into this yesterday while looking to improve a user interface. This following illustrates some of the potential gotchas to keep an eye out for.

Test Code

The following code will capture all events related to that form element and send them to the console log. This is going to be a whole lot of information, so note where you can dial it back as needed. For my project I was able to modify the str return value to just include the fields I was concerned with.

<script type="text/javascript">
function captureKeyboardEvent(e){
	var evt = e || window.event;
	var str = evt.type;
	// to make this less verbose - comment out the following 3 lines
	for (var property in evt){
		str += "\n  -" + property + " = " + evt[property];
	}
	console.log(str);
}
</script>
<form>
<input type="text" 
		onKeyPress="logEvent(event);" 
		onKeyDown="logEvent(event);"
		onKeyUp = "logEvent(event);"
		onFocus = "logEvent(event);"
		onBlur = "logEvent(event);"
	/>
</form>

Using Safari on a Mac produces the following output. What struck me as curious was the presence of pageX and pageY. As you can see the keyCode value is returned but not the charCode, timestamp or many other values as illustrated by the same code run without the benefit of the FBML parser.

keydown
 -type = keydown
 -ctrlKey = false
 -keyCode = 65
 -metaKey = false
 -shiftKey = false
 -target = [object Object]
 -pageX = NaN
 -pageY = NaN
 -__priv = 3
 -preventDefault = function () 
{
  var data = fbjs_private.get(this);
    ...
  data.return_value = false;
}
 -stopPropagation = function () 
{
  var event = fbjs_private.get(this).event;
    ...
    }
}
keydown
  -keyLocation = 0
  -ctrlKey = false
  -shiftKey = false
  -keyIdentifier = U+0041
  -altKey = false
  -metaKey = false
  -altGraphKey = false
  -pageY = 0
  -layerY = 0
  -pageX = 0
  -charCode = 0
  -view = [object DOMWindow]
  -which = 65
  -keyCode = 65
  -detail = 0
  -layerX = 0
  -returnValue = true
  -timeStamp = 1235049247482
  -eventPhase = 2
  -target = [object HTMLInputElement]
  -srcElement = [object HTMLInputElement]
  -type = keydown
  -clipboardData = undefined
  -cancelable = true
  -currentTarget = [object HTMLInputElement]
  -bubbles = true
  -cancelBubble = false
  -initKeyboardEvent = function initKeyboardEvent() {
    [native code]
}
  -initUIEvent = function initUIEvent() {
    [native code]
}
  -preventDefault = function preventDefault() {
    [native code]
}
  -initEvent = function initEvent() {
    [native code]
}
  -stopPropagation = function stopPropagation() {
    [native code]
}
  -MOUSEOUT = 8
  -FOCUS = 4096
  -CHANGE = 32768
  -MOUSEMOVE = 16
  -AT_TARGET = 2
  -SELECT = 16384
  -BLUR = 8192
  -KEYUP = 512
  -MOUSEDOWN = 1
  -MOUSEDRAG = 32
  -BUBBLING_PHASE = 3
  -MOUSEUP = 2
  -CAPTURING_PHASE = 1
  -MOUSEOVER = 4
  -CLICK = 64
  -DBLCLICK = 128
  -KEYDOWN = 256
  -KEYPRESS = 1024
  -DRAGDROP = 2048

Why does this matter?

If your creating code that looks at keypress input, there are some differences between browser implementations, what values are stored within the keyCode value and how Facebook passes those events on. In IE, Firefox and Safari, when a key is pressed down a keydown event is fired. When that key is released, a keyup event is fired. Pretty straightforward right? Here’s where it gets complicated.

As pointed out on Quirksmode, browsers handle the repeating event differently. Some place data in the charCode or keyCode values depending on context. Additionally, other modifier keys such as function and control keys are passed as boolean fields. However, we only get keyCode in FBJS. Furthermore, Safari and IE don’t ever raise the repeating keypress event like Firefox does, instead raising the keydown event only! However, Firefox does not raise the keydown event and the keypress event only passes back a keyCode of ‘0′.

‘Tis the Season of the New TV

Friday, December 12th, 2008

Classic Television Televisions are always a hot topic for holiday purchases and this year is no different. I was reviewing my Google Analytics numbers for AF-Design this morning and saw the annual spike in traffic to my television size calculator. This calculator is something I cobbled together one afternoon 3 or 4 years ago when trying to determine the amount of space a screen was going to take up. Surprisingly, every November and December, this page becomes one of the most visited pages on AF-Design. This year the spike is a little higher, probably due to the impending digital TV conversion. As such, I’ve created a minor update to the page to include optimal viewing distance for the screen size being calculated. Calculators of this nature are hardly complex to write, but apparently there’s a market for explaining them. Perhaps one day I’ll re-write the calculator to make it more flexible. Until then, enjoy shopping for that new set.

So That’s What Broke!

Thursday, November 27th, 2008

MySpace has a good post explaining why you can’t use relative links within OpenSocial applications. If you’re building for the OpenSocial container and trying to make valid HTML apps, watch out for this one. I’ve written the following in a lot of applications and suspect that others have as well. The #na below will cause problems, despite being valid markup.

<a href="#na" onClick="doJavascriptCall();">Click Me</a>

Consider the following ways to invoke functionality instead:

<a href="javascript:;" onClick="doJavascriptCall();">Click Me</a>
<div onClick="doJavascriptCall();">Click Me</div>

jQuery Autocomplete Plugin

Monday, October 20th, 2008

jQuery Logo jQuery users, take a look at the autocomplete plugin which is shipping in the UI framework. While there are some serious limitations to the currently plugin, it does provides a drop dead simple way to provide Google Suggest style functionality to your site users without significant overhead. It’s widely supported and weighs in at 78K (without data/css and markup) for the minified components.

My one complaint is the lack of JSON support for remotely fetched results. Currently you must return items for the suggest widget as plain text list. You can, however, currently parse individual lines (which could be objects). I expect the jQuery team will be adding this functionality soon enough. Jörn Zaefferer has built a modified version of the plugin that supports JSON. I’m holding out until it’s merged into the UI trunk to limit my sources of code updates in this instance.

Below is a quick example of the syntax so you can see how easy it is to create. The file, names.php, simply returns a series of names, 1 per line. This might be static or pulled from a database based on the passed “q” get parameter that holds the contents of the input area.

jQuery Autocomplete Plugin Screenshot

jQuery Autocomplete Plugin Screenshot

<link rel="stylesheet" href="css/jquery.autocomplete.css" type="text/css" />
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="ui.core.min.js"></script>
<script type="text/javascript" src="jquery.bgiframe.min.js"></script>
<script type="text/javascript" src="jquery.dimensions.min.js"></script>
<script type="text/javascript" src="ui.autocomplete.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
   $("#name").autocomplete("names.php");
});
</script>
<form>
   Name <input type="text" id="name" name="name" value="" /><input type="button" value="Go" />
</form>

Updated JavaScript UUID Generator v.0.3

Friday, September 5th, 2008

The JavaScript UUID Generator class has been refreshed again and is even more efficient than the 0.2 version due largely in part to heavier reliance on built in JavaScript functions. It’s still not a “REAL” UUID, but it’s good enough for most projects. The changes are as follows:

  • A leaner and more efficient returnBase(val, base) method for conversion to base16 strings. The original version from irt.org has been replaced with built in JavaScript methods. Some testing for your target platforms should be performed to ensure compatibility with your engine.
  • The change from randrange(min, max) in v.0.1 to rand(max) in 0.2 introduced a bug where the maximum value provided would never be matched. This has been fixed to correct for that issue.

Special thanks go out to Robert Kieffer for pointing out these issues and providing the updated code for the base 16 conversion.

Download the latest version.

View Code JAVASCRIPT
/*
uuid.js - Version 0.3
JavaScript Class to create a UUID like identifier
 
Copyright (C) 2006-2008, Erik Giberti (AF-Design), All rights reserved.
 
This program is free software; you can redistribute it and/or modify it under 
the terms of the GNU General Public License as published by the Free Software 
Foundation; either version 2 of the License, or (at your option) any later 
version.
 
This program is distributed in the hope that it will be useful, but WITHOUT ANY 
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
PARTICULAR PURPOSE. See the GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License along with 
this program; if not, write to the Free Software Foundation, Inc., 59 Temple 
Place, Suite 330, Boston, MA 02111-1307 USA
 
The latest version of this file can be downloaded from
http://www.af-design.com/resources/javascript_uuid.php
 
HISTORY:
6/5/06 	- Initial Release
5/22/08 - Updated code to run faster, removed randrange(min,max) in favor of
          a simpler rand(max) function. Reduced overhead by using getTime() 
          method of date class (suggestion by James Hall).
9/5/08	- Fixed a bug with rand(max) and additional efficiencies pointed out 
	  by Robert Kieffer http://broofa.com/
 
KNOWN ISSUES:
- Still no way to get MAC address in JavaScript
- Research into other versions of UUID show promising possibilities 
  (more research needed)
- Documentation needs improvement
 
*/
 
// On creation of a UUID object, set it's initial value
function UUID(){
	this.id = this.createUUID();
}
 
// When asked what this Object is, lie and return it's value
UUID.prototype.valueOf = function(){ return this.id; }
UUID.prototype.toString = function(){ return this.id; }
 
//
// INSTANCE SPECIFIC METHODS
//
 
UUID.prototype.createUUID = function(){
	//
	// Loose interpretation of the specification DCE 1.1: Remote Procedure Call
	// described at http://www.opengroup.org/onlinepubs/009629399/apdxa.htm#tagtcjh_37
	// since JavaScript doesn't allow access to internal systems, the last 48 bits 
	// of the node section is made up using a series of random numbers (6 octets long).
	//  
	var dg = new Date(1582, 10, 15, 0, 0, 0, 0);
	var dc = new Date();
	var t = dc.getTime() - dg.getTime();
	var h = '-';
	var tl = UUID.getIntegerBits(t,0,31);
	var tm = UUID.getIntegerBits(t,32,47);
	var thv = UUID.getIntegerBits(t,48,59) + '1'; // version 1, security version is 2
	var csar = UUID.getIntegerBits(UUID.rand(4095),0,7);
	var csl = UUID.getIntegerBits(UUID.rand(4095),0,7);
 
	// since detection of anything about the machine/browser is far to buggy, 
	// include some more random numbers here
	// if NIC or an IP can be obtained reliably, that should be put in
	// here instead.
	var n = UUID.getIntegerBits(UUID.rand(8191),0,7) + 
			UUID.getIntegerBits(UUID.rand(8191),8,15) + 
			UUID.getIntegerBits(UUID.rand(8191),0,7) + 
			UUID.getIntegerBits(UUID.rand(8191),8,15) + 
			UUID.getIntegerBits(UUID.rand(8191),0,15); // this last number is two octets long
	return tl + h + tm + h + thv + h + csar + csl + h + n; 
}
 
 
//
// GENERAL METHODS (Not instance specific)
//
 
 
// Pull out only certain bits from a very large integer, used to get the time
// code information for the first part of a UUID. Will return zero's if there 
// aren't enough bits to shift where it needs to.
UUID.getIntegerBits = function(val,start,end){
	var base16 = UUID.returnBase(val,16);
	var quadArray = new Array();
	var quadString = '';
	var i = 0;
	for(i=0;i<base16.length;i++){
		quadArray.push(base16.substring(i,i+1));	
	}
	for(i=Math.floor(start/4);i<=Math.floor(end/4);i++){
		if(!quadArray[i] || quadArray[i] == '') quadString += '0';
		else quadString += quadArray[i];
	}
	return quadString;
}
 
// Replaced from the original function to leverage the built in methods in
// JavaScript. Thanks to Robert Kieffer for pointing this one out
UUID.returnBase = function(number, base){
	return (number).toString(base).toUpperCase();
}
 
// pick a random number within a range of numbers
// int b rand(int a); where 0 <= b <= a
UUID.rand = function(max){
	return Math.floor(Math.random() * (max + 1));
}
 
// end of UUID class file

Documentation of JavaScript DOM

Tuesday, August 5th, 2008

Just found this excellent resource outlining the DOM in JavaScript. It’s clear and to the point spelling out what methods and properties are supported by the major browsers. Thanks Mark for putting it together.

JavaScript UUID Generator v. 0.2

Thursday, May 22nd, 2008

Nearly 2 years ago I crafted some JavaScript code to create a properly formatted UUID in JavaScript. Full compliance with specification for version 1 UUID’s still isn’t possible. However, in the interest of keeping the project updated, I’ve made some changes to the code to make it more reliable and more efficient.

  • It now use getTime() from the date object. This reduced the time spent calculating unnecessary values for the timestamp (idea came from James Hall’s canonical name function).
  • The randrange() function was replaced with one that more evenly distributes the random number values while additionally reducing complexity.

Get the download here.

I’d also like to give a shout to some of the folks who have mentioned it in forums, ported it to different libraries, linked to it from Wikipedia, blogs etc and have generally made my efforts worth while. Thank You!

View Code JAVASCRIPT
/*
 
uuid.js - Version 0.2
JavaScript Class to create a UUID like identifier
 
Copyright (C) 2006-2008, Erik Giberti (AF-Design), All rights reserved.
 
This program is free software; you can redistribute it and/or modify it under 
the terms of the GNU General Public License as published by the Free Software 
Foundation; either version 2 of the License, or (at your option) any later 
version.
 
This program is distributed in the hope that it will be useful, but WITHOUT ANY 
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
PARTICULAR PURPOSE. See the GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License along with 
this program; if not, write to the Free Software Foundation, Inc., 59 Temple 
Place, Suite 330, Boston, MA 02111-1307 USA
 
The latest version of this file can be downloaded from
http://www.af-design.com/resources/javascript_uuid.php
 
HISTORY:
6/5/06 	- Initial Release
5/22/08 - Updated code to run faster, removed randrange(min,max) in favor of
          a simpler rand(max) function. Reduced overhead by using getTime() 
          method of date class (suggestion by James Hall).
 
KNOWN ISSUES:
- Still no way to get MAC address in JavaScript
- Research into other versions of UUID show promising possibilities 
  (more research needed)
- Documentation needs improvement
 
*/
 
// On creation of a UUID object, set it's initial value
function UUID(){
	this.id = this.createUUID();
}
 
// When asked what this Object is, lie and return it's value
UUID.prototype.valueOf = function(){ return this.id; }
UUID.prototype.toString = function(){ return this.id; }
 
//
// INSTANCE SPECIFIC METHODS
//
 
UUID.prototype.createUUID = function(){
	//
	// Loose interpretation of the specification DCE 1.1: Remote Procedure Call
	// described at http://www.opengroup.org/onlinepubs/009629399/apdxa.htm#tagtcjh_37
	// since JavaScript doesn't allow access to internal systems, the last 48 bits 
	// of the node section is made up using a series of random numbers (6 octets long).
	//  
	var dg = new Date(1582, 10, 15, 0, 0, 0, 0);
	var dc = new Date();
	var t = dc.getTime() - dg.getTime();
	var h = '-';
	var tl = UUID.getIntegerBits(t,0,31);
	var tm = UUID.getIntegerBits(t,32,47);
	var thv = UUID.getIntegerBits(t,48,59) + '1'; // version 1, security version is 2
	var csar = UUID.getIntegerBits(UUID.rand(4095),0,7);
	var csl = UUID.getIntegerBits(UUID.rand(4095),0,7);
 
	// since detection of anything about the machine/browser is far to buggy, 
	// include some more random numbers here
	// if NIC or an IP can be obtained reliably, that should be put in
	// here instead.
	var n = UUID.getIntegerBits(UUID.rand(8191),0,7) + 
			UUID.getIntegerBits(UUID.rand(8191),8,15) + 
			UUID.getIntegerBits(UUID.rand(8191),0,7) + 
			UUID.getIntegerBits(UUID.rand(8191),8,15) + 
			UUID.getIntegerBits(UUID.rand(8191),0,15); // this last number is two octets long
	return tl + h + tm + h + thv + h + csar + csl + h + n; 
}
 
 
//
// GENERAL METHODS (Not instance specific)
//
 
 
// Pull out only certain bits from a very large integer, used to get the time
// code information for the first part of a UUID. Will return zero's if there 
// aren't enough bits to shift where it needs to.
UUID.getIntegerBits = function(val,start,end){
	var base16 = UUID.returnBase(val,16);
	var quadArray = new Array();
	var quadString = '';
	var i = 0;
	for(i=0;i<base16.length;i++){
		quadArray.push(base16.substring(i,i+1));	
	}
	for(i=Math.floor(start/4);i<=Math.floor(end/4);i++){
		if(!quadArray[i] || quadArray[i] == '') quadString += '0';
		else quadString += quadArray[i];
	}
	return quadString;
}
 
// Numeric Base Conversion algorithm from irt.org
// In base 16: 0=0, 5=5, 10=A, 15=F
UUID.returnBase = function(number, base){
	//
	// Copyright 1996-2006 irt.org, All Rights Reserved.	
	//
	// Downloaded from: http://www.irt.org/script/146.htm	
	// modified to work in this class by Erik Giberti
	var convert = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
    if (number < base) var output = convert[number];
    else {
        var MSD = '' + Math.floor(number / base);
        var LSD = number - MSD*base;
        if (MSD >= base) var output = this.returnBase(MSD,base) + convert[LSD];
        else var output = convert[MSD] + convert[LSD];
    }
    return output;
}
 
// pick a random number within a range of numbers
// int b rand(int a); where 0 <= b <= a
UUID.rand = function(max){
	return Math.floor(Math.random() * max);
}
 
// end of UUID class file
© 1998-2008 AF-Design, All rights reserved.