Prevent flickering of CSS transitions and transforms in Webkit
I've run into the problem of flickering CSS transitions and transforms in Webkit browsers (Chrome and Safari) a few times, especially while developing Darkadia, which makes liberal use of these properties. Turns out the solution is quite simple and you can check it out here on Stack Overflow. I'll reproduce the fix here for posterity.
Add the following property to the element your transition is applied to:
#element {
-webkit-backface-visibility: hidden;
}
That sorted out most of the flickering on Darkadia, but I was still experiencing flickering on a completely unrelated element. Adding the following property to the body tag sorted that out too:
body {
-webkit-transform: translate3d(0, 0, 0);
}
Omnibar for Safari
I've recently been experimenting with using Safari as my primary browser after mounting disappointment with Chrome's performance on my Mac (random tab crashes and odd rendering issues had me scratching my head). Safari really is very quick and provides a more seamless and polished "Apple" experience, if that's your thing.
What I absolutely love about Chrome though is its Omnibar, the unified search and URL bar that figures out whether you've entered a URL or search term and performs the appropriate action. So I was delighted to find Oliver Poitrey's Safari Omnibar, a free SIMBL plugin that adds all the Omnibar goodness to Safari. It's early days yet as I've only just started using it, but it's standing up well in my testing and I haven't encountered any problems yet.
Now the only thing I'm missing is how the Delicious plugin in Chrome integrates its search results into the Omnibar, but this will do nicely for now.
Good selection of jQuery performance tweaks
jQuery Performance Tips Cheat Sheet by Dumitru Glavan.
Fascinating analysis of how big PHP arrays really are
Use jQuery to get the HTML of a container, including the container itself
Problem: You have the following markup, and you'd like to use jQuery to retrieve the contents of #container, but also include #container's markup in the returned HTML:
<div id="container"> <div class="foo">bar</div> </div>
There are a number of ways to tackle the problem, but the most elegant solution I've come across is one posted as an answer to a question on Stack Overflow. This following snippet selects the container and wraps it in a <div> tag. It then immediately selects the wrapping tag with parent(), before assigning its contents to x.
var x = $('#container').wrap('<div/>').parent().html();
If you like, you can remove the wrapping <div> tag:
$('#container').unwrap();
Handling image uploads with custom validation using the Drupal 6 Forms API
I've recently had to go through the exercise again of building a bespoke form in Drupal 6 that accepts a file upload, validates it and saves it into to the server. Not doing this nearly enough to have it committed to memory means numerous trips to Google and frequent dives into the Drupal forms API are the order of the day. I've distilled my code into a working example that I hope will serve as future reference.
The sample code below assumes that you're comfortable creating a Drupal module, and have created a form using the forms API.
First we define a form with a single upload field and a submit button:
function module_image_form() {
// Set the form's enctype to allow it to handle file uploads
$form['#attributes']['enctype'] = "multipart/form-data";
$form['logo'] = array(
'#type' => 'file',
'#title' => t('Logo'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
Next we define the form's validate function which, after ensuring that the uploaded image meets our validation requirements, will handle the actual upload. Drupal provides built-in validation to check for a valid image, but we won't learn very much from using that. Instead, we specify our own validation function to prevent GIFs from being uploaded. We'll use Drupal's own mechanism for validating the image dimensions though.
function module_image_form_validate($form, &$form_state) {
// Set this to the name of the form's file field
$field = 'logo';
// The directory the image will be saved to (file_directory_path()
// returns the path to your default files directory).
$directory = file_directory_path() . '/images';
// Drupal will attempt to resize the image if it is larger than
// the following maximum dimensions (width x height)
$max_dimensions = '800x600';
// We don't care about the minimum dimensions
$min_dimensions = 0;
// file_check_directory() ensures the destination directory is valid and
// will attempt to create it if it doesn't already exist.
if (file_check_directory($directory, FILE_CREATE_DIRECTORY, $field)) {
// Specify the validators. The first is Drupal's built-in function for validating
// the image's dimensions. The second is our custom validator to exclude GIFs.
$validators = array(
'file_validate_image_resolution' => array($max_dimensions, $min_dimensions),
'module_validate_image_type' => array(),
);
// Move the file to its final location if the validators pass, else
// return to the form and display the errors.
if ($file = file_save_upload($field, $validators, $files_directory)) {
// Set the file's status to permanent, which will prevent Drupal's file
// garbage collection from deleting it.
file_set_status($file, FILE_STATUS_PERMANENT);
// We add our final file object to the form's storage array, so that it gets passed
// through to the form's submit handler, where we can act on it.
$form_state['storage']['file'] = $file;
}
}
}
This is our custom validator, which simply checks to see that the uploaded image does not have a MIME type of image/gif:
function module_validate_image_type($file) {
$errors = array();
$info = image_get_info($file->filepath);
if ($info['mime_type'] == 'image/gif') {
$errors[] = t('Only JPEG and PNG images are allowed.');
}
return $errors;
}
Finally, we implement the form's submit handler. The successfully uploaded image will be passed to it in the $form_state variable. I'll leave the submit handler blank, because what happens next depends entirely on what you're trying to achieve.
function module_sample_form_submit($form, &$form_state) {
// print_r($form_state['storage']['file']) to view the uploaded file's details
}
Creating a MySQL database and user on the command line, and a bash script to automate the process
After having to look up the syntax for creating a MySQL user for the umpteenth time, I've decided to jot it down here for future reference. I've also included a bash script, which automates the process of creating the database and setting up the user privileges.
For the following examples, I assume that you're running a MySQL server, and have logged into it. All text within brackets, e.g. <text>, is meant to be replaced with a value of your choosing.
Let's start with the basics. Create a new database:
CREATE DATABASE <database>;
Next, we create a new user:
GRANT USAGE ON *.* TO <username>@localhost IDENTIFIED BY '<password>';
Now we allow the new user access to the database we've just created:
GRANT ALL PRIVILEGES ON <database_name>.* TO <username>@localhost;
Finally, we tell MySQL to reload its grant tables:
FLUSH PRIVILEGES;
You should now be able to log in to the server and access the database with the credentials you supplied:
mysql -u<username> -p<password> <database>
Right, so if this is something you have to do often, entering all of that quickly becomes a bit of a pain. Brian Racer has published a bash script, which automates the above steps. Brian's example gives the new user access to all the databases on the server, which is not what you might want or expect, so I've modified the script to give the user access to only the database being created:
#!/bin/bash
EXPECTED_ARGS=3
E_BADARGS=65
MYSQL=`which mysql`
Q1="CREATE DATABASE IF NOT EXISTS $1;"
Q2="GRANT USAGE ON *.* TO $2@localhost IDENTIFIED BY '$3';"
Q3="GRANT ALL PRIVILEGES ON $1.* TO $2@localhost;"
Q4="FLUSH PRIVILEGES;"
SQL="${Q1}${Q2}${Q3}${Q4}"
if [ $# -ne $EXPECTED_ARGS ]
then
echo "Usage: $0 dbname dbuser dbpass"
exit $E_BADARGS
fi
$MYSQL -uroot -p -e "$SQL"
Following Brian's example, I've named the script createdb and moved it to the /usr/bin directory on my server, after applying executable permissions to it:
chmod 755 createdb
Creating a new database and user is now as simple as:
createdb <database> <user> <password>
jQuery snippet to force external links into a new window
Usability concerns aside, one thing that is often requested by clients is to force all outbound links from their website into a new window. Here's a little jQuery script I use that achieves just that:
(function($) {
$(document).ready(function() {
var hostname = window.location.hostname.toLowerCase();
$("a").each(function() {
var href = this.href.toLowerCase();
if (href.indexOf("http://") != -1 && href.indexOf(hostname) == -1) {
$(this).addClass('external').click(function(e) {
e.preventDefault();
window.open(href, '_blank');
});
}
});
});
})(jQuery);
The script also adds a class of external to each outbound link, allowing you to style them in some way that differentiates them from normal links. Using JavaScript (and jQuery) for this purpose, offers a few benefits:
- All outbound links are targeted in one fell swoop.
- Adding target="_blank" to links is invalid markup when using the XHTML Strict 1.0 doctype.
How the iPad 2 became Harry McCracken's favourite computer
Harry McCracken in his piece on Technologizer:
At first, when I traveled out of town, I’d bring the iPad and the MacBook Air but use the iPad most of the time. Now I’ve started bringing only the iPad, unless I have specific reason to think I’ll need a full-blown computer. When I went to Chicago on a business trip last month equipped only with the tablet, it was the first time in two decades that I’d boarded an airplane for work purposes without a laptop on hand.
Harry is a writer, so uses the iPad primarily for that purpose. Using the iPad for coding is perhaps a little more difficult, but not impossible. I use Panic's excellent Prompt app for connecting to my server over SSH, so it's relatively painless to fire up Vim to bash out some code when I'm in a bind. But the lack of screen real estate makes it difficult to be really productive.
As for my wife though, who is what I'd consider a more general computer user, this setup would be perfect.
