Mr. Softwarepants

 
Python/Django/.NET/C#/Database project? Hire me.
28
May
2008

Dear Javascript, stop degrading my Internets.

I went to the GMail sign-in screen. There's now a Javascript widget that pulls in recent entries from some Google blog (on blogger). This "sucks" because if there's a delay (and there will be), then your previously-remembered password won't be filled after you've entered a user name.

Remembered passwords only fill in (in Firefox 3) after all the "stuff" on a page is done loading.


On a semi-related note, Reddit went down for a while over the long weekend for a makeover. Which meant that a bunch of blogs that were including reddit widgets were hanging trying to render past the widget.

There's a reason things are done in onload instead of in-line.
19
Jul
2007
The Move Dude is up to version 1.7.2. This one finally adds some style to the preferences panel.

There's just not a lot to add to this script at this point. Or at least, I'm not getting any more site requests.
4
Jun
2007

HTML: Readonly Check Boxes and Radio Buttons

To get readonly check boxes or radio buttons in HTML (without using disabled and getting greyed-out nonsense) requires some Javascript:

<script>
function resetCheck(control){
	if (control.type=='checkbox'){
		control.checked=control.defaultChecked;
	}
	
	if (control.type=='radio'){
		// Get all radio buttons in this group.
		var buttons = control.form[control.name];
		for(var i=0;i<buttons.length;i++){
			var button = buttons[i];
			button.checked = button.defaultChecked;
		}
	}

	return false;
}
</script>

...

<input type="radio" onclick="return resetCheck(this);" name="radio1" value="1"> HELLO I'M RADIO<br>
<input type="radio" onclick="return resetCheck(this);" checked name="radio1" value="2"> HELLO I'M RADIO<br>

This works in IE7, which is my own target browser. (It's embedded in a WinForms app, to display a registration form.) As it happens, it also works in Firefox 2. You can still click the buttons, but they reset to their pristine state after the mouse button release. I have no idea what this means for other control events, but since what I'm doing a display-only, I don't care so much.

Note: This code depends on the radio buttons being inside the same form. If your radio buttons are free-floating, you'll have to replace var buttons = control.form[control.name]; with some other selection code to get the radio button group.

Note 2: I'm auto-generating the name/id values form my form elements, and was just using a database ID. Don't do this. Names can't begin with a number. If the name of your radio button is just a number, control.form[control.name] will index on value, not name, and come back undefined.

5
Feb
2007
Peter's Blog: The window.onload problem (still).
16
Aug
2006
The DOM defines a method Node.hasChildNodes(). This returns true if the... well, the name makes it obvious.

I'll note again, this is a method on Node objects, not a property. Implementations in various languages are somewhat free to do whatever they want, but we're talking about JavaScript On A Browser here.

There's code out there that does:
if (myNode.hasChildNodes){
   for(i = 0; i < myNode.childNodes.length; i++) {
      ... do something ..
   }
} 

which is wrong, but usually works, but is redundant anyway.

It's testing for the prescense of a hasChildNodes attribute on myNode, which is always true because the function exists. Then it walks through the child collection, which works even if there are no child nodes because i will start 0 zero and the loop condition will fail right away. A node with no children has an empty childNodes, not a null one.
13
Aug
2006
I'm thinking of borrowing Prototype's build system, if you can even call it a build system. It uses a very small slug of Ruby, along with rake (a build system kind of like make) and ERB (a very simple template system) to "build" prototype.js.

In the source distro, prototype.js has a bunch of ERB includes that pull in separate .js files. The result is a combined .js file for use on the web. Letting the code be separate on the backend makes it easier to track versions and changes of individual components.

Now I have The Movie Dude and The Game Dude using the same base code, they're going to get out of sync immediately if they haven't already. And I might as well break out the generic utility functions I use in all my GM scripts from script-specific stuff. And integrate the "preferences panel library" I wrote for Snippy, keeping it in a separate source file too.

To bad my ruby book is at work (where I haven't been using it for anything.)

Later: The prototype tarball is missing some files, notably a JavaScript unit test runner. The files exist in the repository, though, so it's svn co for me.
12
Aug
2006

Adding #region support to TextMate

Visual Studio lets you create collapsing regions in C# files by using:
   //#region Label
   ... code goes here ...
   //#endregion

(I think regions tend to be overused, at least by the code on CodeProject and similar sites. I cringe when I open a source file and everything at the top level is regioned, and there are nexted levels of them. But I digress.)

I wanted to be able to use regions in my JavaScript code, so I added these clauses to the JavaScript language definition:
   (^\s*//\s*#region)
   (^\s*//\s*#endregion)

The first one gets |'d into the foldingStartMarker regex, and the second into foldingStopMarker. If you want to get fancy, create a symbol definition for #region so it shows up in the function drop-down.

In related news, The Movie Dude 1.6 will be posted soon with The Game Dude to follow shortly.
23
Apr
2006

Adding "section" support to TextMate bundles

So I'm editing a bunch of JavaScript in TextMate these days, and I wanted to get some section markers in the symbol pop-up. It's easy! Add this pattern to whatever language you're using (that supports double slash comments):

{	name = 'comment.section';
	match = '\s*//\s*--+\s*(.*)';
	captures = { 1 = { name = 'meta.section.name'; }; };
}

And then add a new preference to the same bundle with the scope comment.section:

{	showInSymbolList = 1;
	symbolTransformation = '
s/\s*\/\/\s*--+\s*(.*)/• $1/;
';
}

A section is defined by using a double-slash comment with two or more dashes:

// -- Section name

The section name will appear in the symbol pop-up preceeded by a bullet. And you'll be able to navigate to sections with shift-command-T as well.

In the fonts & colors preference panel you can add an element scoped to meta.section.name to color the section name different from other comments.

At some point I'll add some rules for C#-style collapsable region support.

29
Jan
2006

Simple string templates in JavaScript

String.prototype.template = function(vars){
	return this.replace( 
		/\{(\w*)\}/g,
		function(match,submatch,index){return vars[submatch];}
	) };
	
var s = "{a}";
alert(s.template({a: "Hello."}));

This code adds a method template to the String class that takes a object with replacement strings.

Variables to be replaced in the template are surrounded with {curly} braces. The text inside the braces is used as a key into the pass vars, and replaced with the value.

This function works in Firefox and IE (going back several versions), but only in newer versions of Safari that added support for regex replacement functions.

23
Jan
2006

Informal dictionary iteration in JavaScript.

When I'm writing JavaScript, why do I think this code is even remotely acceptable?

function foreach_dict(obj, f, ignore__dict){
	var whichDict = (!ignore__dict && obj.__dict!=null)?obj.__dict:obj;
	for(var name in whichDict) if (f(name, whichDict[name])) return;
}

Explanation:

JavaScript doesn't define any formal interfaces (aka protocols). It does have some informal protocols. Most objects act like a dictionary when used with "for..in" or index notation "[..]". Objects can act like an array by having an integer count property and having indexed properties in the the range 0..count-1.

The foreach_dict function defined above iterates obj as a dictionary and passes each (name,value) pair in turn to f.

f can return true (or anything that evaluates to true) to stop iteration at any point. (A function that doesn't return anything actually returns undefined, which is more-or-less treated equivalent to null, which is considered false.)

Sometimes instead of iterating obj directly I want obj to delegate it's "dictionaryness" to a property. foreach_dict tests obj for a property named "__dict". If found, that property is iterated instead.

10
Nov
2005

JavaScript v.next

Brendan's Roadmap Updates: JS2 Design Notes. That's JavaScript 2, as envisioned for future Firefox releases.

Any changes above what JavaScript in the wild is today are useful mostly for people using Firefox as a platform (ie, extension writers or future XULRunner users), but not particularly useful for web develoeprs.
7
Jul
2005
Inspired by Bookmarklet Based Debugging, I added a logging module to flangelib.

When you log items they get added to an array. The log array can have a limit, or be allowed to grow unbounded (the default). You can add listeners to the log that will be notified whenever there are updates.

There are two built-in ways to view the log. The first shows the N most recent items via alert(). The opens a new window and uses a listener to show log items as they are added. These methods can be added as bookmarlets.

I instrumented my XmlHttpRequest wrapper for logging. It will only try to log if it detects that logging is available. The start and completion of requests are logged, with the status code on completion.

(None of this code is packaged yet, but you can see it in action here.)

Update: The logging window now sorts newest-first, so that scrolling is sane (as items are added, old ones scroll off the bottom instead of new ones.) Also added a toolbar footer with a "Clear" button.

Update: Listeners are wrapped in try/catch. Closing the logging window removes its listener.

Update: Added timestamps... for some reason. If you hover over an item in the logging window, the timestamp appears in a tooltip (in some browsers.)

Update: The fixed footer on the logging window now kinda-sorta almost works in some browsers. Sometimes! Works "best" in FireFox, but seems to work in Safari and *shudder* IE6. Nice to know we'll be stuck with broken IE6 CSS positioning for years to come, IE7 or no.
6
Jul
2005

Syntactic Baking Powder

A JavaScript function for (pointlessly?) changing the syntax used to make classes:
function Class(constructor, methods){
	constructor.prototype = methods;
	return constructor;
}
Example:
var FormData = Class(
function(form_id){
	this.form = $(form_id);
	this.clear();
},{
any_changes: function(){
	if (!this.has_data) return false;
	var fd=this;
	
	var changes = false;
	foreach(this.form.getElementsByTagName("input"),
		function(input){
			if (input.type == "text" 
				&& fd.original_values[input.id]!=input.value){
				changes = true;
				return true;
			}
		});
		
	return changes;
},

save: function(){
	this.original_values = new Object();
	var fd=this;

	foreach(this.form.getElementsByTagName("input"),
		function(input){
			if (input.type == "text")
				fd.original_values[input.id]=input.value;
		});
		
	this.has_data = true;
},

clear: function(){
	this.original_values = new Object();
	this.has_data = false;
}
});
Woo, TextWrangler 2.1 is out, and has an option to open in new window by default. This option is getting checked immediately. Now if I could figure how to AppleScript a command that would upload the current file via Transmit's DockSend feature...

I'm poking around with some JavaScript on this thing. It's a mock-up, not a real thing, and I'm still not convinced the real thing would actually be useful, but we'll see.

When you click new entities, it fetches them via XmlHttpRequest. The intial entity list is retreived that way too. Entities and the list are just bare .js files on the server that are downloaded and eval()'d.
5
Jul
2005

"using" in JavaScript

You can use objects as namespaces in JavaScript, via object-literal syntax:

var Library = {
   a: function(){alert("a");},
   b: function(){alert("b");},
   c: function(){alert("c");}
};

Library.a();

Doing this is a good idea if you want to group related functions.

It's possible to simulate import or using by looping through a namespace and adding functions to the global scope:

function using(namespace_, new_scope){
   // By default, pull functions into global scope.
   new_scope = new_scope || window;

   var property
   for(name in namespace_){
      property = namespace_[name];
      if(typeof property == "function")
         new_scope[name] = property;
   }
}

using(Library);
a();

Namespace has a trailing underscore, as it is technically a reserved word.

It is left as an excercise for the reader to discover if this function has any pratical use.

JavaScript: Arguments

The Arguments type in JavaScript isn't an Array, but you can still do Array-like stuff with it by binding methods from Array:

function line(s){document.write(s,"<br>");}

function test(){
	var _args = Array.prototype.slice.apply(arguments, [2]);
	return _args;
}

foreach(test(0,1,2,3,4), line);

Assuming you have a suitable foreach function defined, this generates:

2
3
4

Flangelib.js

What do you do after you've surfed out all the help wanted ads? Work on ways to make yourself more employable. (Or something like that.)

I'm reworking my own personal JavaScript library, "FlangeLib". If you surf around on adamv.com with the Web Developer extension for FireFox you can find flangelib.js, though there isn't any official distribution yet.

Flangelib is a set of JavaScript code that has agglomerated since I started hacking AJAX into The Conversatron. Since it was born of hacking, there's no design principles. And since I'm using it more and more in JavaScript projects, now would be a good time to clean it up.

One of the up-front design decisions to make is, "Extend prototypes or not?"

JavaScript is object-oriented, but it's prototype-based instead of class-based. (Though it's easy to create C++/Java/C#-style classes with prototypes.) You can add functions to an object's prototype to give all objects created from that prototype new methods.

You can write Array.prototype.do_something = function(){...} and then call it like:
anArray.do_something()
instead of:
do_something(anArray)

The downside? Adding to an object's prototype pollutes the for..in construct. All of the built-in properties of objects are hidden from for..in, but any you add are visible. This makes for..in semi-worthless for iterating only the "data" properties of an object.

The other problem is that some of the object types in Internet Explorer aren't dervied from Object (or Array), so extending base prototypes won't affect objects returned from the DOM. This means that for some functions you're forced into making them stand-alone anyway. (And in most browsers, a function's arguments list is of type Arguments instead of Array, which is a pointless misfeature.)

Thus, I've talked myself into not extending objects via prototype.

I may add properties to constructor functions, to create the equivalent of static class methods, though.
1
Jul
2005

Bob Ippolito is writing his own JavaScript framework. Everyone writes their own JavaScript framework, because it's "so easy to do".

JavaScript, as a language, gives you just about nothing useful. All you have are objects (little more than key-value bags, or null), extremely lame exception handling, and a few primitives (number, string, undefined, function). That means no classes, macros, continuations, coroutines, metaclasses, etc. Forget even thinking about overloading operators or inventing new control structures.

His complaints about Prototype are spot on. This one: "Crufty code evolved from JavaScript frameworks past that attempts to work around bugs that wouldn't even consider Prototype valid code in the first place!" is so very true. If it doesn't detect them, Prototype defines Array.prototype.push and Function.prototype.apply. All browers that support the DOM twiddling you want to do already support these two methods. Defining "apply" seems to be mostly a matter of showing off.

Maybe the Prototype maintainers just don't realize there's cruft they can remove? Or have some false notion of backward compatibility.

30
Jun
2005
Safari doesn't support replacement functions with string.replace? Lame!
29
Jun
2005

document.cookie

In a browser, the "document.cookie" property is the way to get and set cookie values for the current page (or domain path.) It's also a text-book example of how not to design a property.

Reading this property gives you a string like name1=value1;name2=value2;name3=value3 of all the key/name pairs for the current page. You have to split on ; and =, or do c.indexOf(name+"=") to get at individual values; there's no list or map interface.

Writing to this property requires sending in a different formatted string to set a single name/value pair, with optional settings: name4=value4; path=/EntityMan/; domain=adamv.com.

There's no apparent way to retrieve the path/domain/expires/secure settings for an existing cookie. The somewhat non-obvious way to remove a cookie is to write it with an expiration date in the past.

The net of all this is having to use or roll-your-own cookie access functions for client-side development.

It would be "nice" if next-gen browser had a standard higher-level cookie interface.

(The HTTP/1.1 spec does not define any of the headers involved with getting and sending cookies. The HTTP State Management Mechanism does through three headers that no one really uses (everyone uses Set-Cookie instead.))

Update: PageState cookie library
Back
 
View my:
Reads
Links
Photos
Website
LinkedIn
Last.fm
GM scripts
Twits
Google Code
GitHub
Wiki

Back

The Piehead News
(C) 2003-2008
Adam Vandenberg