Archive for the 'JavaScript' Category

Canvas pie with tooltips

Saturday, March 8th, 2008

This is very flattering: Greg Houston took my script for DIY canvas pie and added tooltips and better colors logic. Here's the result, it's really nice, built with some MooTools. The tooltips are not supported in <canvas> but Creg used an image that overlays the pie and set the tooltips with an image map. Clever, isn't it?

I just used random colors for the pieces of the pie, but Greg knows better. Based on a tutorial by Jim Bumgardner, he was able to achieve much more pleasing, yet reliable results. Funny thing that Jim works in Yahoo! two floors above me (used to be even on the same floor).

Check the demo and the post, cool stuff, works in IE too!

 

replace selected text (Firefox)

Thursday, March 6th, 2008

A quick function to replace the selected text (in Firefox). Accepts:

  • string to replace the selection, or
  • callback function that takes the selected text and returns a string to replace the selection
function replaceSelection(t) {
  if (typeof t === 'function') {
    t = t(window.getSelection().toString());
  }
  var range = window.getSelection().getRangeAt(0);
  range.deleteContents();
  range.insertNode(document.createTextNode(t));
}

Usage:

1. Replace selected text with "dude"
replaceSelection('dude');
2. Uppercase the selected text
replaceSelection(function(s){return s.toUpperCase()});
3. Lowercase the selected text
replaceSelection(function(s){return s.toLowerCase()});

Luckily I'm so close (and yet so far) to finishing this book, otherwise I won't be able to help myself but create a whole toolbar of bookmarklets for "power" Google doc users...

 

Canvas pie

Tuesday, February 5th, 2008

UPDATE: Translation in Brazilian Portuguese here, thanks Maujor!

OK, so you have an HTML table. Let's turn it into a pie chart with a bit of javascript.

We'll be using the canvas tag, so the browser has to support it. For IE - well, you still have the table. That's why we'll call it progressive enhancement. Unobtrusive too. Here's a screenshot:
canvas-pie.png

» The demo is here (refresh for new colors)

Here are the ingredients to the recipe:

  1. One <canvas> tag
  2. One <table> full of data
  3. javascript to get the data from the table
  4. javascript to plot the data on the canvas

One canvas tag

<canvas id="canvas" width="300" height="300"></canvas>

One table full of data

This is a bare bone unstyled old school table.

<table id="mydata">
    <tr>       <th>Lang</th><th>Value</th> </tr>
    <tr><td>JavaScript</td>  <td>100</td>  </tr>
    <tr><td>       CSS</td>  <td>200</td>  </tr>
    <tr><td>      HTML</td>  <td>300</td>  </tr>
    <tr><td>       PHP</td>  <td> 50</td>  </tr>
    <tr><td>     MySQL</td>  <td> 30</td>  </tr>
    <tr><td>    Apache</td>  <td> 10</td>  </tr>
    <tr><td>     Linux</td>  <td> 30</td>  </tr>
</table>

javascript to get the data from the table

First, some setup. Let's tell the script which is the ID of the data table, the ID of the canvas tag and which column contains the data:

// source data table and canvas tag
var data_table = document.getElementById('mydata');
var canvas = document.getElementById('canvas');
var td_index = 1; // which TD contains the data

Next, select all table rows, then loop through the rows, selecting all TDs. Add the data we need to a data array. While at it, run a total of the data in the column and also create an array of random colors. Paint each row with the selected color. (we'll see the actual getColor() in a bit.)

var tds, data = [], color, colors = [], value = 0, total = 0;
var trs = data_table.getElementsByTagName('tr'); // all TRs
for (var i = 0; i < trs.length; i++) {
    tds = trs[i].getElementsByTagName('td'); // all TDs

    if (tds.length === 0) continue; //  no TDs here, move on

    // get the value, update total
    value  = parseFloat(tds[td_index].innerHTML);
    data[data.length] = value;
    total += value;

    // random color
    color = getColor();
    colors[colors.length] = color; // save for later
    trs[i].style.backgroundColor = color; // color this TR
}

javascript to plot the data on the canvas

Time for the fun part, the drawing! First, we need to create a context object. Then figure out the raduis of the pie and the center, all based on the width/height pf the canvas tag:

// get canvas context, determine radius and center
var ctx = canvas.getContext('2d');
var canvas_size = [canvas.width, canvas.height];
var radius = Math.min(canvas_size[0], canvas_size[1]) / 2;
var center = [canvas_size[0]/2, canvas_size[1]/2];

Next, let's loop through data and paint pieces of the pie. To draw a piece of pie, you basically need to call these methods of the context object:

  • beginPath() - to start the piece of the pie
  • moveTo() - to set the pencil in the center
  • arc() - draw a piece of a circle
  • lineTo() - finish the circle piece with a line back to the center
  • closePath() and fill() but set the fill color first.

Here's the actual code for this part, hopefully the comments help:

var sofar = 0; // keep track of progress
// loop the data[]
for (var piece in data) {

    var thisvalue = data[piece] / total;

    ctx.beginPath();
    ctx.moveTo(center[0], center[1]); // center of the pie
    ctx.arc(  // draw next arc
        center[0],
        center[1],
        radius,
        Math.PI * (- 0.5 + 2 * sofar), // -0.5 sets set the start to be top
        Math.PI * (- 0.5 + 2 * (sofar + thisvalue)),
        false
    );

    ctx.lineTo(center[0], center[1]); // line back to the center
    ctx.closePath();
    ctx.fillStyle = colors[piece];    // color
    ctx.fill();

    sofar += thisvalue; // increment progress tracker
}

utility

Here's the function that gives a random color:

    // utility - generates random color
    function getColor() {
        var rgb = [];
        for (var i = 0; i < 3; i++) {
            rgb[i] = Math.round(100 * Math.random() + 155) ; // [155-255] = lighter colors
        }
        return 'rgb(' + rgb.join(',') + ')';
    }

C'est tout! Enjoy your pie :D

UPDATE: Comment by Zoltan below, if you use Explorer Canvas, you can make this work in IE with only this:
<!--[if IE]><script type="text/javascript"
src="/path/to/excanvas.js"></script><![endif]-->

 

MP3 player from Yahoo! – bookmarklet

Wednesday, January 23rd, 2008

Update Jan 30, 2008: updated code based on comments and code from Carl
Update Dec 11, 2008: updated the code thanks to the comment from Nolan

Here's the scenario: you have a page that links to some .mp3 files. You add a line of code in your page and lo and behold, there's a nice media player embedded into the page. Your visitors no longer have to download the MP3s, they can just stream it right there. All the mp3s on the page become part of a playlist.

The line in question you need to add to your page is:

<script src = "http://mediaplayer.yahoo.com/js"></script>

More about the player here - yahoomediaplayer.wikia.com

For examples of sites using this player in the wild, try aurgasm.us

A bookmarklet

Now, here's what you can do if you want to use the player, but the web site owner haven't incorporated it yet. Take the player with you. Run my bookmarklet that will simply insert the required javascript into the page.

So here goes in two easy steps:

  1. Grab the bookmarklet:

    Right-click, add to favorites

  2. Go to any page that links to MP3s and click your new shiny bookmarklet

    Enjoy!

Source

The readable source code of the bookmarklet:

javascript:(function(){

  var start = function(){YAHOO.mediaplayer.loadPlayerScript()};

  var script = document.createElement('script');
  script.src = 'http://mediaplayer.yahoo.com/js';

  if(script.addEventListener){
    script.addEventListener("load", start, false);
  } else{
    script.onreadystatechange = function(){
      if(this.readyState=="complete"){
        start();
        script = null;
      }
    }
  }
  script.type="text/javascript";
  document.getElementsByTagName('head')[0].appendChild(script);

})();
 

YUI pie chart example

Wednesday, January 16th, 2008

If case you haven't noticed - YUI Charts hit the streets.

As with everything new, it's best shown and understood by example. So here's the simplest example of using a pie chart. Basically I took the example from the YUI page, changed all the paths to point to yahooapis.com (where YUI is hosted for free) and stripped everything that could be stripped (there's even no html or head tags, but turned out the body tag is required). The result is a short html with all dependencies satisfied.

OK, so here's the example, grab, paste, customize:
chart.html

 

Table to chart via JavaScript

Tuesday, January 8th, 2008

A javascript by Christian Heilmann that takes a table from your page and constructs the URL to request a chart from Google APIs. Slick and accessible. Check it out.

 

Netflix – how many movies you’ve rented

Sunday, December 30th, 2007

Netflix is a great service, it's a shame they don't seem to provide any APIs. Oh, well, we'll have to resort to other means of extracting data.

A little javascript to count how many movies you've rented:

document
    .getElementById('returned_movies')
    .getElementsByTagName('tbody')[0]
    .getElementsByTagName('tr')
    .length

You can put this code in a bookmarklet or simply type in the Firebug console.

As people say "your markup is your API".

 

Fancy formatting

Friday, December 21st, 2007

Writing readable code means proper indentation. Usually you'd tab (or use 2 or 4 or 3 spaces) after every curly bracket. Something like this:

if (true) {
    // indent
    if (false) {
        // another indent
        // and some more
    }
}

Same goes when you have a bigger hash/object sort of thing:

var memememe = {
    name: 'Stoyan',
    family_name: 'Stefanov',
    blog: 'http://www.phpied.com',
    kids_count: 2,
    books_count: 3,
    occupation: 'programmer'
}

Sometimes I find myself going a little fancy and aligning all the values in the name/value pairs:

var memememe = {
    name:        'Stoyan',
    family_name: 'Stefanov',
    blog:        'http://www.phpied.com',
    kids_count:  2,
    books_count: 3,
    occupation:  'programmer'
}

But recently, inspired by Firebug's Net panel way of presenting header information, I tried aligning the keys to the right in addition to aligning the values to the left. So I ended up with something like this:

var memememe = {
          name: 'Stoyan',
   family_name: 'Stefanov',
          blog: 'http://www.phpied.com',
    kids_count: 2,
   books_count: 3,
    occupation: 'programmer'
}

Fancy, eh? I liked the way it looks. But then I thought that when writing maintainable code, anything fancy suggests uncommon, uncommon suggests that other team members won't be using it, so it means breaking the rule #1 of writing maintainable code: be predictable. (this also happens to be rule #1 of other common activities, such as driving on the highway and designing usable web sites)

This type of formatting is also not easy to type in an editor, so it will require a little more effort. Those two drawbacks are enough, I believe, to dismiss this idea. But I can't help myself liking the way the code looks. Here's a piece of PHP, which looks even better than javascript, because even more characters are centered.

<?php
$memememe = array(
          'name' => 'Stoyan',
   'family_name' => 'Stefanov',
          'blog' => 'http://www.phpied.com',
    'kids_count' => 2,
   'books_count' => 3,
    'occupation' => 'programmer'
);
?>

Ain't that cool?

 

The Front-end Cerberus

Thursday, October 25th, 2007

Some smart guys picture the distinction of content (HTML), presentation (CSS) and behaviour (JavaScript) as a three-legged stool. This is totally fine, but can't we draw a more heroic picture of today's Front-end developer?

frontend-cerberus.jpg

I found the image here, if anyone knows the original author, let me know so I can give proper credit.

BTW, I never knew what Cerberus meant until 15 minutes ago. The thing is that where I come from, we used this name to refer to some of the teachers that weren't very nice to us at school :)

 

My performance article up on SitePoint

Thursday, October 25th, 2007

click

 

JS/PHP string concatenation mistype

Thursday, October 25th, 2007

Another one from the "this is not a syntax error" department.

The front-end developer is a strange beast who has to jiggle to and fro and code in several languages literally at the same time - javascript, html, css, php or some other server side language, some SQL dialect... No wonder that sometimes we make silly mistakes like:

var $myarray;
var array = array();
$myarray = [];
foreach(var i in myarray)

Last night I just did a silly mistake like this. In JavaScript I used the PHP way of concatenating strings. Something like:

var user = 'Stoyan';
alert('hello ' . user);

This is obviously wrong, but the thing is that it's not a syntax error as one might expect. It alerts "undefined". Why is that?

Well, 'hello' is a string object. You can call methods and properties on it, like:

>>> 'hello'.toUpperCase()
"HELLO"
>>> 'hello'.length
5

And spaces don't matter...

>>> 'hello'     .   length
5
>>> 'hello'  . toUpperCase()
"HELLO"

So 'hello' . user is an attempt to access the "user" property of the string object 'hello'. This property doesn't exist, hence the "undefined" result.

Doing the opposite (using JavaScript-type concatenation in PHP) is also not an error:

$user = 'Stoyan';
echo 'Hello ' + $user; // prints 0
 

“Save AnyThing” offline with a Google Gears bookmarklet

Friday, October 19th, 2007

Here's a little bookmarklet I came up with, I called "SAT", stands for "Save AnyThing (for offline reading)". It uses Google Gears and works like this:

  • you're about to go offline (maybe boarding a plane) and want to catch up on some reading
  • you visit any page
  • you click the SAT bookmaklet
  • it saves all the pages that are linked from the current page (only those on the same domain, you know, security and stuff)
  • you disconnect from the Net and read offline

Pretty cool, eh?

Initially I wanted to experiment with Google Gears and write a phpBB extension to save the latest forum topics for offline reading. But after thinking about it a bit I thought it can be done in an even more generic way and save anything for offline reading. All you need is a page that has a bunch of links, any page - a forum, a blog, the array section of the PHP manual... anything. After you run the bookmaklet, you'll have an offline copy of all the linked pages. Gotta love those bookmarklets, nothing to install, just a click in the favourites.

Demo

satdemo.png

» Click here for a recorded demo

(demo is kinda clumsy, my first attempt in screen recording, I used Adobe Captivate, pretty bad I couldn't upload the result to youtube)

For a live demo, you can also test the bookmarklet by simply clicking the download links below.

Download

Drag those two links to your favorites/bookmarklets. The first one is to store offline pages, the second is to remove the stored versions.

Source code

If you want to take a peek at the source code, the human-friendly versions are here:

Note on pages that will not be stored offline

Not all pages will be stored offline. I'm aware of these reasons why (there might be others)

  • same-origin security policy
  • Gears won't follow redirects

Comments, bugs

Thanks for reading, any comments are appreciated. My guess is the bug count will be pretty low, due to the fact that when the sh..., er, the bug hits the fan, you'll be offline and cannot report it :)

Now that I'm all set with stuff for offline reading for my short LAX-SJC flight tomorrow, I cannot help but wonder - should I still take with me a plain old reliable hardcopy of a piece of pulp fiction? You know... software breaks :)

 

IE has a problem with getElementsByName

Wednesday, October 3rd, 2007

Yes, it does.

Sometimes it's convenient to use "HTML arrays", meaning to name fields like:
<input name="something[]" />

Then on the server side you loop through the array $_POST['something']

This allows for a flexibility where your app doesn't know the number of inputs in advance, but works fine regardless of the actual number.

Even cooler is that you can generate fields on the client-side, with JavaScript.

The problem is if you want to do some sort of client-side validation after you've generated fields on the fly. If you have:

<input name="something[]" />
<input name="something[]" />
<input name="something[]" />

Then you can access the fields using

document.getElementsByName('something[]')

So in the case above

document.getElementsByName('something[]').length

will give you 3.

Then you add another fields, for example like:

var new_input = document.createElement('input');
new_input.type = 'text';
new_input.name = 'something[]';
document.body.appendChild(new_input);

Now if you try to count the fields with

document.getElementsByName('something[]').length

you'll get 4 in Firefox as you would expect, but still 3 in IE.

Bugs happen, c'est la vie :D

Here's a demo

Tested IE7 only, don't know if the bug exists in earlier versions.

My example was with an HTML array using []s in field names, but the issue remains if you have regular names without brackets, for example you have radio buttons or checkboxes and you want to create more choices dynamically with JavaScript.

 

Make your javascript a Windows .exe

Friday, August 31st, 2007

These days an average web developer needs to have a broad matrix of skills in order to do his/her job. HTML, CSS, JavaScript, AJAX, XML, some server side language, some linux skills, some apache, some database skills, standards, accessibility, SEO, the list goes on. Parts of the list are also multiplied by (or raised to the power of?) the number of browsers you want to support. Crazy, isn't it? We're such optimists trying to make stuff work in such an environment.

There's gotta be an easier way to write code that does something meaningful! Yes, there is, it's called JavaScript. You learn JavaScript, you learn it well, and you don't need to learn anything else. Isn't that cool? JavaScript is, practically, everywhere. Learn JavaScript and you can:

  • create rich and powerful web applications (the kind that runs inside the browser)
  • write server-side code such as ASP scripts or for example code that is run using Rhino (A JavaScript engine written in Java)
  • create rich media applications (Flash, Flex) using ActionScript which is based on ECMAScript, which is JavaScript
  • write scripts that automate administrative tasks on your Windows desktop, using Windows Scripting Host
  • write extensions/plugins for a plethora of desktop application such as Firefox or Dreamweaver
  • create web applications that store information off-line on user's desktop, using Google Gears
  • create Yahoo!, or Mac, or dunno-what-type-of widgets
  • create Windows apps (those that end in .exe) and libraries (.dll)

I'm sure the list above is not even complete.

OK, it's a joke that with one programming skill only you'll be employed for life, but it's a fun thought anyway. Off to the main topic of the post.

JScript

This is Microsoft's version of JavaScript (yep, the thing that annoys us *sometimes* in IE) and can also be used to create server side pages (ASP, ASP.NET) or desktop applications. Apparently JScript is now called JScript.NET and can be compiled to create .exe files. Let's see how.

The compiler

The compiler (program that creates programs) is an exe file called jsc.exe (JScriptCompiler) and is part of the .NET framework. Good news is that you can use it without installing any MS IDE (whatever Visual Studio is called these days), free of charge. Even better, maybe it's already there, on your machine. I searched my completely normal Windows XP machine that doesn't have any special MS tools and was able to find two copies of the compiler! You can search for "jsc.exe" and in case you don't already have it, you can read how to get it here.

So once you find your jsc.exe (found one o' mine in C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727), then add this path to your environment path: Right-click My Computer - Advanced - Environment Variables - System Variables - Path - Edit

Now open command prompt (Start - Run - "cmd" - OK) and type "jsc"+ENTER. You should see a list of help options for the compiler. Cool!

First .exe (in years)

The last time I created an .exe file is probably yeeears ago, when I was this Visual Basic king, writing a desktop application that takes a directory of images and creates a web gallery (example)

OK, lets create a simple application.

cd ..
mkdir myapps
cd myapps

Create a file called hello.js with the following content:

var d = new Date();
var n = Math.random();
print('Hello, \\ntoday is ' + d + '\\nand this is random - ' + n);

Now let's compile!

C:\\myapps>jsc hello.js
Microsoft (R) JScript Compiler version 8.00.50727
for Microsoft (R) .NET Framework version 2.0.50727
Copyright (C) Microsoft Corporation 1996-2005. All rights reserved.

No error messages, so we'll take that as an OK ;) Let's check:

C:\\myapps>dir
 Volume in drive C has no label.
 Volume Serial Number is B96A-95DB

 Directory of C:\\myapps

08/31/2007  07:33 PM    <DIR>          .
08/31/2007  07:33 PM    <DIR>          ..
08/31/2007  07:34 PM             4,096 hello.exe
08/31/2007  07:33 PM               109 hello.js
               2 File(s)          4,205 bytes
               2 Dir(s)  40,287,092,736 bytes free

YES! An .exe was created! Without further ado, let's run it already!

C:\\myapps>hello
Hello,
today is Fri Aug 31 19:34:32 PDT 2007
and this is random - 0.5855108083158316

That's so cool, the compiled program works!

Making a DLL

Now, we're convinced that we have a good thing going here, so let's create a DLL, meaning create a library that other applications can use.

JScript.NET has the notion of namespaces and packages (which we usually fake on the web) and class-based objects (eww! well, it supports the prototype stuff as well). So if we simply wrap our code in a package and a class and we create a new file LibHello.js:

package LibHello {
    class Hello {
        function say() {
            var d = new Date();
            var n = Math.random();
            return 'Hello, \\ntoday is ' + d + '\\nand this is random - ' + n;
        }
    }
}

Let's compile this into a library, we need the /t:library option when compiling

C:\\myapps>jsc /t:library LibHello.js

This creates hello.dll and we have a library!

Consuming the lib

Finally, let's create an app that leverages the new library we just created.

Create consumer.js with the following:

import LibHello;
var h = new LibHello.Hello();
print(h.say());

Compile and run:

C:\\myapps>jsc consumer.js

C:\\myapps>consumer.exe
Hello,
today is Fri Aug 31 19:53:29 PDT 2007
and this is random - 0.45013379838789525

Nice and easy.

So what?

I didn't have time to experiment, but I'm pretty sure you can take tools such as jsmin or jslint and easily compile them into libraries that can be consumed from windows apps, or VBA scripts in Access, Powerpoint, etc. Imagine you're writing some documentation in Word, you select some JS code you just wrote and JSlint it. That would be nice.

BTW, remember how we used /t:library option to produce a .dll and not an .exe? Well, there's also the option /t:winexe which creates a windows application I mean with the window and everything and not a console app. OK, let's give it a shot, create win.js with the following:

import System.Windows.Forms; // this has a MessageBox class
import LibHello;

var h = new LibHello.Hello();
MessageBox.Show(
        h.say(),
        "Dude!",
        MessageBoxButtons.OK,
        MessageBoxIcon.Exclamation
);

Compile:

C:\\myapps>jsc /t:winexe win.js

Double click in windows explorer and you have a nice little unquestionably useful Windows application ;)

 

JavaScript: Calling the unnamed

Thursday, August 16th, 2007

How does this look like?

o[""]()

A bit like a syntax error, eh? Or an emoticon of some sort. Well, it's valid JavaScript code.

JavaScript objects can have an empty string as a name for a property/method. So if you create an unnamed method called "", you can't call it using the dot notation, but you can, using the brackets.

So type this in your Firebug console:

var o = {
  "": function(){
        console.log('here')
      }
}

then call the unnamed like o[""]()

It works!

Reminds me of the Wizard of Earthsea a little bit where you had to know the true names of things in order to do the wizardry.

 

Order of execution of event listeners

Tuesday, August 14th, 2007

Say you attach several listeners to an event, for example you want a few things to happen on page load. What is the order of execution of the different listeners? You'd think that the listener attached first will execute first, followed by the second and so on... Well, yes, in FF, Opera, Safari on Windows, but not in IE.

The test

  var i = 1, ol = document.getElementById('result');

  for (i; i <= 10; i++) {
      YAHOO.util.Event.addListener(window,'load',
        function(num){
            return function(){
                ol.innerHTML += '<li>' + num + '</li>';
            }
        }(i)
      );
  }

Result in FF, O, Safari

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10

Result in IE

  1. 1
  2. 2
  3. 4
  4. 6
  5. 8
  6. 10
  7. 9
  8. 7
  9. 5
  10. 3

Observation

Here you can try it out yourself

Try in IE. Reload. Reload again. Notice something? The order is not random. Always starts 1, 2, then goes through all even numbers until 10 then backwards - 9, 7, 5, 3 - all odd numbers.

Try with a bigger loop - still the same thing. Hmm, interesting... Maybe not something you'd like to rely on, but still...

 

JavaScript patterns (dot com)

Sunday, July 22nd, 2007

I had this idea of collecting better javascript practices and ... well, patterns. The thing is that there is so much bad javascript floating around from the time of "copy/paste this to make your status bar go crazy", so there should be more of the good JS kind to replace the bad one. I liked the idea so much, I even bought the domain jspatterns.com. Until recently I haven't done much, but now there's a list of patterns I wanted to talk about and also some of them already have articles. Check it out.

Today I just added "Load-time branching" and "Singleton2", enjoy responsibly :) And yeah, it's a Wiki, I'm still debating on the best way to approach this. The Wiki is open to anyone, so if you have something to add/change - be my guest.

 

A JavaScript implementation of the Decorator pattern

Tuesday, July 17th, 2007

For background information on the decorator pattern check the Wikipedia article or for PHP implementations, here and here.

Motivation and example use

Let's take an example - you've created a class and released to the world. You want people to be able to easily build upon it and also release to the world. Some other folks can take your base class and selectively choose from the extensions only those that make sense for them. The example I choose is a class that does something on some text, beautifies it. Makes sure that there's always a space after the dots in a sentence and the dashes are also surrounded by spaces and so on. A Yahoo developer might want to add a feature (a decorator) that also adds an exclamation after the word Yahoo. A Spanish-speaking developer might add a feature where the exclamation sentences have the flipped exclamation mark before them. ¡Hola! Some people might add other language-specific or business-specific functionality. At the end, a new user of the class should be able to easily use the available features he likes, and leave out the rest. Let's see how the "client" code might look like:


// create an instance of the base class
// and initialize with some text
var t = new TextBeautifier.Core('Some text.And some more.Yeah, baby,yeah-Yahoo');
// get a decorator
t = t.getDecorator('Punctuation');
// get another one
t = t.getDecorator('FixDashes');
// another one
t = t.getDecorator('Yodel');
// call the method that will give
//  a beautified result
t.get(); // "Some text. And some more. Yeah, baby, yeah - Yahoo!"
// change the input text
t.set('bla-bla-blah!Huh?');
// beautify again
t.get();

The idea is that no matter how many or how little decorators you add, the basic functionality (setting text input, getting beautified output) remains the same. Later you might want to add new decorators or remove some, but the method calls to get/set are still unchanged.

Implementation

So let's see how this could be implemented in JavaScript. We have a base (core) class that provides functionality, in this simple case just setting and getting a text. In addition to that, the core class has a method getDecorator() that is used to add new decorating functionality.

// just some namespeces
TextBeautifier = {};
TextBeautifier.Decorator = {};

// constructor of the base class
TextBeautifier.Core = function (text){
  // store the text
  TextBeautifier.Core.prototype.text = text;
  // the basic get method
  TextBeautifier.Core.prototype.get = function(){
    return this.text; // might as well be TextBeautifier.Core.prototype.text
  };
  // the set [new text] method
  TextBeautifier.Core.prototype.set = function(t){
    TextBeautifier.Core.prototype.text = t;
  }
  // method that handles the decoration stuff
  // this method accepts the name of the decorator
  TextBeautifier.Core.prototype.getDecorator = function(deco){
    // get the longer name of the decorator class constructor
    var child = TextBeautifier.Decorator[deco];
    // the decorator extends (inherits from)
    //    the parent class
    child.prototype = this;
    // return an instance of the new decorator
    return new child;
  }
}

The method getDecorator() is the most interesting since it contains the decoration logic. So what happens when we have an instance of the Core class, called t and we say:

t = t.getDecorator('Punctuation');

The getDecorator does the following:

  1. Figures out the name of the constructor of the new decorator, in this case it's TextBeautifier.Decorator.Punctuation
  2. Makes the Punctuation class inherit the Core by setting the Puncuation prototype to point to this which is an instance of the Core class
  3. Creates a Punctuation instance and returns it

So after the line:

t = t.getDecorator('Punctuation');

now t is an instance of the Punctuation class which also has everything its parent Core class had.

Next, we add another decorator;

t = t.getDecorator('FixDashes');

Now t becomes an instance of the FixDashes class which inherits Punctuation, which in turn inherits Core. And so on. At first this might look like a normal inheritance chain but the difference is that the order of inheritance doesn't matter and can be changed at any time. The other beauty of this implementation of the decorator pattern is how simple the decorators are to implement. Inheritance is already taken care of by the Core class. A decorator simply implements a get() method that takes the output of the previous decorator and further beautifies is. Like:

// implementing a decorator
TextBeautifier.Decorator.Punctuation = function(){
  // defining own get() method
  this.get = function(){
    // first get whatever was done so far by the previous 
    // class in the chain
    var text = TextBeautifier.Decorator.Punctuation.prototype.get();
    //  then do your work and return
    return text.replace(/([\.,!])\s*/g, '$1 ');
  }
}

Now let's implement another decorator

TextBeautifier.Decorator.FixDashes = function(){
  this.get = function(){
    var text = TextBeautifier.Decorator.FixDashes.prototype.get();
    return text.replace(/\s*-\s*/g, ' - ');
  }
}

Same thing - get what your parent does and further decorate the output. In this case the parent is Punctuation. Another decorator, same thing:

TextBeautifier.Decorator.Yodel = function(){
  this.get = function(){
    var text = TextBeautifier.Decorator.Yodel.prototype.get();
    return text.replace(/Yahoo/g, 'Yahoo!');
  }
}

C'est tout

Simple and elegant. Add decorators at will and combine to taste. This example used only one "working" method - get() - but the pattern doesn't force you to extend only one method, you can have as many methods that do something and can be further extended by decorators.

In this implementation you can also get the original raw text at any time using TextBeautifier.Core.prototype.get(), even in cases where you set() it further in the chain, this is because all decorators defined their own get(), they didn't mess up with the original prototype's get().

A Christmas tree

I couldn't help but add another illustration. Say you have a class ChristmasTree and decorators Angel, HugeStar, BlueBalls, RedBalls, GoldenBalls, SilverBalls, SilverDust, GoldenGarlands, LightBulbs... you get the point. Then you go:

var tree2008 = new ChristmasTree();
tree2008.getDecorator('Angel');
tree2008.getDecorator('RedBalls');
tree2008.getDecorator('SilverDust');
tree2008.getDecorator('BlueBalls');
tree2008.gimme();

or

var tree_at_myparents = new ChristmasTree();
tree_at_myparents.getDecorator('HugeStar');
tree_at_myparents.getDecorator('GoldenBalls');
tree_at_myparents.getDecorator('GoldenGarlands');
tree_at_myparents.getDecorator('LightBulbs');
tree_at_myparents.gimme();
 

PHP/Javascript dev tools for TextPad

Monday, July 16th, 2007

Here are some convenient tools I've added to my TextPad editor, hope you'll like 'em.

TextPad tools

Stuff can easily be added to TextPad's Tools menu, like I did, shown on the screenshot.
textpad-tools.png

In order to do so, you go Configure -> Preferences. Then select Tools on the tree to the left, then Add. You can add a DOS command, an application or a help file (.hlp or .chm)

The first three tools on the picture above come out-of-the-box, the other 4 I've added myself. Let me show you what I did.

Tool #1 - PHP lint (a.k.a. syntax check)

So I'm editing a PHP file and I want to syntax check it from the editor. Good. PHP on the command line comes with the -l option (this is lowercase L) which does just that. For example if you run this from your command prompt, it will check the file test.php for syntax errors:
C:\php> php -l test.php

So for tool #1 I just did - Configure-Preferences-Tools-Add-DOS command, then typed:
php –l $File

In Textpad apparently $File refers to the current file being edited. So now I can edit a file, press CTRL+4 and syntax check the file. Neat.

Tool #2 - PHP help

This is exactly the same idea as the previous tool. I use PHP command line option --rf which gives you help information. For example try getting help with the str_replace() function:

C:\php>php --rf str_replace

The result is

Function [ <internal> public function str_replace ] {

  - Parameters [4] {
    Parameter #0 [ <required> $search ]
    Parameter #1 [ <required> $replace ]
    Parameter #2 [ <required> $subject ]
    Parameter #3 [ <optional> &$replace_count ]
  }
}

Adding this functionality to textpad is very similar to tool #1, only this time the command is:

php --rf $SelWord

$SelWord is the currently selected word in textpad (just placing the cursor somewhere in the word is enough)

Tool #3 - PHP Manual

Sometimes the help above is not enough and you want to hit the manual on php.net. Here's the next tool. You go:
Configure-Preferences-Tools-Add-Program and you find your firefox.exe, like
C:\Program Files\Mozilla Firefox\firefox.exe

Now, in order to edit a tool you created in TextPad, you need to expand the Tools node of the Preferences tree and click the tool you need, as shown on the screenshot:

In this screen, you need to type this in the Parameters field:

http://php.net/$SelWord

Tool #4 - JS Lint

JSLint is a tool for checking JavaScript code, it also can be run on the command line in Windows, using cscript. So if your jslint.js is in C:\, you can have tool #4 another DOS command:

cscript C:\jslint.js <$File

Hope you like it

Or maybe add these simple tools to your text editor of choice.

Last thing

One little thing I didn't mention - it's a bit of a challenge to figure out how to rename a tool once created. Basically on the list of tools (next to the Add button), just click, right click, double-click or simultaneously click left and right mouse buttons. One of these will work eventually :)

 

On a publishing diet

Thursday, July 12th, 2007

So I launched this little tool csssprites.com that allows you to upload images and create one CSS sprite image, plus it gives the background-position CSS definitions to use in order to show parts of the sprite. People have been trying it out, but unfortunately sometimes uploading 20 megs of images to create a sprite, which is not the point of the css sprites technique. Anyway as a result I exceeded the disk quota my host gives me and since the site is hosted on the same server as this blog, the blog stopped working. Hence the publishing diet.

Initially I blamed WordPress because it started acting strangely, asking me to update my database, saying that I don't have admin privileges, then not loading the CSS files and finally just stopped working even on the front end. I said oh well, I need to upgrade it anyway, so let's do it now. Just trying to FTP a single file got me the message that I can't copy so I finally figured out the real case - the exceeded disk space.

It's all good now, I just deleted all CSS sprite images that were generated, I was planning to do a cron job do delete the ones older than a day or two anyway, but never got around doing it. I should just check and warn the cssspritres.com users not to upload huge images, because this is not how CSS sprites were designed to work anyway.

Long story, short message. I'm off the publishing diet now.

Meanwhile I wrote an article for the International PHP Magazine, it's an intro to unit tetsing with PHPT, called "PHPT - Unit testing for the rest of us". Nice, eh? Just got an email today that the new IPM issue is out the door, you can check the TOC here. I wanted to further experiment with PHPT and was thinking of writing this test generation tool. Say you have a bunch of classes, you run the tool and it generates PHPT test stubs, based on the classes and methods in finds. Then you tweak the generated stubs here and there to implement the actual tests. PHPUnit has this feature, so why not PHPT as well. We'll see if I'll find the time.

On a different technology, I was playing around implementing the decorator pattern in Javascript, will post about it later (sneak peek).

On a different subject, just added a few very simple tools to my favorite Textpad, I found them helpful for PHP development, will post about them later.

On a yet another subject a few days ago I finished a draft outline for my new book-to-be and we started discussing with the editor.

On a totally unrelated subject, I did a new phpBB theme (copy of the default subSilver) following as many of the Yahoo front end performance rules as I found applicable. Naturally, I'll post about it later.

Otherwise life has been good. I moved with my family to LA to start working for this company called Yahoo!. Work is great, LA was bit of a surprise and not very welcoming, but hey, it's the experience. We had some initial rental issues (we lost quite a bunch of money double and some point triple renting), then there was the stress of the whole move, having to start everything over again, driving licenses, shocking 20% APR rates from Toyota, credit cards refusals and stuff (the only thing that I still use here from Canada is the Costco card!). Yahoo did help a lot during the moving process, can't imagine what would have been without all the little and not so little perks I got during the relocation. So anyway, after all that initial shock, the family is starting to settle. The kids just loooove Disneyland, we ended up getting anual passes for California residents, so we'll be seeing it a lot. Also the beach is quite nice, not the cleanest mind you, but it's probably because we're new don't know where to go, we just hit the closest to us, in Venice. By the way, Venice is amazingly similar to some little Bulgarian towns on the Black Sea. In general LA is quite expensive, especially the Santa Monica area, where the office is, but I wanted to be close to the family in those times of change, so we ended up renting a place only 5 miles from the office (still getting used to those miles and pounds). I proudly bike to work now, doing my share in saving the environment. Half an hour in each direction, it's a nice excersise. Talking about biking, here's some biking-to-work wisdom for you:

Tree branches are hanging lower than they appear.

also

Just when you thought you learned how to bike without using your hands and to light a cigarette meanwhile... you didn't.

 

CSS Sprites generation tool

Wednesday, June 27th, 2007

Here's my last weekend's project - a web-based tool to generate images for CSS sprites: http://www.csssprites.com. Cool domain name, eh? I couldn't believe it was not taken.

CSS Spr...what?

This is a simple technique used for page load performance purposes. Since HTTP requests are the most expensive thing you can do in regards to front-end performance, you strive for making as little requests as possible. So instead of having the browser download 4 rounded corner images for example, you create one image that has all four. Then you use CSS' background-position to only show the part of the image you want. More on the subject in this ALA article

How does the tool work

You upload as many images as you want and the tool generates a mosaic of all images, gives you the result as PNG and gives you the coordinates you need to use in the background-position declaration. The tool also gives you an html page as an example, so you can save both the PNG and the html page for reference.

Image size

If you properly optimize the big image, you might actually have smaller size than all the individual images combined. In my tool, the PNG image generated is not optimized at all, I leave this to you to use PNGOUT or any other tool you know. Also you can convert the PNG into GIF if that's better for your purposes.

Implementation - PHP

The PHP code is fairly simple. The actual spriting (is that a word?) class takes a list of images and calls getimagesize() on each one to get the dimensions. The image with the biggest height is used as distance between images. The rest is just composing the imagemagick command that will to the work. Here's the important method:

<?php
function combine() {
    if ($this->distance === false) {
        $distance = $this->_biggest;
    } else {
        $distance = (int)$this->distance;
    }

    if ($this->output_dir === false) {
        $output_dir = $this->_dir;
    } else {
        $output_dir = $this->output_dir;
    }

    $half = ceil($distance / 2);

    $coord = array();
    $y = 0;

    foreach ($this->images as $i=>$data) {
        $this->images[$i]['x'] = $half;
        $this->images[$i]['y'] = $half + $y;
        $coord[] = '-page +0+' . $y . ' ' . $i;
        $y += $data[1] + $distance;
    }

    $cmd = 'convert ' . implode(' ', $coord)
         . ' -background none -mosaic -bordercolor none -border '
         . $half . 'x' . $half
         . ' ' . $output_dir . '/result.' . $this->output_format;
    system($cmd, $ret);

    return $ret === 0;

}
?>

Implementation - JS

In the spirit of web2 I couldn't afford a complete page reload :lol: although it would've been much simpler. I just had to get fancy. YUI to the rescue. On page load I set up the form for async request, using YAHOO.util.Connection. In case of file uploads YUI generates an iframe behind the scenes and uploads to the iframe. Then it takes whatever is in the body on the iframe and gives it to you instead of the XMLHttpRequest's responseText property.

So the files are uploaded to upload.php which calls the class that has the method mentioned above then loops through the $images property of the said class and writes the example html file as well as prints out a JSON string with the same image information.

YUI's Connection calls my callback function and I get the invaluable responseText. Parsing the JSON with the json.js, I get a JS object. Looping through it and DOM-generating a table of results is the semi-last step. The last is (we're fancy, remember?) to yellow-fade the background color of the result, using YAHOO.util.Animation.

BTW, I got fancy once again and combined and minified json.js with my JS file, so that there is one less request and a side effect impossible to read. The unminified version of the JS that does all the work is here for reference.

Comments

I hope this tool cane be useful for quickly generating those sprites, if only for prototyping purposes. Any comments, requests, bug reports are all very welcome.

And how do you like the version of the tool? Anyone n00b can do "beta", it takes a true h@x0r (or something) to do a better job :D

Ah, yeah, and the page badly needs a stylesheet, do you want to help?

 

Weird attempt for flicker-free rendering solution

Saturday, June 23rd, 2007

Flicker-free rendering - not allowing the user to see the page loading progress or how the different elements get downloaded and put at their right place. Extra points if the URL in the address bar doesn't change. Why not use AJAX to only change part of the page? Well, don't look at me, but sometimes flicker-free loading of the whole page might be a requirement.

One solution is the frame swapping technique. You have two frames, one has 0% height, the other one 100%. All links target the invisible frame and the new page is loaded there. Once the new page is loaded, it swaps and becomes 100% while the other one that has the old page becomes 0%. And so on. But... frames?

I played around with a different approach. Page A (old page) receives the new URL (of page B) to be loaded. A uses javascript to create a new XML document reading the content of B, then replaces the A nodes with the B nodes.

The solution is nowhere perfect, nor advisable to use, but hey, it's possible.

In for this whole thing to work, all pages should not only be valid XHTML (therefore XML) but also served with the correct content type header:
Content-Type: application/xhtml+xml

Because this whole experiment was done in the Firebug console, I needed to find a site that serves with this header. First one I found was http://www.smackthemouse.com/, so that's why I'm using it as an example. Let's say you're somewhere in the site and lick a Home link. The home URL http://www.smackthemouse.com/ is requested and when it arrives, the current HEAD and the BODY nodes are removed and the HEAD and BODY nodes of the new XML document are added. Finally the document.title is updated. Don't ask why I did it this way exactly, I tried other things like using replaceChild instead of removeChild + appendChild, I tried replacing the HTML node, I even tried innerHTML. Nothing seemed to work in both IE and FF without messing things up, like not applying styles and so on.

So here's what I came up with for Firefox.

var x = document.implementation.createDocument('','',null);
x.load('http://www.smackthemouse.com/');
x.onload = function() {
  var root = document.getElementsByTagName('html')[0];
  root.removeChild(document.getElementsByTagName('head')[0]);
  root.appendChild(x.getElementsByTagName('head')[0]);
  root.removeChild(document.getElementsByTagName('body')[0]);
  root.appendChild(x.getElementsByTagName('body')[0]);
  document.title = x.title;
}

For IE, since it can't createDocument, you need the XMLDOM activeX

var x = new ActiveXObject("Microsoft.XMLDOM");
x.onreadystatechange = function () {
  if (x.readyState == 4) {
    // do the swapping
  }
};
x.load("http://www.smackthemouse.com/");

So that's all folks, nothing special, just shared headaches and ideas :D

Another idea - as shown in a previous post, the browsers (except Opera) won't show anything until the last bit of CSS is downloaded. So if you put you big-ass CSS file at the bottom of the page (which is exactly the opposite of what you should be doing when aiming at performance) you might also achieve the desired flicker-free rendering.

 

YUI anywhere (bookmarklet)

Friday, June 8th, 2007

Hooked on YUI? You can now take it anywhere you go. The thing is Yahoo hosts the libraries publicly, so they are available at any time. Let's say you visit a page and you want to do something with it. Comes the YUI bookmaklet that adds a new script tag to the page pointing to utilities.js that contains all YUI utilities (DOM, Event, DragDrop, Animation, Connection...). Then just open your Firebug console and start messing with the page. The powerful toolset that YUI is, is at your disposal.

The code

The code for the bookmarklet is really simple, just a question of adding a new script tag.

(function(){
    var s = document.createElement('script');
    s.src='http://yui.yahooapis.com/2.2.2/build/utilities/utilities.js';
    document.getElementsByTagName('head')[0].appendChild(s);
})()

Install

Right-click, add to favorites, or drag to bookmarks.

YUI anywhere

Let the fun begin

So you go to any page, click the bookmarklet and for example decide to make the logo on the page draggable. All you need to write in your JS console (or in address bar for IE) is:

new YAHOO.util.DD('logo')

The result is really not bad for a one-liner. But why stop there? Let's make everything on a page draggable.

var all = document.getElementsByTagName('*');for(var i = 0; i < all.length; i++) {new YAHOO.util.DD(all[i])}

Whoa! We can mess up with other people's pages like we've never messed up with other people's pages before! Example:

google-yui-drag.png

And why only other people's pages, what about ours? Imagine you're sitting with a client or boss, showing the new site and they go:
- Hmm, well, you know, I don't know about this spacing between the images here ...
And you:
- Hold that thought (clicking bookmarklet, making everything draggable, dragging the offending image). Is this how you prefer it?
Client:
- Well, maybe, or actually it was better before.
You:
- Ah, OK, whatever suits your business needs

Update: Some more one-liners to prevent loading a new page when you attempt to drag a link or a submit button.

Disable links:
YAHOO.util.Event.addListener(window, 'click', function(e){ if (e.target.nodeName.toUpperCase() == 'A'){ YAHOO.util.Event.preventDefault(e) } });

Prompt before unload:
YAHOO.util.Event.addListener(window,'beforeunload',function(e){ e.returnValue = "Sure?" });

 

YUI good for your performance

Sunday, April 1st, 2007

One of the cons of using any of the good and popular third-party JS libraries is the file size of the .js files associated with them. A lot has been done by the library developers to address this issue - providing minified versions, segregating the script files based on what they do and using loading-on-demand, among others. There's more good news - Yahoo is now offering hosting of their YUI library files.

How is it good for you?
- less on the bandwidth bill - you don't need to worry about hosting these files yourself
- high availability - well Yahoo is behind this, so you can rest assured that these files are delivered to your surfer
- small downloads - Yahoo hosts the minified versions and gzips them
- files already cached!

The last one is the topic I had in mind with the this blog's title. Since Yahoo will be using the same locations for the libraries you need and since Yahoo is the most popular site, chances are your visitors have already checked their Y! mail or their Y! finance page and searched or done anything on the Yahoo network of sites. This means they have already requested and (hopefully) cached these .js files. And as proved before, lowering the number of HTTP requests is top 1 performance optimization you can ever do.

Even better is that you can include only libraries you need (less transfer) or you can include several in a batch (less requests).

If you only need Event and DOM, include only yahoo-dom-event.js (8K)
If you need only Event, DOM, AJAX, include yahoo-dom-event.js and connection-min.js (8K + 4K)
If you also need more like Drag and Drop and animation and AJAX, why not include all utility classes at once, utilities.js. After all this file is 22K, probably less than two images that will appear on your page.

Note: all filesizes above are when files are gzipped, which is how Yahoo serves them.

So bottom line, your visitors hit your site and, lo and behold, they have all the JS already cached, resulting in your page loading as fast as a rocket ;) Sweet.

 

Two bookmarklets for debugging in IE

Tuesday, February 20th, 2007

Here are two bookmarklets that could make your life easier when trying to figure out why in IE a page behave as wrong as it behaves. For Firefox we have Firebug, so none of this is necessary. For IE we have also Firebug lite (see my post), but you need some setup before you can use it. With this thing here you can mess up any page you see on the web, not only yours :)

Bookmarklet 1 - Eval() textarea

I saw this bookmarklet here and it's beautiful. When you start it, it puts a textarea at the bottom of your page and you can type javascript in it, then eval()-uate it. Perfect! Only ... it doesn't work in frames. So I did the same thing but when you have frames (works without frames as well). The way mine works is - you first select some text in a frame, then you click the bookmarklet. A new textarea, ready to execute javascript will be placed in this frame (or iframe) that you selected. Also in this case when you type document.something, it refers to the document in the frame, not the frameset.
If you don't select any text and click the bookmarklet, it will place the textarea in the topmost document, so it will work for frame-free pages as well.

So here's the bookmarklet.

textarea eval

And here's a page where you can test.

Bookmarklet 2 - dump anything

After having my beautiful textarea, I wanted to be able to dump variables, like print_r() or var_dump() but for Javascript. I googled and I found this little script. All I did then is to make it a bookmarklet. How it works? You select the bookmarklet, it gives you a prompt, where you type whatever you want to dump, like document.location for example. Then it shows you an alert with all properties of this thing you typed. (Don't try to dump document though, or something else that recurses, because the script won't handle the recursion and will freeze).

Install it from here:

dump var

While this second bookmarklet will most likely work in FF as well, you don't need it, you have firebug!