SASS and CSScaffold

I think the concept that SASS brings to the table (or CSScaffold, for that matter) is one we’ve all had when we play with CSS and think, “Gee, I would be nice if you could use variables and constants here, and if you could duplicate less code.” And then we would think of splitting our CSS up into many little files, since they’re easier to organize by function, only to find that that wasn’t such a hot idea because a browser will have to make a new HTTP connection for each one to download it.

So here’s SASS/CSScaffold adding just those features that CSS was missing. But is it all good news? I’d say on the whole, yes, but here’s a few points:

SASS requires that you compile your stylesheets every time you update them. My typical development cycle is make a little change to CSS (one one monitor) and hit refresh in my browser (other monitor) to see if the change did what I wanted it to do. That would have to change: now I would need to compile my CSS before I hit refresh. Not insurmountable, but it’s one more thing I can and will forgot and then I’ll think, “Hey, now why didn’t that change do anything?” only to find out after some head-scratching that I forgot to compile.

CSScaffold doesn’t seem to have this problem: since it’s written in PHP, it’ll run every time the CSS is requested from the server. I’m sure the authors have built in some sort of caching, so it should be quick enough. That actually sounds handier to me than SASS does, merely because I don’t need to compile. So the question is then, is CSScaffold just as good, better, or worse? If it’s just as good, I’ll go with it instead of SASS!

But is what SASS/CSScaffold do really that new? Like I said at the start of this post, it’s an idea all of us have thought of… and implemented! It’s always been possible to produce CSS through PHP. You can put a link to a PHP file in your page’s header, have it output a text/css header and you’re good to go. That’ll allow you to use variables, like SASS, constants, like SASS, functions and mixins, like SASS, all at zero cost since you had PHP anyway. You’ll basically only need to write the fancy gradient functions that SASS adds.

In order to add caching, you could pull your CSS through Smarty, thus prettifying the syntax a bit (it never feels quite right having PHP produce actual HTML or CSS what with the separation of code and presentation, so using Smarty gives a fuzzy warm feeling of righteousness). You could even write some spiffy new functions for Smarty, thus creating your own Sassy Smarty. So why all the hullabaloo?

Well, for one thing… SASS does more than I ever implemented with a CSS/PHP/Smarty approach, so hats off for that. But I still don’t like the compilation requirement.


Dynamic CSS through PHP

When writing CSS,you will find yourself repeating information a lot, which is always a bad thing in programming. CSS 2 lacks constants, which would allow us to define a value once and refer to it many times. Instead, we are forced to repeat the actual value many times, making updating CSS a process that is prone to errors.

Also, in order to reduce the number of connections a client must make to the server, it’s necessary to place all CSS in a single file. But this may mean that you end up with a lot of possibly unrelated CSS in a single file, making it difficult to navigate while you’re developing. There are times when it’s simply handier to have lots of  small files instead of one big file, but it’s just not practical for download by your visitors.

These two problems can be resolved by loading your CSS through PHP. Instead of serving the CSS file directly, i.e.

<link rel="stylesheet" href="css/style.css" type="text/css" media="screen"/>

you can have the server load a PHP script that produces CSS like so:

<link rel="stylesheet" href="css/style.css.php" type="text/css" media="screen"/>

Note that this will only work if the scripts emits a text/css header:

<?php
  header("Content-type: text/css");
  ...
?>

Now your PHP script can define some constants that you simply insert into your CSS:

<?php
   header("Content-type: text/css");
   $mycolor = "#aaa";
?>
 
p {
  line-height: 1.1em;
  color: <?php echo $mycolor; ?>
}

Your script could also load various CSS files for processing and output the result in one go, solving the second problem we found. But we can do better still. You can have your PHP script use Smarty to produce the CSS, making the use of contants easier (and prettier):

<?php 
  header("Content-type: text/css"); 
  require_once "../smarty/Smarty.class.php";
  $smarty = new Smarty();
  $smarty->template_dir = "../smarty/templates";
  $smarty->compile_dir = "../smarty/templates_c";
  $smarty->cache_dir = "../smarty/cache";
  $smarty->config_dir = "../smarty/configs";
  $smarty->compile_check = true;
  $smarty->caching = 0;
  $smarty->display("file:style.css");
?>

The file style.css would be the main style sheet manifold; it could load other (sub-) stylesheets. For instance:

{assign var="defaultfont" value="normal 11px/1.2em Arial, sans-serif"}
{assign var="thinborder" value="solid 1px #aaa"}
{assign var="inputcolor" value="#666"}
{include file="sys/global-reset.css"}
{include file="sys/base.css"}
{include file="sys/loader.css"}
{include file="control/accordion.css"}
{include file="control/ajaxtable.css"}
{include file="control/button.css"}

The values that were assigned to defaultfont, thinborder and inputcolor can be used in the sub-stylesheets like so:

input
{
  border: {$thinborder};
  color: {$inputcolor};
}

FireFox 4 does not like script.aculo.us builder

After upgrading to FF4 I noticed that some of my JavaScript, which had been working perfectly fine, stopped working. I was able to isolate the problem to the use of the script.aculo.us Builder class to create a script element, like so:

var head = $$("head")[0];
js = Builder.node("script", { type: "text/javascript", src: path });
js.onreadystatechange = function() { if (js.readyState == 'loaded'
  || js.readyState == 'complete') js.onload(); };
js.onload = function() { console.log("loaded!"); };
head.insert(js);

However, the onload event would never be triggered. In fact, Firebug indicates that the JavaScript file I’m trying to load is never actually loaded from the server. So it’s back to basics without using script.aculo.us’s Builder:

var head = $$("head")[0];
var js = document.createElement('script');
js.type = 'text/javascript';
js.onreadystatechange = function() { if (js.readyState == 'loaded'
  || js.readyState == 'complete') js.onload(); };
js.onload = function() { console.log("loaded"); };
js.src = path;
head.appendChild(js);

And guess what: this works. The file is loaded. Now why does this happen? The new script element is in fact added to the DOM; I can see that in Firebug. But it never loads the JavaScript from the server.

Playing around with script.aculo.us’s builder.js shows that the script tag cannot be created through innerHTML but must be created through document.createElement instead. I don’t know why script.aculo.us tries the innerHTML approach first, but it does – and it works. It just doesn’t load the javascript file. If I deliberately make the innerHTML approach fail, it falls back to document.createElement, which works.

This is not the whole story, though. When adding attributes to the newly created element, builder.js again tries to use innerHTML before using document.create. And again, skipping innerHTML to make it fall back to document.create works.

The reason innerHTML is used can be found here, according to the source, but I could not access this URL at the time of this writing.


What to do about PHP 5.3′s timezone compaints

If you’ve just upgraded to PHP 5.3, your scripts will probably generate a bunch of errors in this vein:

Warning: date(): It is not safe to rely on the system's timezone settings.
You are *required* to use the date.timezone setting or the
date_default_timezone_set() function. In case you used any of those methods
and you are still getting this warning, you most likely misspelled the
timezone identifier. We selected 'Europe/Helsinki' for '2.0/no DST' instead.

Obviously, the solution is to use the date_default_timezone_set() function, but that means you need to change scripts that ran perfectly well and warning-free previously.

There’s another approach. You can set the default time zone in PHP’s configuration file so that it won’t bother you again (at least on your server). To do so, fine the date.timezone setting in php.ini and change it thusly:

date.timezone = Africa/Maputo

Now the warnings will be gone.


Creating a Forward Proxy with WEBrick

Building a simple forward proxy in Ruby with WEBRick requires very little code. Here is a small sample that forwards all requests but for the example.com domain, which it blocks.

require 'webrick/httpproxy'
 
def handle_request(req, res)
  puts "[REQUEST] " + req.request_line
  if req.host == "example.com" || req.host == "www.example.com"
    res.header['content-type'] = 'text/html'
    res.header.delete('content-encoding')
    res.body = "Access is denied."
  end
end
 
if $0 == __FILE__ then
  server = WEBrick::HTTPProxyServer.new(
    :Port => 8123,
    :AccessLog => [],
    :ProxyContentHandler => method(:handle_request))
  trap "INT" do server.shutdown end
  server.start
end

The interesting bit is the handle_request method. WEBRick provides us with the request and response instance for each request, so that we can check what’s being requested and block certain URLs. Since the response is also already available, we can even perform content filtering.


Get local machine’s MAC address in C#

Here’s a way to get the local machine’s MAC address in C#. Note that there may be various MAC addresses (Ethernet cards, local loopback devices, hooked up 3G devices etc.), so we try to find only the Ethernet MAC address:

static string GetMacAddress()
{
  string macAddresses = "";
  foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
  {
    // Only consider Ethernet network interfaces, thereby ignoring any
    // loopback devices etc.
    if (nic.NetworkInterfaceType != NetworkInterfaceType.Ethernet) continue;
    if (nic.OperationalStatus == OperationalStatus.Up)
    {
      macAddresses += nic.GetPhysicalAddress().ToString();
      break;
    }
  }
  return macAddresses;
}