LESS vs. SASS: The PHP Preprocessors

I think there's a bit of fanboi-ism that is keeping the LESS and SASS camps divided, since at first glance both languages seem pretty similar. Here's where I think the real differences start.

Both preprocessors come with some baggage… With LESS, it's the notion that Javascript running client-side can handle CSS processing (a bad idea). The road from there to a Node.js or Rhino set up isn't clearly defined. The SASS webpage, on the other hand, displays the three lines of code necessary to start watching SCSS right at the top of every page, and the Compass CLI provides "compass create ". However, SASS and Compass are saddled to Ruby.

What really needs to be built for both systems is a good PHP processor. I really hate to say it, but most of us web developers don't have the luxury of working without PHP.

SASS's PHP analog is phamlp, but the original Google hosted project hasn't been updated since September 2010. This has led to several splinter projects on github, with none gaining any real traction. Drupal's AdaptiveTheme has recently included SASS support using a PHP preprocessor. Since this is a pretty popular base theme, I'm hoping it leads to the development of a more solid PHP preprocessor. However, I'm really not sure about the level of Compass support any PHP analog provides… The Ruby version of Compass provides sprite generation, which I believe may be missing from all PHP implementations of the library.

LESS's PHP analog is lessphp. While it seems to be maintained and follows the original library pretty closely, both lessphp and phamlp suffer from a singular shared issue… both are just ports of the originals.

Of course, any server-side language is better than PHP, so I can understand why both preprocessors were written in Ruby and Javascript. This may lead to better hosting support for alternative server-side languages.

My $0.02.

Migrating a Drupal or Concrete5 installation using mysqldump, ssh, and rsync

Update (2012-05-08): For Drupal, Drush is likely a better solution than the script presented here. Instructions for migrating and maintaining a remote Drupal installation are available at drupal.org.

I've been doing a lot of Drupal and Concrete5 development using a LAMP stack on my local machine. I ended up pulling together a bash script to automate the migration process. It helps if you use ssh-copy-id to set up password-less login first… otherwise you have to enter your SSH password twice (first time to transfer the SQL dump, second time to run rsync). Simply save the two files to your hard drive and make the .sh file executable. rsync will exclude files listed in exclude-from.txt. The files listed in my exclude-from.txt include all the CMS config files, so make sure to copy them over manually and edit any server specific settings (database user/pass, etc).

migrateTemplate.sh

#!/bin/sh # upload database mysqldump --add-drop-table --extended-insert --force --log-error=error.log -uLOCAL_DB_USER -pLOCAL_DB_PASSWORD LOCAL_DB_NAME | ssh -C REMOTE_SSH_USER@REMOTE_SSH_HOST "mysql -uREMOTE_DB_USER -REMOTE_DB_PASSWORD REMOTE_DB_NAME" # rysnc files rsync -avzP --exclude-from "exclude-from.txt" --bwlimit=20 --ipv4 LOCAL_DRUPAL_FOLDER REMOTE_SSH_USER@REMOTE_SSH_HOST:REMOTE_DRUPAL_FOLDER

exclude-from.txt

; Concrete5 specific config/site.php files/cache/* files/tmp/sess_* ; Drupal specific sites/default/settings.php ; vi users represent! .*.swp

Bootstrapping Drupal 7 and adding a new admin account

Recently I had FTP access to a site, but had lost my Drupal user/pass. I was able to create a PHP file that I uploaded to the root of the Drupal installation that bootstrapped Drupal and created a user with administrator access. The code used is found below…

The code is also a good example of how to bootstrap Drupal, albeit from the Drupal installation root folder. If you want to bootstrap Drupal from another folder, make sure to change DRUPAL_ROOT to the Drupal installation folder, and make sure the "include_once(…" line points to the correct file.

<?php // Grants 'administrator' rights to a new username define('USERNAME', 'username'); define('PASSWORD', 'password'); define('EMAIL', 'email address'); // Set the header to something that produces readable output in the browser header('Content-type: text/plain'); // Bootstrap Drupal define('DRUPAL_ROOT', '.'); include_once(dirname(__FILE__) . '/includes/bootstrap.inc'); drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); // Get an array of defined user roles echo "Defined roles:\n"; $roles = user_roles(); var_dump($roles); // Find the Role ID for the 'administrator' role $adminRole = 'administrator'; $adminRID = array_search($adminRole, $roles); echo "\nAdministrator role ID: $adminRID\n"; // Try to load the user account with the specified username echo "\nExisting account: "; $account = user_load_by_name(USERNAME); echo $account === FALSE ? 'does not exist' : 'found'; // Update the user account, setting it to active and granting it the role 'administrator' echo "\nSetting account to active and assigning new role:\n"; $newRole = array(); $newRole[$adminRID] = $adminRole; $account = user_save($account, array( 'name' =?> USERNAME, 'pass' =&gt; PASSWORD, 'mail' =&gt; EMAIL, 'init' =&gt; EMAIL, 'status' =&gt; 1, 'roles' =&gt; $newRole, )); var_dump($account);

Adding a custom link to a Drupal 7 Views Template

Anyone familiar with Drupal knows Views. It's great for pulling information from the database and displaying it in just about any format you want. I recently set up a job listing site, which was pretty trivial task in Drupal (I've seen guides on setting up job listing sites in a few Drupal books). The problem I had was when I needed to add a separate "read more" link to the job listing after the description text.

Image of the job listing site

Overriding the default output on the table was simple… Views shows the list of suggested template override filenames under "Advanced → Other → Theme: Information". In my case, the template to create "theme_directory/templates/views-view-field--list-all-jobs--page--body.tpl.php". I started by copying the content of "/sites/all/modules/views/theme/views-view-field.tpl.php" and modifying to taste. Creating the "read more" link to that row's node was done using the following code.

print $output; echo '<a href="', url('node/'. $row-&gt;nid, array('absolute' =&gt; TRUE)), '" class="readMore">Read more…</a>';

The documentation for the function url(...) can be useful in understanding what's happening here. Basically, we're using the node ID to make the URL "node/X", and then asking the "url" function what the absolute URL for that path should be. This returns the full path for that link as defined in the alias for the node.

Automatically resizing Colorbox to IFRAME content

This is going to be a quick mind-dump, and hopefully it's helpful to someone. I swear, I have plans to go back through this blog and make things sensible to the sensible reader some day, but I never take/find the time :-p

I needed a good way to automatically resize a Colorbox popup to the size of the loaded IFRAME content. Colorbox itself provides the callbacks and methods to make this possible, so it was just a problem of calculating the dimensions of the IFRAME's content. The following code assumes that the IFRAME content is on the same domain as your parent page… this code won't work across domains.

The BODY tag, being a block-level element, automatically becomes 100% the width of the IFRAME. If the content of the BODY tag is wider or narrower than the initial IFRAME width (which, since we're trying to auto-calculate the width, it's a safe assumption that the width is different) we'll need to make the BODY tag the correct width before measuring it. I do this in the following code by floating the BODY tag, then removing the float after the measurement is finished.

// Initialize colorbox for gallery links $(function() { if (!$.fn.colorbox) return; // Initialize Colorbox for the IFRAME links, setting a loading width and height and // telling Colorbox to wait until the IFRAME finishes before calling onComplete $(".detailRow .viewGallery").colorbox({ "iframe": true, "fastIframe": false, "innerWidth": 480, "innerHeight": 360, "onComplete": function() { // Do some work to automatically calculate the height and width of the IFRAME's content var iframe = $("iframe.cboxIframe"), body = iframe.contents().find("body"), floatStyle = body.css("float"); // Float the BODY so that it assumes a minimal width body.css("float", "left"); // Resize the colorbox $.colorbox.resize({ "innerWidth": body.width(), "innerHeight": iframe.contents().height() }) // Remove our float style on the BODY body.css("float", floatStyle); } }); });

Javascript Message Window

I'm working on a popup message window for my uncle's game Dark Expanse. The idea is that clicking a star on the star map will pop up a small "e-mail" window displaying messages related to that star. The code is a work in progress, but I just hit a milestone and wanted to upload it quick.

At this point, the data is loaded using a $.getJSON call, displayed in the message selector, and clicking a message loads it into the display area. The last few hours have been spent integrating a scrollbar solution so that the scrollbars are always a fixed width, instead of dealing with browser and UI inconsistencies. I used Tiny Scrollbar created by Maarten Baijs because it was simple, straightforward, and very lightweight. I've also handled up and down arrow keystrokes, and set it so that the scrollbar updates when the newly selected message is outside of the viewport. I'm not sure if anyone else will find this code useful, and I'm not even sure what to call it, but perhaps someone else needs something similar.

Here it is

Format price output with Javascript

What follows is a script to format price output in Javascript. The function takes as its input a single number, forces exactly two decimal points, then adds commas for the the thousands, millions, etc. Finally, before returning the output it adds the dollar symbol and a minus sign if applicable.

Depending on where you are using this script, you may not be able to guarantee that only numbers are being passed to the code. For example, you may need to parse a string on the page to get a number before doing math (to apply a discount, calculate a price difference, etc). The first function will strip out non-numeric characters, then convert the number to a price with two or fewer decimal points. If I remember correctly, the multiplication and division by 100 not only serves to round to 2 decimal points, but also avoids a floating number problem (but I can't remember exactly, or I'd test it…).

// Take a string (possibly containing currency symbols and/or commas), // and strip out the extra characters, returning an actual number. function parsePrice(str) { return Math.round(Number(str.replace(/[^-\d\.]/g, "")) * 100) / 100; } // Take a number, and add all the necessary prettifying characters that // make a number look like a price (currency symbol, minus sign, commas). function formatPrice(price) { // Run toFixed to force exactly 2 decimal points // Note: This will round the thousandths up if applicable. For example, 3.145 // will be rounded to 3.15, but 3.144 will become 3.14 var value = Math.abs(price).toFixed(2), commaRe = /([^,$])(\d{3})\b/; // Add commas in convincing locations while (commaRe.test(value)) { value = value.replace(commaRe, "$1,$2") } // Add the appropriate currency symbol and minus sign if applicable return (price &lt; 0 ? "-$" : "$") + value; }

So for example, say you want to apply a discount of 20% to a product page dynamically (not necessarily a good idea, but it's late and good examples aren't coming to me easily atm). The page lists the product price as "$8.99" in a DIV with the class="price". Using jQuery to get the DIV, you could write:

// Get the price from the page, and parse it to a number var listedPrice = parsePrice($(".price").text()); // Do our multiplication by .8 to get a 20% discount, then format that price and // and display it in the ".discountedPrice" element $(".discountedPrice").text(formatPrice(listedPrice * .8));

… or, you can do the entire bit on one line, if readability isn't an issue :-p

jQuery :checkbox replacement

I had to set up a checkbox replacement for the site http://www.reallysimplesurvey.com/sign-up, so I first went looking for a good replacement. I found one that I'd used before, but it required jQuery UI. Since I hate adding jQuery UI for "simple" tasks, and since trying to get jQuery UI working sanely in Concrete5 can be a bit of a trick, I decided to write my own (people who know me know it doesn't take much to convince me to rewrite Javascript just for kicks and giggles…).

I had some trouble with race conditions in IE, and I didn't spend too much time troubleshooting why, so the code might be sloppier than usual. Also, the only comments in the code at the moment are IE gripes, so my apologies :-p The use of double-class selectors means IE6 won't show when the checkboxes have keyboard focus, but all else should work without problem.

Please take it for a test spin and let me know how it works. This is the first release, so YMMV and feedback is welcome.

Screenshot of checkbox demo

Lossy PNG Compression

I finally found a program that I've been dreaming of for years. Image Analyzer will perform lossy compression on PNGs, preserving the alpha transparency of the PNG file while, in most cases, creating a file comparable in size to a JPEG.

Originally I had hung my hopes on JPEG 2000, which supported alpha transparency as part of the updated JPEG file format. However, application support for the new format never coalesced, and 11 years past the release of the standard it still isn't supported by Firefox or Internet Explorer.

Back to Image Analyzer… Loading a PNG with alpha-transparency into the program will trigger a dialog which asks whether it should "Merge alpha channel with picture?". Choose No to load the alpha channel and RGB data as separate images, then choose the RGB image and choose "Save As" from the File menu. From the save dialog, click the "File format option" button on the right-side of the dialog. Click the "Alpha transparency" checkbox and choose the correct alpha channel from the dropdown that appears. Then choose the compression quality from the slider at the bottom of the dialog. The default quality setting is lossless. I've found a setting of about -7 to be a nice balance between the size of the file and the quality of the output. The "Test" button allows for previewing of each quality setting to find what works best for each image.

The program works like a charm, and the output looks great. This saved my sanity while working on a new gradient-and-dropshadow heavy website that refused to cooperate with my attempts to flatten the images. PNGs again ftw.

jQuery SELECT Dropdown

I'm toying with some code to reproduce a SELECT dropdown using jQuery and some CSS3. The default SELECT element is notoriously difficult to style, but also amazingly difficult to program in a manner that works properly across browsers. I've seen many different projects along the lines of what I've done here, but I finally found one that I liked :-p The original code is from the blog Janko At Warp Speed. I will continue to clean up this code, but for now it is provided without warranty or promise of any kind, so YMMV. For the record, have not even tested in IE yet. Click below for the demo page.

Update (2011-02-23)

I finally got around to adding OPTGROUP support for a project I'm doing, and I added a few examples showing how the code can be used for site navigation (UL-LI-A dropdown navigation, instead of using a SELECT with an "onchange" handler). My next set of goals is to add keypress support, figure out the best way to make sure the dropdowns always show the same value as the SELECT (even after a page refresh), and maybe add a few more display styles.

Update (2011-05-23)

I'm still doing work on this project (amazingly). I finally organized the CSS, moving it and the Javascript out of the HTML and into their own files. I also added a download archive and slapped a license at the top of each file (I've been using this code frequently and the license links back to this site). I plan on adding SELECT MULTIPLE and keystroke functionality for a project. I'm hoping ARIA support is not far behind.

Update (2012-03-17)

Moved the project to github.

Screenshot of the dropdown demo

Pages

Subscribe to blog.gatherage.com RSS