Archive for the 'JavaScript' Category

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!

 

JSON data island

Monday, February 12th, 2007

Here's a hacky thing, I called it JSON data island, referencing the XML data island, that MS came up with for their IE browser. The idea is to use a comment in your HTML that holds some data in JSON format. Then using DOM, you access the comment, eval()-uate it and there you go - you have the data as a Javascript object.

» Here's a little proof of concept.

What we have in the HTML is:

<div id="some-div"><!–
    {prop:'value', prop2:['value1','value2','value3']}
–>
    Some stuff in div
</div>

Then the JavaScript that will process the JSON data in the comment:

var island = document.getElementById('some-div').firstChild;
var the_data = eval('(' + island.data + ')');
alert(the_data.prop2);

In this case I added the comment island as the firstChild of some div, but it could be anywhere, as long as you know how to access it with DOM methods.

Q&A

OK, why?
Well, I need some data that comes from the database, so it needs some server-side processing before it appears in the JavaScript code.

Can't it simply be in an inline <script>?
Inline scripts are bad, they mix behaviour (JS) with content (HTML). Also, imagine how your client runs some SEO tool that reports inline scripts in the content, the client goes: "You used an inline script!? Gimme my money back this instant!"

In your external .js then?
I'd like to have external .js simple, with no server-side scripting involved. I don't want to make my beautiful, big, long script appear as something like script.js.php

One small additional little .js.php only for the data you need?
Look, I said "no"! .js.php will require a new database connection, since it's a seperate request. Performance will suffer. In addition it's a new HTTP request and those are expensive.

An AJAX request fired from your beloved clean external .js that will only return the JSON data you hacked into your island thing of yours?
Same as above. New HTTP request, new DB connection.

OK then, I give up!
It was about time!

Thanks: to Phil Renaud for the inspirational idea of making conversational posts, his are hysterical.

 

Check/toggle 'em all

Saturday, February 10th, 2007

Recently I decided to clean up all the spam from an abandoned phpBB forum of mine, there was a lot to delete. In the phpBB version that I use there is no option to "check all" topics you want to moderate. So I came up with a little bookmarklet to do this for me. Here it is, only improved to work within frames and toggle (check if not checked, uncheck otherwise) all checkboxes in sight. Tested in FF and IE6/7.

To install, add this to your bookmarks/favourites:

Toggle 'em all

Here are
some checkboxes for your testing pleasure.

The reading friendly code:

javascript:(function(){
function checkFrames(w) {

  try {
    var inputs = w.document.getElementsByTagName('input');
    for (var i = 0; i < inputs.length; i++) {
      if (inputs[i].type && inputs[i].type == 'checkbox'){
        inputs[i].checked = !inputs[i].checked;
      }
    }
  } catch (e){}
  if(w.frames && w.frames.length>0){

    for(var i = 0; i < w.frames.length;i++){
      var fr = w.frames[i];
      checkFrames(fr);
    }
  }
}
checkFrames(window);
})()
 

AJAX-MCV in Russian

Monday, January 29th, 2007

Boris of http://www.ajaxplanet.ru/ has published a translation of my article on the little AJAX/MVC framework I came up with, this is trully flattering, thanks a lot!

If you speak Russian check the post here.

The translation is by Gennady Potapov, sposibo Gennady!

 

Dynamic SCRIPT and STYLE elements in IE

Friday, January 26th, 2007

So you know how to add external scripts and styles, using the DOM, after the page is loaded. And what if you don't have external files, but have some style definitions and some JS code as text and you want it inserted and executed into a page.

The DOM way

"Ha! An easy one", you'd say and then go like:

var ss = document.createElement('script');
var scr = 'alert("bah");';
var tt = document.createTextNode(scr);
ss.appendChild(tt);
var hh = document.getElementsByTagName('head')[0];
hh.appendChild(ss);

"Ha!" in turn says IE, "No way!"

The IE way for SCRIPT

The above won't work in IE, but you can use the text property instead of creating a text node. Interestingly enough, this also works in Firefox.

var ss = document.createElement('script');
var scr = 'alert("bah");';
ss.text = scr;
var hh = document.getElementsByTagName('head')[0];
hh.appendChild(ss);

The IE way for STYLE

STYLE, SCRIPT, what's the difference, they are merely elements of the DOM tree. For the normal browsers, yes, so creating a text node with the stylesheet body will work in Firefox. For IE, you need a workaround.

var ss1 = document.createElement('style');
var def = 'body {color: red;}';
ss1.setAttribute("type", "text/css");
ss1.styleSheet.cssText = def;
var hh1 = document.getElementsByTagName('head')[0];
hh1.appendChild(ss1);

Note that while in the SCRIPT case I took the liberty of skipping the type attribute, it's absolutely required here.

So with a bit of object sniffing, we can get a cross-browser solution:

var ss1 = document.createElement('style');
var def = 'body {color: red;}';
ss1.setAttribute("type", "text/css");
if (ss1.styleSheet) {   // IE
    ss1.styleSheet.cssText = def;
} else {                // the world
    var tt1 = document.createTextNode(def);
    ss1.appendChild(tt1);
}
var hh1 = document.getElementsByTagName('head')[0];
hh1.appendChild(ss1);
 

User stylesheet in IE

Saturday, January 20th, 2007

Let's say you want to quickly try out some small stylesheet changes, but you don't want to (or prefer not to, or for some reason temporarily you just can't) modify your application's CSS file(s). In FF it's easy - you have Firebug and you can play with styles until blue in the face. And in case you do get blue in the face and start making so many changes that you get lost, you can create a new clean and tidy CSS file, place it on your hard drive and use Web Developer extension to load it (Menu CSS->Add User Style Sheet). With WebDev Extension you can also Edit CSS right there, although unfortunatelly it's not always working when you have frames.

OK, there are options for Firefox. But how about IE?

In IE you have IE Developer Toolbar, definitelly helpful, but you can only modify element styles, not the stylesheet rules. So? A tiny little bookmarklet to the rescue!

My bookmarklet assumes you have a file called C:\user.css and loads this stylesheet on demand in your page, in every frame, just in case you use frames. Simple, yet useful, I hope. Here's the (readable) code:

javascript:
var css_file = prompt('Which CSS file you want to load today?','c:/user.css');
function addcss(w) {
    var html_doc = w.document.getElementsByTagName('head')[0];
    var css = w.document.createElement('link');
    css.setAttribute('rel', 'stylesheet');
    css.setAttribute('type', 'text/css');
    css.setAttribute('href', css_file);
    html_doc.appendChild(css);
}
var errors = 0;
function checkFrames(w) {
  if(w.frames && w.frames.length>0){
    for(var i=0;i<w.frames.length;i++){
      var fr=w.frames[i];
      try {
        addcss(fr);
      } catch (e) {
        errors++;
      }
      checkFrames(fr);
    }
  }
}
checkFrames(window);
addcss(window);
if (errors > 0) {
    alert('Could not access ' + errors + ' frame(s)');
}

To install and play around

Right-click this link and add it to your favourites:

Add User StyleSheet

Have in mind that this is IE-only (tested IE7). I don't think FF will allow you to access files on your local drive like this. But for FF you have the tools to do this anyway.

Another option to load local stylesheets in IE is to use the user CSS capability built in the browser, you can find it under Tools/Internet Options/Accessibility, but this will load your user CSS first (as opposed to last as the case is with my bookmarklet), so the "real" style definitions will overwrite yours, unless you always use !important and the "real" styles don't.

Thanks!

Have fun with the custom CSS and let me know how you find it.

 

Firebug console for IE

Wednesday, December 6th, 2006

Update: A better version of what I was trying to do is here. It works around the cross-domain permission problems in IE by not loading a page in the frame, but putting there the actual content.

Firebug - no words to describe how cool it is, really. After the recent new release (1.0. beta) the number of features is overwhelming. I for one can't live anymore without it, seriously.

One of the things I noticed on the website is the ability to use the Firebug console in other browsers than Firefox. I don't know if this existed before version 1.0 but if it did, it was the best kept secret. I am so addicted to the console in Firefox, I use it all the time to tweak a few things here and there when I'm working on a page. Recently I was looking for something similar for IE, but couldn't find it. Lo and behold, it was right under my nose.

So, here's the page that describes how to use Firebug in IE (and others). Basically you unzip the Firebug Lite files somewhere on your server and then you include firebug.js in your pages. But why stop there? And isn't it possible to avoid including this script on every page (and forgetting to remove once you're done, or removing it prematurelly since a page, just like a painting, is never really finished). Bookmarklets to the rescue!

I wanted to host the Firebug files on my hard-drive and then use a javascript dynamic include to load firebug.js via a bookmarklet. This way I would be able to load the firebug console every time I want it, on any page. Unfortunatelly IE's security policy won't allow it. Then?

Solution

The solution I came up with is:

  1. you copy the Firebug Lite files somewhere on your server
  2. you call a bookmarklet that will load firebug.js
  3. you hit F12 and you have a console!

This procedure has to be repeated for every domain you're working on, because of the security policy that won't allow cross-domain frame scripting. You can have one copy for your http://localhost and one for every domain. To ease the creation of bookmarklets that load firebug.js, I came up with a Firebug bookmarklet generator.

In action

  1. I copied Firebug Lite files (get the .zip) on this server (phpied.com), they are here.
  2. I (and you can try the same) generate a bookmarklet, using the bookmarklet tool
  3. Add the generated bookmarklet to the favorites
  4. Go to any page on phpied.com
  5. Click the new favorite
  6. Hit F12 to show/hide the console

Here's how (a readable version of) the generated code looks like:

javascript:(function(){
  var firebug_js = document.createElement('script');
  firebug_js.setAttribute('type', 'text/javascript');
  firebug_js.src = 'http%3A//www.phpied.com/files/firebug/firebug.js';
  document.getElementsByTagName('head')[0].appendChild(firebug_js);
  firebug_js.onreadystatechange = function () {
    if (firebug_js.readyState == 'complete') {
      console.open()
    }
  }
})()

Minor improvement to the console

The Firebug Lite console executes the code you type, but doesn't show it again when you use the up/down arrows, the way it does in Firefox. So I added this feature (copying from myself), you can replace the firebug.js you download with my version.

Not sold yet?

Here's a screenshot of the console in action, I used it to change my photo on the homepage.

firebug-ie-console.png

Go ahead, please

I strongly encourage everyone to try this out. Firebug is a beautiful thing and using even a bit of it in IE is great.

 

Y! homepage - CSS sprites in action

Friday, December 1st, 2006

Have you looked at the HTML markup of the new Yahoo homepage? Then you should. The markup (although it won't validate) is a piece of semantic art. Lists are lists, tabs are lists, only one table to be seen (obviously plugged-in coming from a different site)

The total number of markup elements on the page (document.getElementsByTagName(*).length) is 662, which is not bad for such a busy page. Compare that with Google search results page, which is semantically pretty much nothing but a just a list and uses 468 elements to display the content, among which there are 22 tables. Amazon's home page has the stunning 1469 elements.

Anyway, the thing that I saw and liked, was the use of the so-called CSS sprites (tut, tut, demo). It's a technique where instead of creating multiple images (10 little icons for example), you create one image file that has them all. Then you use CSS's background-position to only show the part of the big image that contains the icon you want. This may look like too much of a hassle, but when you think about the number of HTTP requests you save by getting one image instead of ten, then it starts making sense. Y! proves this point by using this technique on its homepage. Since the technique is used on what is probably one of the top visited pages on the web, I would considered it production-ready :)

You can get an idea of how Y! homepage works with its image assets by using Firefox's Web Developer extension: "Images -> View Image Information". In case you don't browse with Firefox packed with Web Developer extension (then you should!), then you can check the copy that I made - copy is here. Get a load of this guy for example.

Updated Dec 02, 2006:
Just deleted one comment by mistake (I got so much spam comments), pointing out that the correct syntax is:
document.getElementsByTagName("*").length
where * is quoted.
This is true, a typo on my part.

Thank you very much Philip, I'm so sorry I deleted your comment :(

 

JSON renderer for Text_Highlight

Friday, October 27th, 2006

Text_Highlighter is one of my favourite PEAR packages, seems like I'm addicted to highlighting source code. After adding BB code and simple HTML renderers and an ABAP code syntax definition, today I played with adding a JSON renderer. Useful in case you want to get highlighted source code in your new shiny AJAX app.

Array renderer

After I did the JSON renderer, I said, OK, what if I want to tweak the JSON output just a bit (or the output from any renderer for that matter)? Add more options? Nah, I had a better idea, I scrapped the whole thing and did an Array renderer first. If you have the array output from the renderer, it's trivial to format it as JSON, or XML, or HTML, or anything. I believe even the exisitng Text_Highlighter renderers should be rewritten, to extend an Array renderer. Anyway, back to JSON.

Demo

To see the JSON renderer in action, you can go to my hiliteme.com site and check the JSON tab.

Source

The source code is available here - JSON.phps which extends Array.phps. To test, you need to add the two renderers to your PEAR repository under Text/Highlighter/Renderer

Example

So let's say you need to highlight the PHP code

<?php
    echo "Hello Highlighted World!";
?>

You create an instance of Text_Highlighter and Text_Highlighter_Renderer_JSON and call the highlight() method, assuming that the code you need highlighted is in $source

<?php
// dependencies
require_once 'Text/Highlighter.php';
require_once 'Text/Highlighter/Renderer/JSON.php';

// instance
$json_options = array();
$json_options['tabsize'] = 4;
$json_options['enumerated'] = true;
$renderer =& new Text_Highlighter_Renderer_JSON($json_options);
$highlighter =& Text_Highlighter::factory($_POST['language']);
$highlighter->setRenderer($renderer);

// do the highlighting
$json_result = $highlighter->highlight($source);
?>

Now $json_result will look like:

[["inlinetags","&lt;?php"],["code"," \n    "],["reserved","echo"],["code"," "],["quotes","&quot;"],["string","Hello Highlighted World!"],["quotes","&quot;"],["code","; \n"],["inlinetags","?&gt;"]]

As you see the JSON output is an array, one element per highlighted keyword, and in this array there is a sub array - class/keyword. If you want to display this in your page (let's say you got it from an AJAX call), you can do a loop through the array and surround the keywords with span tags of the selected style:

// say your ajax call returned var src 
// var src = xmlhttp.responseText;
var data = eval(src); 

var res = '';
for (var i in data) {
    res += '<span class="hl-' + data[i][0] + '">';
    res += data[i][1];
    res += '</span>';
}

var el = document.getElementById('some-div').
el.innerHTML = '<pre>' + res + '</pre>';

Here I used innerHTML, you can also use DOM, but in this case you need a special case for the "\n" so that you can create a br element to address IE's habit of ignoring line feeds in a DOM-generated pre tag.

BTW, if you don't set the enumerated option to true, you'll get objects inside the main array, check hiliteme.com's JSON tab for an idea of how this would look like.

 

JS includes - the saga continues…

Wednesday, October 25th, 2006

The problem in question is how to find out a dynamically included JavaScript file is actually loaded. The concept of JavaScript includes is here, the IE-only solution is here. The IE solution is to use the onreadystatechange event that is fired when a new script is included. It also works for dynamically loaded CSS files using a new link DOM element. Thanks to the comment from Björn Graf, I tried using onload event to test if the new script is included using Firefox. It worked!

The code

What we have here (demo) is trying to include a .js file and an .css file, creating new script and link DOM elements. Then I'm attaching event listeners to those new elements - one onload and one onreadystatechange. The script that is included (jsalert.js) has one alert().

var css;
function include_css(css_file) {
    var html_doc = document.getElementsByTagName('head')[0];
    css = document.createElement('link');
    css.setAttribute('rel', 'stylesheet');
    css.setAttribute('type', 'text/css');
    css.setAttribute('href', css_file);
    html_doc.appendChild(css);

    // alert state change
    css.onreadystatechange = function () {
        if (css.readyState == 'complete') {
            alert('CSS onreadystatechange fired');
        }
    }
    css.onload = function () {
        alert('CSS onload fired');
    }
    return false;
}

var js;
function include_js(file) {
    var html_doc = document.getElementsByTagName('head')[0];
    js = document.createElement('script');
    js.setAttribute('type', 'text/javascript');
    js.setAttribute('src', file);
    html_doc.appendChild(js);

    js.onreadystatechange = function () {
        if (js.readyState == 'complete') {
            alert('JS onreadystate fired');
        }
    }

    js.onload = function () {
        alert('JS onload fired');
    }
    return false;
}

Results

As you can probably guess, the results are different in IE and FF.

  • CSS inclusion - IE fires both events, onload first, then onreadystatechange. FF fires nothing.
  • JS inclusion - IE fires onreadystatechange. FF fires onload. Both will execute the script before firing the event.

Conclusion

1. So there is, after all, a cross-browser way to tell when a JavaScript is actually included and that is to attach two event listeners - onload and onreadystatechange.
2. In IE you have two ways to tell when a CSS is included.

 

Deferring scripts for faster rendering

Monday, October 23rd, 2006

So it turns out one can defer scripts, meaning give the browser a hint that a script will not mess up with the page right now, so it won't affect the rendering and can be delayed for later. This could give you increased performance during the initial page load and rendering, since you're giving the browser a clearance to temporarily skip this script and continue with the other magic it's doing.

The syntax is simple, just add defer attribute to the script tag and (to be XHTML compliant, give it a) "defer" value. Like this:

<script type="text/javascript" defer="defer">
  // deferred … 
</script>

<script
  type="text