Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Wednesday, July 30, 2008

Somewhat Simple Image Viewer

The last two days I have been catching up on blog comments and related emails. Sorry for any delays. For the most part I have either responded to comments directly in the blog, and written an occasional email response as necessary.

Anyway, one of the most requested features is an improvement to the Super Simple Image Viewer (SSIV) to allow multiple instances on the same page. As the SSIV is designed to be really simple, I do not want to muck around with it. So I have created a second version that is not quite as simple, but works by instantiation instead of a static global object.

Somewhat Simple Image Viewer

To get this code to work, include the following on your site just before the closing body tag:

Example 1: Somewhat Simple Image Viewer

<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/yahoo-dom-event/yahoo-dom-event.js"></script> <!-- This is the Framework I use for building JavaScript applications; designed by YAHOO --> <script type="text/javascript" src="http://mattsnider.com/assets/js/widget/matts_photo_viewer2.js"></script> <!-- This is the actual JavaScript Photo Viewer application --> <script type="text/javascript"><!-- // replace these with the actual names of your files var data = { portfolio1: // this needs to be the DOM ID attribute for the anchor tag to select this set ['http://farm1.static.flickr.com/142/331222000_76f271e745.jpg?v=0', 'http://farm1.static.flickr.com/159/384269775_f8b21b5e52.jpg?v=0', 'http://farm1.static.flickr.com/178/415791942_4a4e411464.jpg?v=0'], portfolio2: // this needs to be the DOM ID attribute for the anchor tag to select this set ['http://farm1.static.flickr.com/186/421171930_80ef8ee7b2.jpg?v=0', 'http://farm1.static.flickr.com/155/421172107_2f20ec7aeb.jpg?v=0', 'http://farm1.static.flickr.com/54/133256387_e99cd967ff.jpg?v=0'] }; var data2 = { portfolio3: // this needs to be the DOM ID attribute for the anchor tag to select this set ['http://farm1.static.flickr.com/142/331222000_76f271e745.jpg?v=0', 'http://farm1.static.flickr.com/159/384269775_f8b21b5e52.jpg?v=0', 'http://farm1.static.flickr.com/178/415791942_4a4e411464.jpg?v=0'], portfolio4: // this needs to be the DOM ID attribute for the anchor tag to select this set ['http://farm1.static.flickr.com/186/421171930_80ef8ee7b2.jpg?v=0', 'http://farm1.static.flickr.com/155/421172107_2f20ec7aeb.jpg?v=0', 'http://farm1.static.flickr.com/54/133256387_e99cd967ff.jpg?v=0'] }; var photoViewer1 = Core.Widget.PhotoViewer(data, 'image', 'showNext', 'showPrev'); var photoViewer2 = Core.Widget.PhotoViewer(data2, 'image2', 'showNext2', 'showPrev2'); // additional photo viewers go here, just pass the HTML ID for the main image, and the next/previous anchors --></script>

Instead of calling the ‘addPhoto’ method, as we did with the original Image Viewer, that created and managed the Image Viewer data, we now create the data manually. In this case, the data Object needs a key for each collection of images needed for the Image Viewer; if there is only 1 collection of images, then ‘data’ can just be an Array, instead of an Object. If you have multiple collections of images, say different portfolios (as in Example 1), then the key for each collection of images needs to be the same as the ID attribute of the anchor tag to be used for choosing that collection in the DOM. This anchor will have an event attached to it that updates the Image Viewer to the correct collection, and an error with be thrown if you do not have a matching anchor tag in your page.

However, for the most part it works nearly identically as the original ImageViewer. I tried to change as little as possible, so that it would be easy to convert original ImageViewer pages to this new one.

posted by Matt Snider at 6:50 pm  

Saturday, July 26, 2008

Object Functions

While it is always best practice to never extend the ‘prototype’ of Object, it is fine to extend Object directly with your own static methods. There are 4 methods that I find critical to working with JavaScript objects: forEach, getIgnoreKeys, is, and toQueryString. The ‘forEach’ method iterates on the Object using the “for … in” loop, but calls ‘getIgnoreKeys’ to determine which keys are methods attached to the Object prototype, and ignores those values. You can also use the ‘forEach’ method to iterate on an Array, even though it would be more efficient to use the ‘forEach’ method we attached to Array in my last post. Lastly, the ‘is’ Function is simply a type detection method and ‘toQueryString’ calls ‘forEach’ so that we can construct a query string, “key1=value1&key2=value2&key3=value3…”.

Check out object.js here and the unit test here.

posted by Matt Snider at 11:40 am  

Tuesday, July 22, 2008

Extending Array

I have not written code extending Array yet, as I have had concerns about breaking the web while extending Array. The problem is that any changes to the prototype of Array breaks “for … in” loops. After some discussion with other JavaScript architects and further thought, it became apparent that extending Array doesn’t really break the web. First of all, the only issue is that after modifying the “Array.prototype”, using “for … in” loops on an Array returns each attached Function, as well as each member of the Array, so you would have to do what YUI does and ignore theses keys. However, you would only need to use “for … in” loops if you were creating associative arrays, and an associative Array can be represented instead with an Object, so there is no need to use them. Therefore, it is good practice to use Object in place of any associative Arrays. If you follow this practice, then one only needs to iterate numerically on an Array, never needing the “for … in” loop; allowing us to safely extend “Array.prototype” with useful methods.

The most important method is probably the “Array.get” Function attached statically to Array. This method converts Array-like objects such as ‘arguments’ and ‘collection’ to an Array, so that you get all the syntactic sugar of “array.js” on non-arrays. This is most useful in conjunction with “document.getElementsByTagName” or simply ‘arguments’.

I have also added the concept of a pointer to Array so that you can iterate through the Array with functions like: ‘next’, ‘prev’, and ‘current’, which point to the appropriate index in the Array. Some other useful short-hand methods like: ‘first’ and ‘last’ have also been added.

Beyond those, there is a slew of other useful functions, that allow for easy Array management. It is all well documented, check “array.js” out here. And I have written an array unit test here.

posted by Matt Snider at 8:49 pm  

Sunday, July 20, 2008

Extending String, Revisited

In the past, I wrote an article extending the native JavaScript String Object with useful methods and also about a RegExp escape method:

String Functions
RegExp Escape

I have revisited those methods and also added several other methods to String.prototype and static methods to String that I find useful. Check out the new String.js here and the unit test here if you want to double check that it all works well on your favorite browser.

The first noteworthy change is that I have migrated the “RegExp.esc” method to “String.prototype.escapeRx”. The old “RegExp.esc” method always required that a String be passed in, so it made sense to extend String with this functionality, instead of always calling a static method and passing a string. This allowed me to also not have a dependancy on the “string.js” utility file in my “regexp.js” utility file.

The second noteworthy change, is “String.prototype.decode” Function. This was written by Jason Yiin at Mint.com and unescapes HTML and XML entities. I have had numerous issues with server-side view technologies over escaping my HTML and/or XML code, and this Function has allowed me to correct these indiscretions.

Otherwise, I believe the files are similar to the older versions, but dependencies on other utility Functions have been removed (except Core.js) and they fail more gracefully, when bad parameters are provided.

posted by Matt Snider at 1:41 pm  

Tuesday, July 15, 2008

Extending Number, Revisited

4 months ago, I wrote an article extending the native JavaScript Number Object with a format method:

Numbers and Number Format Function

I have revisited that method and also added several other methods to Number.prototype and Number that I find useful. Check out the new Number.js here and the unit test here if you want to double check that it all works well on your favorite browser. Mostly, there is the format method and shortcuts to Math functions.

You may have noticed on Date.js and Number.js that I have been adding an ‘is’ method to the actual native Object for type detection (not the ‘prototype’). This is a compromise that I made with myself, because I do not want a bunch of ‘isType’ methods in the global namespace, but at the same time I find it tedious to always type “isType(object, ‘type’)”. So, instead of putting a bunch of ‘isType’ functions in the global namespace, I am attaching ‘is’ methods to the native Object for a given type. So, with Number, instead of using “isType(object, ‘number’)”, I can just write “Number.is(object)”. This works for all Object types, including ‘object’, because the method is not attached to the ‘prototype’ Object, thereby not breaking for…in loops. And it is an especially useful shortcut, if your type detection is attached to a nested namespace, such as the YAHOO type detection attached to the “YAHOO.lang” Object.

posted by Matt Snider at 11:15 pm  

Sunday, July 13, 2008

Extending Date, Revisited

About 6 months ago, I wrote several articles about extending the native JavaScript Date Object with constants, static methods, and public methods on the prototype:

Date Functions
YUI Datemath on JavaScript Date Object

The first article describes methods that I designed myself, while the second explains how to convert YUI Datemath to extend the Date Object instead of using static methods. I recently revisited all the methods that I append to Date and Date.prototype, creating a JavaScript unit test using the YUI Test Framework. While they all worked before, I have re-written them now to handle errors and bad parameters better than before. As noted in the comments, many of them return default values, or NULL when enough parameters are not provided.

Check out the new Date.js here and the unit test here if you want to double check that it all works well on your favorite browser.

posted by Matt Snider at 8:18 pm  

Wednesday, July 9, 2008

Namespace Function

Today’s idea is inspired by the “YAHOO.namespace” Function of YUI. The concept is to have a method that creates your namespaces for you. In the YUI version, you pass in a String (or multiple strings) containing the new namespace(s) and they are attached to the YAHOO Object. If the namespace already exists, then you are simply returned that namespace.

Example 1: Single YUI Namespace

var myObject = YAHOO.namespace("example.mattsObject");

The example namespace is frequently used by the YUI team in their documentation. I have attached an Object (’mattsObject;) to the the example namespace (”YAHOO.example.mattsObject”). If either the “YAHOO.example” or the “YAHOO.example.mattsObject” namespaces do not exist, they will be created and the latter namespace Object will be returned, and in Example 1, assigned to ‘myObject ‘.

If you wanted to setup all your namespaces in a single call, you might do something like the following:

Example 2: Multiple YUI Namespaces

var myWidget = YAHOO.namespace("myProject.lib", "myProject.util", "myProject.widget");

This works just like example 1, except this time we setup 3 namespaces, and return the last one (”YAHOO.myProject.widget”) to set ‘myWidget’. Each namespace can be accessed directly from or you can continue to use YAHOO.namespace(myNamespace) to ensure that they always exist.

The YAHOO namespace method works great, especially, because it returns a reference to the desired namespace, but what if you do not use YUI or you want to use a different naming convention. Here is a modified version of the YAHOO namespace method that starts builds from ‘window’ instead of the ‘YAHOO’ Object.

Example 3: Modified Namespace Method

window.object_mes_namespace = function() { var a = arguments, o = window, i = 0, j = 0, tok = null, name = null; // iterate on the arguments for (i = 0; i < a.length; i = i + 1) { tok = a[i].split("."); // iterate on the object tokens for (j = 0; j < tok.length; j = j + 1) { name = tok[j]; o[name] = o[name] || {}; o = o[name]; } } return o; }

First we initialize all the variables that we will need, and create local variables to reference the Function arguments (’a') and window (’o'). We iterate through each of the arguments and split them around period, setting that Array to ‘tok’. Then iterating on each of the tokens we see if that token exists yet on ‘o’, setting it to Object when it does not, then we update ‘o’ to point to the new namespace. This keeps happening until ‘o’ is pointing to the last namespace.

Using this Function allows you to safely create your own namespaces in global without overriding any existing objects. If we took the ImagePopout Object from Simple Image Popouts we might want to change it’s implementation and attach the Object to the ‘Widget’ namespace instead of global.

Example 4: Using Namespace With ImagePopup

object_mes_namespace('Core.Widget').ImagePopout= function(bd, src, emap) { ... };

This way we ensure that both ‘Core’ and ‘Core.Widget’ objects exist, before we set ImagePopout to the popout generation Function, without having to ensure namespace existence or accidentally overriding an already existing namespace.

posted by Matt Snider at 12:08 am  

Friday, July 4, 2008

Fourth of July 2008

No article today, as I am on holiday in Pismo Beach, California. However, Smashing Magazine has written two great article recently about web design/implementation patterns: Web Form Design Patterns: Sign-Up Forms and Should Links Open In New Windows?. The first article covers some really good form design considerations, whilst the second explains why you should not force open links in a new window (there are a few exceptions).

As you’ve probably experienced on this site, I used to always use ‘target=”_blank”‘ for external links. However, after reading many, many articles (including the smashing magazine one), I am not longer opening links in a new window, except for help text and definitions.

posted by Matt Snider at 11:34 am  

Wednesday, July 2, 2008

Server-Driven Constants

On the web today, it is commonplace to use JavaScript to communicate with the server (think AJAX or manipulating window.location). This means that there will be parameters that are the same on the server- as in the client-side code. As your code grows there will be more parameters and occasional name changes, resulting in parameter management increasingly wasting your time and/or causing errors. As a result it is considered best practices to manage these parameters on the server as constants where they can be maintained in one place and propagated throughout your code.

I recommend using constants for the following:

  • Query Parameters
  • Class and Id DOM attributes
  • Messaging and Dynamic text
  • Errors
  • XML tags and JSON keys
  • Cookie Keys

My general rule of thumb is to create a constant any place where a String references the same value in the server- and client-side code. I do a pretty good job of this on Mint.com, which allows me to change query parameter names in seconds (without having to do a find/replace), return error codes to the JavaScript for handling (such as a session has timeout out), reduce the number of query parameters to a manageable set, and a whole lot more.

Server-side you should use whatever constant paradigm you are comfortable with. In JAVA I tend to use a static object that I call WebConstants and attach constants to it. In PHP, I tend to use a large, associative array, where the constant is the associative key. Consider the technology you are most familiar with and how best to use constants.

Client-side, I recommend that you map everything to a common namespace. I usually use something like, ‘ProjectName.constants.constantType.constantName’ (capitalize ProjectName, but camel-case the other namespaces). Then I make capital ‘C’ a shortcut to ‘ProjectName.Constants’. This way, if I want to use the query parameter for ‘username’, you would use: ‘C.query.username’. I find that I most frequently use the query parameter constants when hand creating AJAX requests.

At this point, you are probably wondering about how to pass the constants from the server code to the JavaScript. This can be done many different ways, but the best is to have a build process that iterates through the constant, creating a ‘constant.js’ file and include that in your JavaScript library. If you already run a build process when starting your web application, then just add another step, otherwise, you need to write and run a build step when changing a constant.

Here is a simple PHP function that I use in my build step to convert my PHP constants into JavaScript ones:

Example 1: PHP JavaScriptConstantWriter

JavaScriptConstantWriter

I had to link this file, because wordpress keeps choking on the PHP code snippets in here.

This function requires that several parameters are passed in: an associative array of constants, the path to your project’s JavaScript directory, the name of your constant file (defaults to ‘constants.js’), and the namespace to attach the constants to (defaults to Core.Constants’). Since this function is for personal use, I make several assumptions about the naming convention used for constants, which we’ll discuss below.

Example 2: Calling writeJavaScriptConstants

$_C = array( 'HTML_KLASS_PROJECT_NAME' => 'project', 'HTML_ID_PROJECT_NAME' => 'project', 'QUERY_KEY_USERNAME' => 'username', 'QUERY_KEY_EMAIL' => 'email', 'QUERY_KEY_PASSWORD' => 'password', 'SESSION_KEY_USER' => 'user' ); $pathToJs = '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR; $fileName = 'constants.js'; $prefix = 'Core.Constants'; writeJavaScriptConstants($_C, $pathToJs, $fileName, $prefix);

Example 2 will produce the following ‘constants.js’ file:

Example 3: Results of writeJavaScriptConstants

Core.Constants={}; Core.Constants.html={}; Core.Constants.html.klass={}; Core.Constants.html.klass.PROJECT_NAME='project'; Core.Constants.html.id={}; Core.Constants.html.id.PROJECT_NAME='project'; Core.Constants.query={}; Core.Constants.query.key={}; Core.Constants.query.key.USERNAME='username'; Core.Constants.query.key.EMAIL='email'; Core.Constants.query.key.PASSWORD='password'; Core.Constants.session={}; Core.Constants.session.key={}; Core.Constants.session.key.USER='user';

First it is important to note that the method makes no assumptions that a given namespace object exists, and creates a new Object for each new namespace. Therefore, if you attach anything to these namespaces, make sure you do so after you include ‘constants.js’ in your library.

I use a very strict namespace convention for constants, where OBJECT + ‘_’ + TYPE + ‘_’ + NAME. If you use my logic, it is important that both OBJECT and TYPE are single words and do not have extra underscores. I sometimes use abbreviations or shortcuts to achieve this, but I find that this convention keep my constant names shorter. The NAME portion of the constant can have as many underscores as desired; I usually separate words with underscores.

So, if we consider the PHP cosntant ‘HTML_KLASS_PROJECT_NAME’, then the object is ‘HTML’, the type is ‘KLASS’, and the name is ‘PROJECT_NAME’. This will attach the namespace ‘html’ to ‘Core.Constants’, then attach the namespace ‘klass’ to ‘html’, and then attach the actual constant name to ‘klass’. Once I setup my shortcut ‘C’ to reference ‘Core.Constants’, then I can reference this constant in JavaScript using C.html.klass.PROJECT_NAME. Therefore, if we ever decide to change the project name to say, ‘Mint’, then we change the constant value in PHP (’$_C’) and run ‘writeJavaScriptConstants’, and every reference in ones JavaScript is also updated. On a side note, I have used ‘klass’ instead of ‘class’, because ‘class’ is a reserved word in JavaScript.

posted by Matt Snider at 5:43 pm  

Powered by WordPress