Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Friday, August 8, 2008

Calling Flash From JavaScript

First, let me say that there are many different ways in which to embed Flash content on your website, including using nested HTML tags, browser detection, and/or JavaScript. There are also different Flash movie formats, but most people choose the SWF format, so we will discuss how to show SWF files. I do not regularly need to work with Flash, as I can do almost everything I want with JavaScript, however, there have been clients who demand its use. And my experience with Flash and SWF files has led me to chose SWFObject as my favorite technique for embedding Flash content on a page.

When embedding Flash content on a page, you will encounter issues in at least these 3 areas:

  • Is Flash supported in the client browser?
    Flash needs to be installed and enabled in the client browser, and you need to handle situations when it is not.

  • How does this browser handle Flash ?
    Flash is a proprietary technology and different browsers have chosen to support it in differing ways, especially detecting Flash versions and rendering alternative content when the flash player is not present.

  • Is the required Flash version support?
    Your code was written for Flash 9, but the client browser only has Flash 8. The user should be asked to upgrade their Flash version.

I am normally adamant about following standards and accessibility practices, which dictates that I not use JavaScript, but an HTML technique, such as found in the List Apart Article: Flash Satay: Embedding Flash While Supporting Standards. This method worked great for me, and until I started working on secure servers (SSL) where this technique causes IE 6 to show a security warning, it was my first choice.

When the Satay method failed me, I then looked to Adobe, the owner of the proprietary Flash technology. Adobe supports several methods for embedding Flash: Adobe Flash Detection Kit. I was hoping that since I was not able to use the more accessible Flash Satay method, that I could at least use officially supported technologies. Unfortunately, I was still receiving the same security warning on secure servers in IE 6 (although, the Adobe site claims that their script works around this).

Then, I asked myself it the IE 6 security warning was really a serious issue. And It is a huge issue, as the site is most likely secure, because it contains sensitive information. Users of a site, where their browser frequently warns them that they not secure, will be less likely to trust it that site. And even though the warning is bogus, your users won’t care.

The only technique successfully defeating the IE 6 security warning bug, is SWFObject, which is why I choose to use it above all others. It is also simple to use, and easy to setup alternative content. Here is a simple example:

Example 1: Using SWFObject

var so = new SWFObject('/assets/swf/mySWF.swf', 'embeddedSwfId', '950', '950', '9.0.115', '#FFFFFF'); so.addParam('quality', 'high'); so.addParam('wmode', 'transparent'); so.addParam('salign', 'tl'); so.write('flash-container'); // DOM node to embed flash content into

This little code snippet will embed the ‘mySWF.swf’ inside the ‘flash-container’ element and assign the embedded SWF the id of ‘embeddedSwfId’. The create DOM node will have width and height of 950 and a background of black. Lastly, we tell the ‘SWFObject’ that we require version ‘9.0.115′ of the Flash player.

In addition to all the issues above, you may encounter server-related issues when requesting the Flash content (usually a SWF file) as a result of compression and/or caching techniques. Fortunately, these issues are well documented and you will most likely find a reasonable solution with a quick google search.

posted by Matt Snider at 8:59 pm  

Tuesday, August 5, 2008

Simple Image Viewer With Captions

It seems that, if I am to leave a legacy after this blog is gone, it will be my Super Simple Image Viewer, as more people read and ask about this widget than any other part of this blog. I attempted to address most people concerns in last weeks Somewhat Simple Image Viewer article, but I was only partially successful, as I did not add in the second most requested feature: captions. So I went back to the drawing board and have written a third version of the Image Viewer.

Version 3 supports both single collection and multiple collections of images (such as portfolios or events), and captions are optional. This way it is nearly backwards compatible with Version 2 that we discussed last week.

Users will quickly notice that the PhotoViewer Function now only requires two parameters: ‘cfg’ and ‘data’. Where ‘cfg’ is an Object containing DOM references and ‘data’ is the collection of image URLs (and other meta data). Below is an example of the ‘cfg’ Object:

Example 1: Configuration Object

var cfg = { caption: 'viewer-caption', // optional image: 'viewer-image', // required next: 'viewer-next', // required prev: 'viewer-prev' // required };

While the caption value is optional, if any of the other 3 DOM references are missing, PhotoViewer will alert you and not initialize. The ‘data’ can be in two different formats, first it can just be a Array of image URLs, second it can be an Object where each key is the DOM ID of the anchor tag to select that set and the value is the Array of images. Here are 3 different valid data sets:

Example 2: Data Object

// 3 sets of objects, containing the image and caption var data = { set1: [// this needs to be the DOM ID attribute for the anchor tag to select this set {image: 'http://farm1.static.flickr.com/142/331222000_76f271e745.jpg?v=0', caption: 'frozen landscape'}, {image: 'http://farm1.static.flickr.com/159/384269775_f8b21b5e52.jpg?v=0', caption: 'mountain stream'}, {image: 'http://farm1.static.flickr.com/178/415791942_4a4e411464.jpg?v=0', caption: 'mountain lake'} ], set2: [// this needs to be the DOM ID attribute for the anchor tag to select this set {image: 'http://farm1.static.flickr.com/186/421171930_80ef8ee7b2.jpg?v=0', caption: 'cool canpopy'}, {image: 'http://farm1.static.flickr.com/155/421172107_2f20ec7aeb.jpg?v=0', caption: 'cool tree'}, {image: 'http://farm1.static.flickr.com/54/133256387_e99cd967ff.jpg?v=0', caption: 'vibrant rose'} ], set3: [// this needs to be the DOM ID attribute for the anchor tag to select this set {image: 'http://farm1.static.flickr.com/46/167939744_2357ffe1af.jpg?v=0', caption: 'white mountains'}, {image: 'http://farm1.static.flickr.com/48/177853809_ad9454a89e.jpg?v=0', caption: 'small turtle'}, {image: 'http://farm1.static.flickr.com/54/183725415_ff609f45e4.jpg?v=0', caption: 'mountain sunset'} ] }; // 3 sets of image URLs var data2 = { set4: // 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'], set5: // 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'], set6: // this needs to be the DOM ID attribute for the anchor tag to select this set ['http://farm1.static.flickr.com/46/167939744_2357ffe1af.jpg?v=0', 'http://farm1.static.flickr.com/48/177853809_ad9454a89e.jpg?v=0', 'http://farm1.static.flickr.com/54/183725415_ff609f45e4.jpg?v=0'] }; // an array of image URLs var data3 = [ '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', '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', 'http://farm1.static.flickr.com/46/167939744_2357ffe1af.jpg?v=0', 'http://farm1.static.flickr.com/48/177853809_ad9454a89e.jpg?v=0', 'http://farm1.static.flickr.com/54/183725415_ff609f45e4.jpg?v=0' ];

Data set 1 shows how to add captions into your PhotoViewer, while set 2 is exactly the same as we used last week, and set 3 is supported to for those of you who don’t need multiple image collections. You can see an example of all 3, Image Viewer Example and the JavaScript code here. Also, to support this more powerful version, I needed extra library code, and you will need to include the following 3 files before importing the PhotoViewer script.

Example 3: JavaScript Files to Include

// core dom and event framework <script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/yahoo-dom-event/yahoo-dom-event.js"></script> // required for type detection <script type='text/javascript' src="http://www.mattsnider.com/assets/js/core/type.js"></script> // required for object manipulation <script type="text/javascript" src="http://www.mattsnider.com/assets/js/object.js"></script>

Lastly, version 3 has a brand new feature, that improves the initial performance of the PhotoViewer widget. I have noticed that in some browsers, like FF2+, even though we are pre-caching the images (using circa 1999 technology), the first time we clicked to see an image it takes an exceptionally long time to load, as if it wasn’t pre-cached (and it probably isn’t). Browsers have become rather smart and to improve performance they don’t always download files, when they don’t believe they are necessary. Therefore, to defeat the browser intelligence, I have created a hidden node at the end of the body tag, in which we will append each image that we want to cache. This tricks the browser into downloading (and pre-caching) the images, thereby drastically improving the performance the first time iterates through the images in PhotoViewer.

posted by Matt Snider at 9:56 am  

Saturday, August 2, 2008

Boolean Functions

There is very little you need to attach to Boolean, as it is a very simple object. I have only found two methods that I like to attach to the ‘Boolean’ Object: ‘is’ and ‘get’. The ‘is’ method is a type detection like I have attached to other native JavaScript objects. The ‘get’ method is to convert falsy/truthy values into ‘false’ and ‘true’. While this isn’t necessary because you can use “! yourObject”, however I frequently like to use strict, type comparisons operations, such as ‘===’ and ‘!==’.

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

As a side note, I have been updating the blog layout a bit. Hopefully, I am making this blog more useful.

posted by Matt Snider at 5:33 pm  

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  
« Previous PageNext Page »

Powered by WordPress