Tuesday, November 18, 2008

Flash on AMD64 Linux

Adobe answered the cries of Linux users by releasing a 64-bit Linux Flash 10 Plugin. Note that as of this posting the plugin is an Alpha plugin, but I've installed it and have it working on both Firefox-3 and Opera-9.62 for Ubuntu 8.10 Linux.

After closing Firfox and Opera, then using Synaptic to remove flashplugin-nonfree from my system and Cleaning up My Ubuntu Box, I followed the installation instructions given by Adobe.

Firefox wanted to update itself as soon as I removed flashplugin-nonfree, but I held off on doing that until I had the new plugin installed. After extracting libflashplayer.so to ~/.mozilla/plugins I allowed Firefox to update via Ubuntus automatic updates, then started Firefox up and the Flash Player worked like a charm.

Opera didn't want to work right at first however. For some reason Opera wasn't aware of my ~/.mozilla/plugins directory as being a plugins directory so I had to add that directory to my plugins path in Opera.

Adding ~/.mozilla/plugins to my plugins path was simple enough.
Tools > Preferences > Advanced > Content > Plugins Options > Change Path


After adding ~/.mozilla/plugins to the list of folders and restarting Opera the Flash Player was working properly.

Sunday, November 16, 2008

PHP Robot Check

I've seen quite a few methods for determining if a request was made by a robot or search engine spider using PHP over the years, and they always have something to do with checking the User Agent. That works, but you have to keep up with User Agent strings which can get to be annoying.

I had an idea how to check for robots using PHP and htaccess today that doesn't involve a User Agent string at all. Instead it uses sessions and exploits the fact that all well behaved robots are going to request robots.txt before they request anything else.

I start by making sure my usual robots.txt file is in place. Then I upload a robots.php file to the same location as robots.txt with a tiny bit of code in it.

<?php
session_start();
$_SESSION['robot'] = 1;
echo file_get_contents('robots.txt');
exit;
?>


All that does is start a session, set a robot $_SESSION variable that I can check in subsequent scripts, then return the contents of robots.txt.

In htaccess I have the following line which transparently redirects requests to robots.txt made by visitors/spiders to robots.php, which in turn returns the contents of robots.txt

RewriteEngine on
RewriteRule robots\.txt robots.php


Now in my applications I can easily check for robots and drop things like advertisement banners to speed up page loads since spiders don't look at advertisements anyway.

<?php
session_start();
echo isset($_SESSION['robot']) ? 'ROBOT !!' : 'Not a robot';
?>


Here are some of the benefits of doing it this way.

  1. I can continue to modify robots.txt as I normally would
  2. I don't need to keep up with changing User Agent strings
  3. Checking for the existance of a session variable is quicker than performing pattern matching on a variable

Saturday, October 11, 2008

Disable Firefox Image Drag

Firefox 3 has a behavior where clicking an image and moving your mouse will start to drag a preview of that image around. The behavior even occurs with the background images for elements.

While this drag and drop behavior for images in Firefox can be really usefull for the visitor, this behavior can be a real problem if you're writing an application that depends on JS to drag around elements that contain images or have background images.

Placing the image or element inside a container and attaching event listeners to that container instead of the image or element with a background seems like an obvious answer, but it will only work for the first click. If the visitor drops the element and tries to drag it again Firefox will start up with the drag and drop behavior.

What does work though is having your mousedown event listener prevent the default action, which seems to be that drag and drop behavior.

Within the event handler you're assigning to your mousedown event, assuming you're argument list includes an event object, as is the case with most jQuery event handlers, your function declaration might look like this.

var callback = function(event){
if(event.preventDefault)
{
event.preventDefault();
}
// Other code...
}


Nice and easy.

Friday, October 10, 2008

PHP: JPEG/JPG DPI function

While working with a lot of JPEG images lately I needed to be able to set the DPI with PHP and I was having trouble finding a way to do that with the native PHP or GD functions. The stuff I did find seemed to be overly complicated or just plain wrong.

So I wrote my own DPI function for PHP that rather than doing pointless resampling simply adjusts the file header.
My needs didn't require me to do it "right", but it will handle from 1 to 255 DPI.

/*
@$jpg -- Path to a jpg file
@$dpi -- DPI to use (1-255)
*/
function set_dpi($jpg, $dpi = 163)
{
$fr = fopen($jpg, 'rb');
$fw = fopen("$jpg.temp", 'wb');

stream_set_write_buffer($fw, 0);

fwrite($fw, fread($fr, 13) . chr(1) . chr(0) . chr($dpi) . chr(0) . chr($dpi));

fseek($fr, 18);
stream_copy_to_stream($fr, $fw);

fclose($fr);
fclose($fw);

unlink($jpg);
rename("$jpg.temp", $jpg);
}

Friday, September 26, 2008

Broken Pipe: ef_unified_filter() failed

I was getting this error message in my Apache error logs while trying to setup an Apache based output filter using ExtFilterDefine and AddOutputFilter.

The error that accompanied it was
Broken pipe: apr_file_write(child input), len 0.

Not sure what either of those meant, but guessing the ef_unified_filter() failed was just a notice that the filter was broken, I tried a bunch of things and only found maybe 50 results while searching around, none of which helped.

It turns out that the problem was in the way I was defining the cmd="" argument for ExtFilterDefine. I, and the people who were having the same problem but with different commands, weren't using the full path to our command applications.

My original ExtFilterDefine directive looked like this.

ExtFilterDefine AutoWrap \
cmd="cat -"


However I needed to specify the full path to cat, which in my case looked like this.

ExtFilterDefine AutoWrap \
cmd="/bin/cat -"

Tuesday, September 9, 2008

gtk-stock and gdk_cairo_set_source_pixbuf

It took me the better part of a day to work out how to use a gtk-stock icon as the source for a cairo_paint operation. Basically what I was doing was drawing a magnified section of the screen and painting an eyedropper over it in the middle for the Picksel colorpicker project. I was using a PNG loaded at runtime in the beginning but since GTK has a stock eyedropper icon I figured I might as well use that.

I was having segfault and other issues when I tried gtk_image_create_from_stock and then gtk_image_get_pixmap on the resulting GtkWidget. Apparently the storage method is done in such a way that you can't obtain a GdkPixmap or GdkPixbuf.

What did work though, and actually turned out to be easier was gtk_widget_render_icon. The second and third arguments are the same as the ones used for gtk_image_create_from_stock, the last argument I just left blank, and for the first GtkWidget argument I just passed the reference to my main application window for the stock icon settings to be taken from.

The return from gtk_widget_render_icon is a GdkPixbuf which I used with gdk_cairo_set_source_pixbuf before finally drawing the icon to my destination with cairo_paint.

Here's a brief example.
GtkWidget *window;
GdkPixmap *buffer;
GdkPixbuf *eyedropper;
cairo_t *ctx;


ctx = gdk_cairo_create(buffer);
eyedropper = gtk_widget_render_icon (window, GTK_STOCK_COLOR_PICKER, GTK_ICON_SIZE_MENU, "");
gdk_cairo_set_source_pixbuf (ctx, eyedropper, x, y);
cairo_paint(ctx);

Friday, September 5, 2008

Web File Formats 101

These are file extensions or formats you're likely to come across at one point or another if you're going to get into designing web sites. They're split into common groups, include a brief description of where you're likely to see or use them, and clicking the *.EXTENSION link in the titles will take you to a Wikipedia entry with a lot more details on that file format.

This list is a collection of about 45 file formats I've used enough times to remember when presented with a huge list of file formats after roughly five years of experience.




Browser File Formats

Browser file formats are files that instruct web browsers to do something directly. Many technologies may work with browser file formats, but browsers are generally the things that make use of them.

*.AS Adobe/Macromedia Action script file

AS files are generally used as library or optional include files in Flash or Flex applications.

*.CSS Cascading Style Sheet file

CSS files contain CSS rules which web browsers use to decide what web pages should look like.

*.HTM / *.HTML Hyper Text Markup Language

HTML files are web pages. HTML is what files such as PHP, ASP, CFML, and JSP produce when they're executed.

*.ICO Icon file

ICO files are commonly used as Favicons (that little icon in the address bar)

*.JS Javascript file

JS files are used to store Javascript. Javascript is a cornerstone in many of the applications on the web today. Javascript provides a way for web developers to utilize the browsers rendering engine and make changes to the content of a web page without needing to make a trip back to the web server to generate a new page with the updated content. Javascript is becoming more popular for producing animation in web pages since the jQuery, Prototype, and Moo-Tools Javascript libraries were developed.

*.SWF Adobe/Macromedia Shockwave Flash file

SWF files are generally used in Rich Internet Applications and require the Adobe/Macromedia Flash Player to view.

*.VBS Microsoft Visual Basic Script file

VBS files are similar in concept to JS files but they often have control over operation system functionality normally off limits to JS, however VBS files are only supported by Microsoft Internet Explorer versus JS which is available on almost every popular web browser. VBS is generally only found on private Microsoft Windows based Intranet sites, though even there it's being replaced by newer offerings from Microsoft.

Image File Formats

Image file formats are files used to store and transfer photographs, vector art, logos, favicons and things like that. They're also used to store information artists use to modify the images in image manipulation applications such as Photoshop and Fireworks.

*.BMP Bitmap Graphic file

BMP files are an early image format with little or no compression applied. BMP files are sometimes found on websites, but have generally been replaced by formats such as PNG, GIF, and JPG in recent years.

*.GIF Graphic Interchange Format file

GIF files are image files that sometimes include simple frame-based animation and alpha transparency. PNG has gained popularity over GIF in recent years do the the superior color quality of PNG, however transparency problems with Microsofts Internet Explorer 6 have slowed the transition from GIF to PNG.

*.JPG / *.JPEG Joint Photographic Group image file

JPG files are image files designed specifically for photos. The compression method used has been optimized for working with complicated images, making it especially well suited for transferring photos on the web.

*.PNG Portable Network Graphic file

PNG is an image file format that has gained popularity over GIF in recent years do the the superior color quality of PNG, however transparency problems with Microsofts Internet Explorer 6 have slowed the transition from GIF to PNG. PNG is also the native file format used by Adobe/Macromedia Fireworks, similar to how PSD is the native file format used by Adobe Photoshop. PNG has the ability to store information about layers when used in Fireworks in much the same way that PSD can store information about layers in Photoshop.

*.PSD Photoshop Document file

PSD is an image file format and is the native format used by Adobe Photoshop. PSD allows image authors to store information about layers in an image when edited in Photoshop in much the same way that PNG enables layer information when edited in Fireworks. Unlike PNG which generally has multiple export modes and can be exported for use on the web, PSD is primarily used for raw or drafts before exporting the finished pieces of the image to other image formats such as PNG, GIF, or JPG.

*.SVG Scalable Vector Graphics file

SVG files are vector image files. Unlike common raster image formats that store information about each pixel in the image, vector image formats only store information about how to draw the image, which generally consists of coordinates to place simple shapes and how to fill them with color.

Audio/Video File Formats

Audio/Video file formats are used to store and transfer audio and video.

*.AVI Audio Video Interleave file

AVI files are commonly used to distribute video.

*.FLV Flash Video file

FLV files are usually not observed directly, but rather played through an SWF file. FLV files are used for distributing streaming video.

*.MOV / *.QT Apple QuickTime Video Clip file

MOV or QT files are used to distribute video, similar to AVI files. Quicktime is generally known for a great level of quality in video.

*.MP3 MPEG Audio Stream, Layer III file

MP3 files are used to distribute audio. MP3 files use a compressed version of the audio information and are generally many times smaller in size compared to WAV files, despite including extra information about the audio such as the author, title, album name, and description.

*.MPEG / *.MPG MPEG 1 System Stream file

MPEG or MPG files are used to distribute video, similar to AVI, MOV, and QT files.

*.WAV Waveform Audio file

WAV files are an early audio format file which has all but been replaced by MP3 on the web due to superior compression and information embedding provided by the MP3 audio file format.

*.WMA Microsoft Windows Media Audio file

WMA is an audio file similar to MP3 and WAV developed by Microsoft. Like WAV, WMA has been replaced to a large extend on the web by MP3.

Compression File Formats

Compression file formats are used to apply compression to single, or groups of files in order to reduce the size needed to store and transfer the files, as well as making large groups of files easier to transfer at once.

*.BZ2 BZIP2 compressed archive file

BZ2 files are generally used by programmers to distribute compressed source code packages.

*.GZ GZIP compressed archive

GZ files are sometimes used in the same way BZ2 files are, for programmers to distribute source code packages. Some systems use GZIP compression on their old log files to save space which results in file names such as "date.log.gz". Some web servers use GZIP compression to compress data before sending it from a server to a browser.

*.RAR winRAR compressed archive

RAR files are often used by individuals to compress and transfer large files, or large groups of files. Similar to GZ, BZ2, and ZIP files.

*.ZIP Compressed archive file

ZIP files are similar to RAR, BZ2, and GZ files however ZIP files are generally a lot more common. ZIP files package and to a point compress other files to be transferred from system to system.

Misc File Formats

Misc file formats are generally not designed specifically for web sites, but have found numerous uses on them.

*.*.BAK Backup copy of file

Backup. The second asterisk is generally the original file extension.Rather than trying to open this file, you will usually rename it and remove the .BAK from the end, then open it with the appropriate application

*.DOC Microsoft Word document

DOC files are often used by companies to distribute bundled information suitable for printing. DOC files are a favorite of companies for sending contracts via e-mail. DOC files are similar in concept to PDF files.

*.INI Information/configuration file

INI files are commonly used to store persistent configuration data for web servers and web browsers. INI files are also used in the same way for applications that have nothing to do with web design. Using XML files to store configuration data instead of INI files has been gaining popularity in recent years.

*.KML Google Earth/Keyhole place mark file

KML files are used to share specific locations, or placeholders in the popular Google Earth application. KML files are becoming increasingly popular on business web sites who want to provide directions for customers.

*.PDF Acrobat Portable Document Format file

PDF files are often used by companies to distribute bundled information suitable for printing, similar to DOC files. Some print shops will ask for PDF files when you need to have fliers or brochures printed out. Some companies will make PDF copies of important information found on their web site available for people do download and read offline, or print out and read away from their desk.

*.RDF Resource Description Framework file

RDF files are often used to deliver RSS content. RDF RSS feeds are generally more specialized in comparison to the generic XML versions of RSS files.

*.TXT Plain text file

TXT files are plain text files. TXT files generally display the characters as-is with no consideration for special style codes.

*.XML Extensible Markup Language file

XML files are generally used to store structured information. Many web sites use AJAX to load XML in the background and update small portions of web pages without needing the server to generate the entire web page over again with slightly changed information. XML files are also gaining popularity as a settings or preference file format. The generic yet descriptive format used to section data off makes XML the perfect file format for storing raw information that will be displayed in many different ways.

Server File Formats

Server file formats are generally scripts that web servers execute in order to produce web pages dynamically. these types of files are also often used to perform maintenance on the server through automation.

*.ASP Microsoft Active Server Pages

ASP files are an earlier version of ASPX files. ASP files are similar in concept to CFML, PHP, ASPX, RB, and JSP files.

*.ASPX Microsoft ASP.NET source code file

ASPX files are a more recent version of ASP files. ASPX files are used on web servers to prepare HTML documents to send to a web browser. ASPX files are similar in concept to ASP, CFML, PHP, RB, and JSP files.

*.CFM / *.CFML Coldfusion Markup Language file

CFML files are used on web servers to prepare HTML documents to send to a web browser. CFML files are similar in concept to ASPX, PHP, ASP, RB, and JSP files.

*.CGI Common Gateway Interface file

CGI files are generally used to provide a bridge, or gateway, between a web server and applications installed on the server such as Python, Perl, sed, and many others.

*.JSP Java Server Pages

JSP files are used on web servers to prepare HTML documents to send to a web browser. JSP files are similar in concept to ASPX, PHP, ASP, RB, and CFML files.

*.LOG Log file

LOG files are generally exactly what the name implies, a log. Web servers use them to store details about each visit to a web site as well as to store information useful to administrators and developers in the event of a problem. Old, or archived LOG files are often compressed using GZIP compression, which results in "filename.log.gz" types of file names.

*.MDB Microsoft Access Database file

MDB files are a Microsoft database data storage format. The MySQL database engine has been gaining popularity over MDB in recent years. There are many MDB to MySQL converters and conversion methods available on-line.

I posted a how to for Importing MDB to MySQL on a Debian server not too long ago.

*.PHP Hypertext PreProcessor script file

PHP files are used on web servers to prepare HTML documents to send to a web browser. PHP files are similar in concept to ASPX, JSP, RB, ASP, and CFML files. PHP is one of the most popular pre-processors used on the web today.

*.PL Perl script file

PL files are Perl script files. Perl is one of the first scripting languages to be used on the web. Perl is often used in conjunction with CGI. In recent years PHP has stolen much of Perls thunder, leaving Perl used primarily by veterans.

*.RB Ruby script / Ruby on Rails class file

RB files are used on web servers to prepare HTML documents to send to a web browser. RB files are similar in concept to ASPX, JSP, PHP, and CFML files.

*.SHTML / *.SSI HTML including Server Side Includes

SHTML files are used on web servers to tell the web server that there's lines in the file that need to be replaced with the contents of other files before sending the contents of the file to the web browser. SHTML is generally a very simple version of what file types such as ASP, ASPX, CFML, JSP, PHP, and RB are designed for.

*.SQL Structured Query Language data file

SQL files generally contain the description of a database and the database table layouts. In some cases SQL files will contain information dumps from database tables, though this is generally reserved for small databases.

.htaccess Apache dynamic configuration file

.htaccess files are commonly used to make runtime adjustments to an Apache web server configuration without needing to restart the Apache server. Not all configuration options are able to be altered via .htaccess and depending on the servers configuration at start up there might not be any options that can be configured via .htaccess

Monday, August 11, 2008

Javascript Lorem Ipsum Generator

About 2 years ago I wanted a Lorem Ipsum Javascript to make adding Lorem Ipsum to webpages in progress a little less work & bandwidth consuming.
It's worked well for quite awhile now, but today seemed like a good day to revamp the script.

This new version of the script works the same way as the last version.
HTML comments are placed where Lorem Ipsum should go & the script replaces those comment nodes with text nodes at the DOM level.
Version 2 is also about 3 times faster, the comment scheme is easier to use, & there's some convience methods added.

Starting with the easier comment scheme.
Comments are placed like so depending on whether words, sentences, or paragraphs should replace the comment.
It starts with the number of units to use, followed by what type of units to use. The script is pretty forgiving about the usage of whitespace when defining these. The script also doesn't care if the unit name is pluralized or not.
<-- 2 words -->
<-- 4 sentences -->
<-- 1 paragraph -->


Ranges can also be used for the amount of units.
For instance, if somewhere between 2-5 words should be generated you could use the following comment.
The script is just as forgiving with whitespace between the range numbers as it is with the rest of the format.
<-- 2- 5 word -->


There's a main method included that will start at the <body> element & process all comment nodes in the document. This method can be called at the end of the document, or added as an event listener.
LoremIpsum.LoremIpsum();
window.addEventListener('load', LoremIpsum.LoremIpsum, false);
window.attachEvent('onload', LoremIpsum.LoremIpsum);


The processing can also be rooted at any other node by using the scanElm method.
var trunk = document.getElementById('example');
LoremIpsum.scanElm(trunk);


The script doesn't have to be used to replace comments in the document, or in a certain node.
The word, sentence, & paragraph generation methods can be called directly & will return however many units they're asked for.
The only restriction is that ranges can't be used when calling the methods directly.
If you want random numbers of units, you'll need to generate those numbers yourself.
var elm = document.getElementById('elm');
var word_count = Math.ceil(Math.random() * 10);
var ipsum = LoremIpsum.words(word_count);
elm.appendChild(document.createTextNode(ipsum));


Quite a few aliases are included for each method.
Camel notation, Underscore notation, Lowercase notation.
main & init are included for starting with the root element.
get_* schemes are included for the generation methods.
/* Convenience / aliases / etc */
LoremIpsum.main = LoremIpsum.init = LoremIpsum.LoremIpsum;
LoremIpsum.getWords = LoremIpsum.get_words = LoremIpsum.words;
LoremIpsum.getSentences = LoremIpsum.get_sentences = LoremIpsum.sentences;
LoremIpsum.getParagraphs = LoremIpsum.get_paragraphs = LoremIpsum.paragraphs;
LoremIpsum.scan_elm = LoremIpsum.scanelm = LoremIpsum.scanElm;
lorem_ipsum = loremipsum = loremIpsum = LoremIpsum;


Here's the source, it saves to about 4.5KB fully commented & uncompressed.
The methods include further documentation in the form of comments.

LoremIpsum = {
fullstop: '.', /* Character(s) to add to the end of sentences */
min_wps: 10, /* Minimum words per sentence */
max_wps: 25, /* Maximum words per sentence */
min_spp: 2, /* Minimum sentences per paragraph */
max_spp: 8, /* Maximum sentences per paragraph */

/* Word bank
Words are selected from this array randomly
to generate complete text nodes.

NOTE: This array __can__ be altered provided it remains a single-dimension array.
*/
src: new Array('a','adipiscing','amet','arcu','at','auctor','bibendum','commodo','congue','curabitur','cursus','diam','donec','duis','eget','elementum','enim','eros','et','eu','fusce','gravida','in','integer','ipsum','justo','lectus','leo','ligula','lorem','maecenas','magna','malesuada','massa','mattis','mauris','metus','molestie','morbi','nam','nec','nibh','non','nulla','odio','orci','ornare','pellentesque','pharetra','porta','porttitor','proin','quam','quisque','risus','rutrum','sagittis','sapien','sed','sem','sit','sodales','tellus','tempus','ultricies','urna','ut','vitae','vivamus','vulputate'),

/* words, getWords, get_words
@param words: Number of words to return
@param fullstop: String to append to the words, such as sentence ending punctuation.
@returns String: String of whitespace delimited words.
*/
words: function(words, fullstop){
var lastword, nextword, str, wc = (LoremIpsum.src.length - 1);
lastword = nextword = Math.round(Math.random() * wc);
str = LoremIpsum.src[lastword].charAt(0).toUpperCase() + LoremIpsum.src[lastword].substr(1);
words--;
while(words > 0){
while(lastword == nextword){
nextword = Math.round(Math.random() * wc);
}
str += (' ' + LoremIpsum.src[nextword]);
lastword = nextword;
words--;
}
return str + (fullstop ? fullstop : '');
},

/* sentences, getSentences, get_sentences
@param sentences: Number of sentences to return.
@returns String: String of sentences delimited by current value of LoremIpsum.fullstop
*/
sentences: function(sentences){
var wpsos = LoremIpsum.max_wps - LoremIpsum.min_wps;
var str = LoremIpsum.words((LoremIpsum.min_wps + Math.round(Math.random() * wpsos)), LoremIpsum.fullstop);
sentences--;
while(sentences > 0){
str += (' ' + LoremIpsum.words((LoremIpsum.min_wps + Math.round(Math.random() * wpsos)), LoremIpsum.fullstop));
sentences--;
}
return str;
},

/* paragraphs, getParagraphs, get_paragraphs
@param paragraphs: Number of paragraphs to return.
@param format: How to format each paragraph, where %s is the text.
For instance: <p class="myclass">%s</p>
@returns String: String of paragraphs formated using @format.
*/
paragraphs: function(paragraphs, format){
if(!format){
format = "%s\n\n";
}
var sppos = LoremIpsum.max_spp - LoremIpsum.min_spp;
var str = format.replace(/%s/i, LoremIpsum.sentences(LoremIpsum.min_spp + Math.round(Math.random() * sppos)));
paragraphs--;
while(paragraphs > 0){
str += format.replace(/%s/i, LoremIpsum.sentences(LoremIpsum.min_spp + Math.round(Math.random() * sppos)));
paragraphs--;
}
return str;
},

/* scanElm, scanelm, scan_elm
@param elm: Entry point, DOM Node to begin searching for comment nodes.
@returns Number: The number of comments replaced with text nodes.
*/
scanElm: function(elm){
var rc = 0;
for(var c = 0; c < elm.childNodes.length; c++) {
var child = elm.childNodes[c];
if(child.nodeType == 1){
LoremIpsum.scanElm(child);
}else if (child.nodeType == 8){
var matches = child.data.match(/^\s*(\d+(?:\s*-\s*\d+)?)\s+(words?|sentences?|paragraphs?)\s*$/i);
if(matches){
if(matches[1].indexOf('-') > -1){
var ms = matches[1].split(/\s*-\s*/);
matches[1] = Math.max(ms[0], Math.ceil(Math.random() * ms[1]));
}
child.parentNode.replaceChild(document.createTextNode(LoremIpsum[matches[2]](matches[1])), child);
rc++;
}
}
};
return rc;
},

/* LoremIpsum, main, init
Starts with the root node & recursively processes all comment nodes.
*/
LoremIpsum: function(){
var body = document.documentElement ? document.documentElement : document.body;
LoremIpsum.scanElm(body);
}
};
/* Convenience - aliases - etc */
LoremIpsum.main = LoremIpsum.init = LoremIpsum.LoremIpsum;
LoremIpsum.getWords = LoremIpsum.get_words = LoremIpsum.words;
LoremIpsum.getSentences = LoremIpsum.get_sentences = LoremIpsum.sentences;
LoremIpsum.getParagraphs = LoremIpsum.get_paragraphs = LoremIpsum.paragraphs;
LoremIpsum.scan_elm = LoremIpsum.scanelm = LoremIpsum.scanElm;
lorem_ipsum = loremipsum = loremIpsum = LoremIpsum;

Monday, August 4, 2008

Native Ubuntu/Gnome Alternative to Colorpix

One of the applications I've missed most when transitioning from Windows 2000/XP to Ubuntu Linux is Colorpix, a useful little color picker that grabs the pixel under your mouse and transforms it into a number of different color formats.

I tried gcolor2, but wasn't very fond of it. gcolor2 included a bunch of functionality I didn't need and not enough of the functionality I wanted. Perhaps the functionality with the largest weight on my decision making was the magnified section provided by Colorpix for making sure I get the color for the pixel I want. It might seem obvious in some instances when you're over the color you want without magnification, but alot of times I'm looking for the color of a single pixel wide line & it's nice to know I need to move left one pixel instead of moving right a few pixels before realizing I'm going the wrong direction.

My other alternative, was using colorpix through Wine like some bloggers have mentioned here and here.
The only problem I had with that is I don't always have Wine up and running, so it can take a few seconds to load.


So, since I'm trying to pickup C/C++ I decided to write my own implementation of Colorpix to run natively on my Ubuntu system.
So far it starts instantly VS waiting for a translator to load, displays the color in RGB and hexidecimal format, and magnifies an 11x11 pixel area around the pointer 2000%

I've made the source available via a project named Picksel at Google Code.

Friday, July 18, 2008

Byte Chart w/ Large Numbers

Chart showing filesizes

It took me entirely too long to lookup how many digits were in a Petabyte, so after some hunting I put together this neatly organized chart displaying up to the 25th digit or 1024^8th place.
I tried to arrange the data in such a way that the chart shouldn't need much explaination or any legends or keys.

It shows
  • kibibyte / kilobyte filesize
  • mebibyte / megabyte filesize
  • gibibyte / gigabyte filesize
  • tebibyte / terabyte filesize
  • pebibyte / petabyte filesize
  • exbibyte / exabyte filesize
  • zebibyte / zettabyte filesize
  • yobibyte / yottabyte filesize


I noticed an easy way to remember which digit a Yottabyte starts with, Y is the 25th letter in the English alphabet & 25 is the digit a Yottabyte starts at. The same goes for P and Petabyte.

Thursday, June 26, 2008

SMS/Email Domains for U.S. Cellular Providers

I often see people asking how to send SMS messages from their website. Well, you can do so through your SMTP server or if you're using some other type of email system, for instance Postfix on Debian, you can use that too.

You send the message/email just like you would any other email, you just have to direct it to the address the service provider has setup to recieve messages.

In most cases this is the cellphones ten digit number @ provider-address dot com.
Below is a list of common U.S. service providers and the domain they have setup to recieve SMS messages via email protocols.

CarrierDomain Pattern
AirTouch Cellular 10digitphonenumber@airtouchpaging.com
Alltel 10digitphonenumber@alltelmessage.com
Ameritech Cellular 10digitphonenumber@paging.acswireless.com
AT&T Wireless 10digitphonenumber@mobile.att.net
Bell Atlantic 10digitphonenumber@message.bam.com
BellSouth 10digitphonenumber@wireless.bellsouth.com
Cellular One Not available from carrier
Cingular Wireless 10digitnumber@mobile.mywireless.com
Comcast Cellular 10digitphonenumber@cellularone.tstmsg.com
GTE Wireless 10digitphonenumber@messagealert.com
Nextel 10digitphonenumber@page.nextel.com
Omnipoint 10digitphonenumber@omnipointpcs.com
Pacific/Nevada Bell 1+10digitphonenumber@pacbellpcs.com
PrimeCo 10digitphonenumber@primeco.textmessage.com
Southwestern Bell 10digitphonenumber@email.swbw.com
Sprint 10digitphonenumber@messaging.sprintpcs.com
Suncom 10digitphonenumber@mobile.att.net
TMobile 10digitphonenumber@tmomail.net
Telecorp 10digitphonenumber@mobile.att.net
Tritel 10digitphonenumber@mobile.att.net
Triton PCS 10digitphonenumber@mobile.att.net
U.S. West 10digitphonenumber@uswestdatamail.com
Verizon 10digitphoneumber@msg.myvzw.com
Voicestream 10digitphonenumber@voicestream.net
Boost Mobile 10digitphonenumber@myboostmobile.com


Google is able to look these up just by having the number somehow, I'd really like to know where's they're getting it from.

preg_replace_callback VS preg_replace

Some interesting results of a test between the preg_replace_callback function VS the preg_replace function using the e pattern modifier.

Here's the test code.
<?php
function uppers($match)
{
return strtoupper($match[0]);
}
$out = '12345678901234567890123456789012';
$strs = array();
$str_count = 1000;
while($str_count)
{
$strs[] = md5($str_count . microtime());
$str_count--;
}

foreach($strs as &$str)
{
$out = preg_replace('#[ace]+#e', 'strtoupper($0)', $str);
}

foreach($strs as &$str)
{
$out = preg_replace_callback('#[ace]+#', 'uppers', $str);
}

?>


The test was benchmarked using the profiler included in the Xdebug Debugger and Profiler Tool for PHP.
OS is Ubuntu 8.04, Apache version 2.2.8, PHP version 5.2.4
Profiler output is being viewed in KCachegrind.

Here's the callee map generated from the output.



And here's the slightly easier to understand source map.



The two numbers on the left are the percentage of the total runtime dedicated to the actions next to them.
It appears to me that the preg_replace_callback way is faster, but I would love to see how it performs on other OS's if anyone cares to do their own tests & comment.

Sunday, June 22, 2008

Unexpected '}' In Runtime-Created Function

When working the the create_function function in PHP, it's possible to get an error message similar to the following.

Parse error: syntax error, unexpected '}' in /var/www/test.php(36) : runtime-created function on line 1


Now if you're using the return from create_function in an anonymous context you might also get a message like the following.

Warning: preg_replace_callback() [function.preg-replace-callback]: Requires argument 2, '', to be a valid callback in /var/www/test.php on line 36


After getting a Parse Error in the beginning, a subsequent warning can be downright baffling since parse errors generally halt script execution. Apparently parse errors within anonymous, create_function context only halt execution within that context though.

Now when you look into that initial error and the create_function code portion thinking maybe you used a curly brace to end a function call instead of a closing parenthesis and don't see a curly brace anywhere "baffled" can't begin to explain the feeling you might have.

The reason for the initial error likely has to do with a missing semi-colon in the code argument of your create_function call. For instance if your code simply runs the arguments through a function like the following.

$str = preg_replace_callback('#[ACEGIKMOQSUWY]+#', create_function('$a', 'return strtolower($a[0])'), $str);


That seems perfectly fine, however it needs to have the semi-colon to end the function call as in the following.

$str = preg_replace_callback('#[ACEGIKMOQSUWY]+#', create_function('$a', 'return strtolower($a[0]);'), $str);


This fact can be extremely confusing if you've used preg_replace with the e modifier where this ending semi-colon isn't required.

Tip:

preg_replace_callback can be twice or more as fast as preg_replace and the e modifier.

Friday, June 20, 2008

Random Color Generation With Javascript

I'm sure there's quite a few ways to get a random color using JS, I can think of a few such as returning a random index from an array of CSS color names, or generating three random numbers between zero and 255 and formatting from there.

The following method is my favorite though because the single randomly generated integer can be easily converted into pretty much any other format or quickly returned as-is.
To illustrate this I've written the following function which will return a hexidecimal, RGB, or integer format random color depending on the argument passed to the function.

// @format (hex|rgb|null) : Format to return, default is integer
function random_color(format)
{
var rint = Math.round(0xffffff * Math.random());
switch(format)
{
case 'hex':
return ('#0' + rint.toString(16)).replace(/^#0([0-9a-f]{6})$/i, '#$1');
break;

case 'rgb':
return 'rgb(' + (rint >> 16) + ',' + (rint >> 8 & 255) + ',' + (rint & 255) + ')';
break;

default:
return rint;
break;
}
}

Tuesday, June 17, 2008

50 Random PHP5 Facts

Here's a list of 50 49 random facts about PHP.

  1. You MUST load PDO before loading any PDO drivers
  2. An iterator cannot be used with foreach by reference
  3. Cannot create references to elements of a temporary array expression
  4. Cannot create references to/from string offsets nor overloaded objects
  5. Cannot assign by reference to overloaded objects
  6. Only variable references should be returned by reference
  7. Only variables can be passed by reference
  8. Cannot return string offsets by reference
  9. Cannot use string offset as an array
  10. Cannot use object as array -- See Dans comment
  11. Cannot use a NULL value as an array
  12. Cannot use a scalar value as an array
  13. foreach() can not iterate over objects without PHP class
  14. Cannot use assign-op operators with overloaded objects nor string offsets
  15. __HALT_COMPILER() can only be used from the outermost scope
  16. Assigning the return value of new by reference is deprecated
  17. Variables passed to each() must be an array or object
  18. Constants may only evaluate to scalar values
  19. is_a() is deprecated. The "instanceof" operator should be used instead This has changed as of PHP 5.3, see Gregs comment
  20. Cannot add internal functions to return value from get_defined_functions()
  21. Cannot add user functions to return value from get_defined_functions()
  22. Static functions cannot be abstract
  23. Exceptions must be derived from the Exception base class
  24. Need to supply an object when throwing an exception
  25. Cannot declare self-referencing constants
  26. Cannot use call_user_function on objects without a class entry
  27. Cannot access self:: when no class scope is active
  28. Cannot access parent:: when no class scope is active
  29. Cannot access parent:: when current class scope has no parent
  30. add_assoc_function() is no longer supported
  31. Usage of {} to access string offsets is deprecated and will be removed in PHP 6
  32. Cannot re-assign $this
  33. __toString() must not throw an exception
  34. Can't use method return value in write context
  35. Can't use function return value in write context
  36. Multiple access type modifiers are not allowed
  37. Cannot use the final modifier on an abstract class member
  38. Default value for parameters with a class type hint can only be NULL
  39. Default value for parameters with array type hint can only be an array or NULL
  40. Cannot call __clone() method on objects - 'clone $obj' should be used instead
  41. Clone method does not require arguments
  42. Interfaces cannot not implement themselves
  43. Classes may not inherit from classes declared final
  44. Classes cannot inherit previously-inherited constants from interfaces
  45. Classes cannot implement previously-implemented interfaces
  46. Class declarations may not be nested
  47. Cannot use reserved words as class or interface names
  48. Interfaces may not include member variables
  49. Properties cannot be declared abstract
  50. Cannot declare properties final, the final modifier is allowed only for methods

Wednesday, June 4, 2008

Xdebug MUST be loaded as a Zend extension

XDebug profiler not working ?
Found this in your error log ?

PHP Warning: Xdebug MUST be loaded as a Zend extension ...



You've probably installed xdebug.so or php_xdebug.dll using a line similar to this in php.ini.

;extension=xdebug.so
; or
;extension=php_xdebug.dll


Well the fix seems to be simple, as seen in xdebugs installation instructions.

You need to use a different directive, and you need to specify the entire path to the so/dll.

Making Windows something similar to this
zend_extension_ts = "c:/php/modules/php_xdebug.dll"


And making UNIX/PECL something like this
zend_extension = "/usr/lib/php5/modules/xdebug.so"


I imagine the "_ts" corosponds to "Thread Safe" which could mean the "_ts" directive needs to be used on IIS, Apache2, & other multi-threaded servers, but I'm not entirely sure & haven't found any documentation on that yet.
So far the non-_ts version is working on my Apache2 test box, though.


Update: Using "zend_extension_ts" didn't work at all on my Debian/Apache2 box.

Now if you're using the new directive and still getting errors, which happen to come half-a-dozen at a time, in your logs, then you will want to add the directory used for extension_dir to the value of include_path in php.ini.

; Assuming the following
;extension_dir = "/path/to/extensions"
;extension_dir = "c:\path\to\extensions"

; UNIX: "/path1:/path2"
;include_path = ".:/usr/share/php:/path/to/extensions"
;
; Windows: "\path1;\path2"
;include_path = ".;c:\php\includes;c:\path\to\extensions"

Wednesday, April 30, 2008

PHP Error: function return in write context

I was presented with a strange error in PHP today,

Fatal error: Can't use function return value in write context in /var/www/script.php on line 31


This is the code I was using on line 31.

$email = empty(ini_get('sendmail_from')) ? $default : ini_get('sendmail_from');


The problem was with the call to ini_get being within a call to empty.

To get rid of the error, I needed to check for empty values returned from ini_get differently.

$email = ini_get('sendmail_from') == '' ? $default : ini_get('sendmail_from');

Sunday, April 13, 2008

gedit External Tools indent-- & indent++

UPDATE: The same thing can be accomplished by simply pressing the tab key when you have a block of code selected, hold shift to decrease indent instead of increase. Thanks Ben !

program conditional block example There's times when I have to remove an outter conditional block from a script & in order to keep everything orderly this means I also need to decrease the indent in the code within that conditional block by one.

To the right is an example of this situation. I want to remove the else outter conditional since the inner code needs to be evaluated whether the preceeding code succeeds or not.

Normally in something like Notepad this would mean alot of fancy finger work on the down arrow and the delete key on my keyboard.
In Notepad2 I was able to utilize the find & replace support for Regular Expressions and remove the tabs from the beginning of each line in the selection.

Now that I have gedit as my primary editor since I'm on Ubuntu I don't have acces to Notepad2, however I do have access to the External Tools plugin for gedit.

Through the external tools plugin I can filter selected text through sed, which is basically a RegEx filter designed to be used with pipes.

So I setup two new commands through that plugin, one for decreasing the indent by one which I call indent-- and another which I use to do the exact oppisite which I call indent++

If you're new to gedit like I am, first you'll need to enable the Enternal Tools plugin.
You do this by going through
Edit -> Preferences -> Plugins Tab -> Check "External Tools"


This will enable the following menu option where you can manage commands to filter selected text and documents through.
Tools -> External Tools


The dialog for managing tools is pretty self explainitory, here's what I entered for the command to be used for my indent-- tool.
#!/bin/sh

sed 's/^\t//g'



Here's the command I'm using for my indent++ tool.
#!/bin/sh

sed 's/^/\t/g'


I actually got the idea after seeing the existing command to remove whitespace from the end of lines.

Sunday, April 6, 2008

UID of script is smaller than min_uid

I just spent almost two hours trying to figure out what this error meant for suPHP.
I couldn't figure out whether it applied to the UID of the file itself, or the user the process was running as when it started just going by what I found through searching.

So I ended up installing suPHP on my Ubuntu box just to find out for myself.
I went ahead and installed the Apache2 module, to make things easy.
sudo apt-get install libapache2-mod-suphp


Once that was installed it restarted Apache by itself & the first PHP script I accessed in my browser worked like a charm. Reason being, the PHP script was already owned by the user I usually use to test stuff out with, a non-root user with a UID of 100.

Now when I chowned the file to be owned by root,
sudo chown root /var/www/test.php


I got the following error showing up in /var/log/suphp/suphp.log.
[Sun Apr 06 07:45:22 2008] [warn] UID of script "/var/www/test.php" is smaller than min_uid


As soon as I gave the file back to myself it worked fine however.
sudo chown joebert /var/www/test.php



Update: Now that I read this post a few months later, I'm not sure what the actual problem was is clear enough. It's not soo much who owns the file as much as it is what their system UID is.

When a user account is created on the system it's generally given an ID number. Usually when you create a user account which will actually get logged into by a human, such as "bob" or "arlene", rather than a system/service account such as "www-data" or "postfix", the UID will tend to be 100+. However with a system/service account the UID will tend to be below 100.

So, if you chown the problematic file to "root", "www-data", or similar, you're likely going to get the min_uid error since those accounts tend to be system/service accounts.

The point of suPHP is to make sure scripts are running as the person who owns them. If you need to assign ownership of scripts to services, you'll have to modify the suPHP configuration file so that min_uid is as low as the UID of the account you wish to assign ownership of the file to.

Saturday, March 22, 2008

MySQL ORDER BY multiple columns

Given this MySQL query where the single letter column names relate to the format argument of the PHP date function,

SELECT Y,n,j,G
FROM my_table
ORDER BY Y,n,j,G DESC
LIMIT 1


One might assume the most recent row of the following two would be returned.
In case you don't know these off the top of your head the order is,
hour, day, month, year

INSERT INTO my_table (G, j, n, Y) VALUES 
(23, 27, 10, 2007),
(0, 28, 10, 2007)


However that's not the case & the earlier of the two is actually returned.

Reason being that when a MySQL result is being ordered by multiple columns, the ASC or DESC keyword needs to be specified after each of the column names.

So the following query would return the later of the two rows.

SELECT Y,n,j,G
FROM my_table
ORDER BY Y DESC,n DESC,j DESC,G DESC
LIMIT 1

Wednesday, February 13, 2008

SVN: Can't open file '/format' Permission denied

I setup a Subversion repository on my Ubuntu system yesterday. Everything went smoothly for the most part.

I installed the Subversion package with the following command. Included in the package was svn, svnadmin, & svnserve.
sudo apt-get install subversion


I added a new subversion user & group. I added the new subversion user, and myself, to the subversion group.
sudo useradd --password pass subversion
sudo groupadd subversion
sudo adduser subversion subversion
sudo adduser me subversion


I created a repository, changed ownerships, & permissions.
sudo svnadmin create /svn/myrepos
sudo chown -R me:subversion /svn/myrepos
sudo chmod -R 0770 /svn/myrepos


I modified the repositories conf/passwd and conf/svnserv.conf files to allow read-only access by anonymous users & write access for me.

I went in and modified certain files like conf/passwd & conf.svnserv.conf to 0440 (owner & group read-only)

I started svnserve in the /svn directory as the subversion user.
sudo su subversion
svnserve -d -r /svn


But when trying to checkout the repository with Tortoise SVN on my Windows machine, I kept getting an error similar to the following.
Error Can't open file '/svn/format': Permission denied


Even when I chmod'ed the format file to 0777, I still got the error.

Come to find out, it really had little to do with that file directly. What it did have to do with was the permissions of the user that started svnserve & the directory svnserve was told to use as the repository root.

/svn had 0760 permissions, as soon as I gave my subversion group execute permissions so the subversion user who is a member of the subversion group & is starting the server could execute, it worked like a charm.
sudo chmod 0770 /svn

Tuesday, February 5, 2008

Hello World C Application On Ubuntu

Working with PHP has been a good way for me to become more familiar with C programming. It seems that alot of the syntax carries over between the two. Same with standard function names, like fopen for instance.
My biggest issue with picking up C was that I always became fustrated with trying to setup and understand compilers & the tools that come with them. I couldn't even get a Hello World application written in C to work.

I decided to give C Programming another try on my Ubuntu box. All of my previous attempts had been on Windows, and now that I've become familiar with Ubuntu it seemed like somthing worth trying.

It was actually alot easier than I thought it was going to be. Since I don't have a monitor hooked up to my Ubuntu computer I used PuTTy to run commands, and FTP for file editing.

First thing I did was use PuTTy to create a foo directory in my home directory, then I put some files in the foo directory.
me@box:~$ mkdir /home/me/foo
me@box:~$ cd /home/me/foo
me@box:~/foo$ touch .deps
me@box:~/foo$ touch Makefile
me@box:~/foo$ touch hello.c


I opened hello.c in my text editor, and typed the following.
#include <stdio.h>

main()
{
puts("Hello World!");
return 0;
}


After I saved hello.c and placed it on the server with FTP I ran the following command.
me@box:~/foo$ make hello

Which output the following before going back to the prompt.
cc     hello.c   -o hello


That's it. I ran the application from the prompt & out popped "Hello World!".
me@box:~/foo$ ./hello
Hello World!
me@box:~/foo$

Friday, January 25, 2008

sudo: /etc/sudoers is owned by

Right about now you're probably looking at somthing similar to this.
sudo: /etc/sudoers is owned by gid 1000, should be 0


Maybe you accidently chown'd or chmod'd files recursively where the etc directory was effected. Maybe you did the same thing I did & chowned /etc/sudoers to root:mygroup instead of root:root like it's supposed to be.

In any event you're looking at a message like the one above and you can't use sudo anymore. This is because the sudo application wants things exactly right or it assumes there's been a compromise.
The file /etc/sudoers should be owned by root:root and should not be readable by anyone other than root.

Unless for some reason you allow remote root logins, you will need to physically have the server in front of you to fix this issue. If you don't have a monitor hooked up to the system, you better find a monitor to hookup now.

If you have the root password, login as root & chown the file to root:root.
root@box:~$ chown root:root /etc/sudoers

Then chmod the /etc/sudoers to 0440.
root@box:~$ chmod 0440 /etc/sudoers


If you, like I, never set a root password, you'll need to reboot the system, press ESC during boot, & select the recovery mode from the boot options. That will give you a terminal logged in as root you can use to run the chown and chmod commands.

Once the proper ownership & permissions are set on that file you can restart the system as usual & sudo should be up and running again.

Saturday, January 12, 2008

Fancy Blogger Labels Widget

I just noticed Blogger has a post labels widget the other day. After adding it to my layout I discovered I was using quite a few one-time-use labels for my posts. I spent a little while going through my labels & trying to consolidate some of the one-timers into better upper-level groups.
Even after doing this there was still a huge list of words running down the right side of the layout, and I really didn't like how that worked.

So, I fixed it.

With the widget added to my layout, I then modified the HTML used so it was a little cleaner as well as matching the rest of the layout. Blogger templates seem to like using <h2> elements for sidebar items and I like to use <h6> elements.

<b:widget id='Label1' locked='false' title='By Subject' type='Label'>
<b:includable id='main'>
<span class='top'><span/></span>
<div class='content' id='by-subject'>
<b:if cond='data:title'><h6><data:title/></h6></b:if>
<ul><b:loop values='data:labels' var='label'><li><b:if cond='data:blog.url == data:label.url'><data:label.name/><b:else/><a expr:href='data:label.url'><data:label.name/></a></b:if> <small><data:label.count/></small></li></b:loop></ul>
</div>
<span class='bottom'><span/></span>
</b:includable>
</b:widget>


With that, I have a clean matching list of labels in the event JS is disabled.

Now for the fancy part.
I'm having jQuery split that list up into smaller lists, much like the Last 50 Posts navigation works with the dates.
Here's the JS I'm using to do that, note that it's among the things that happen after the document has loaded.
var tags = $('<ul/>');
var branch;
var label = {start:'', end:''};
var tagIndex = 0;
$('#by-subject li').each(function(){
tagIndex++;
label.end = $(this).children('a').text().toLowerCase();
switch(tagIndex){
case 1:
branch = $('<ul/>');
label.start = label.end;
break;
case 10:
tagIndex = 0;
var at = $('<li><a href="#">(' + label.start + ') -- (' + label.end + ')</a></li>');
at.children('a').click(function(){
$(this).siblings('ul').slideToggle(500);
return false;
});
branch.slideUp(1);
branch.appendTo(at);
at.appendTo(tags);
break;
}
$(this).appendTo(branch);
});
var at = $('<li><a href="#">(' + label.start + ') -- (' + label.end + ')</a></li>');
at.children('a').click(function(){
$(this).siblings('ul').slideToggle(500);
return false;
});
branch.slideUp(1);
branch.appendTo(at);
at.appendTo(tags);
$('#by-subject ul').replaceWith(tags);


Basicly, the list is split up into sections of 10 items & each section is labeled with the first and last items in each section. I'll probably switch it to 20 items sometime in the future.

Tuesday, January 8, 2008

Javascript Random Ad Buttons/Banners

I like to think of ad banners on my blog like like the racks in the checkout lines at the grocery store, Impulse Items. I wanted to be as un-obtrusive as possible with ad banners, while still having them stand out.

In order to do this I'm using JS to display them. That way I can defer them from showing untill the content people have come here from search engines to read in the first place has already loaded.
I absolutely hate it when a site makes me stare at a blank section where the article would be while I wait for the advertisements to load. If there's another result I considered clicking on I will immediately close the tab with that blank section in it & go with the alternative instead.
I also don't want to go out of my way to force people to look at advertisements by bypassing blockers & such, doing so appears to be a waste of time.

Now that we know I don't want to intrude, but do want to nag you a little bit, here's how I'm doing it.

First I have an array with objects for all of my banners. Each object includes the alt, href, and src attributes I will need for each banner. Though for some odd reason I decided to use url to denote my href attributes here.
var impulses = new Array(
{alt:'image alt text', url:'click-through-url', src:'image-source'},
{alt:'image alt text', url:'click-through-url', src:'image-source'},
{alt:'image alt text', url:'click-through-url', src:'image-source'}
);


For the sake of simplicity there's only three in that example, but just imagine there's about twenty of those objects in that array.

Now since I don't want the order they're shown in from left to right to be too predictable I'm going cycle a random number of ad banner objects to the end.
Basicly what this code is doing, is geting a random number between zero and the number of ad banner objects there are in the impulses array, and moving the first ad banner object to the end of the array that many times.
for(var i = Math.round(Math.random() * impulses.length); i > 0; i--)
{
impulses.push(impulses.shift());
}


Next I narrow the size of the array down to the number of ad banners I want shown.
Basicly what this code is doing is randomly picking an ad banner object in the array and removing it, untill there's only as many as I want left.
while(impulses.length > 5)
{
impulses.splice(Math.round(Math.random() * (impulses.length - 1)), 1);
}


Once the ad banners that will be displayed are determined I generate the HTML to show them with, I'm using simple <a> and <img> elements for this. I like to generate it using basic data like this so it's easier to modify the actual HTML that's used in a single place later on if I need to.
for(var i = 0; i < impulses.length; i++)
{
impulses[i] = '<a href="'+impulses[i].url+'"><img alt="'+impulses[i].alt+'" src="'+impulses[i].src+'"/></a>';
}


Now with a string of ad banner HTML to use, I make use of the jQuery library to insert it amongst my pages.
var impulse = jQuery("<div id='impulses'>" + impulses.join('') + "</div>").insertBefore('#load-anim');