Archive for December, 2007

Internet Explorer 8 - Part 2

Saturday, December 29th, 2007

Interesting news from the Microsoft camp. The IE8 development team has announced that their current development copy passes the ACID2 test!

See the full video from Microsoft: http://channel9.msdn.com/showpost.aspx?postid=367207

James.

Object initialisation with PHP5

Wednesday, December 12th, 2007

Frequently we’ll write PHP classes for objects which need to be both listed on a page, and have their own detail page. I’ve seen lots of methods used, each with their own pros and cons. In this article I talk about my personal choice and the factors that influenced that choice. This article refers to PHP5.

An overview of the problem

I’m sure you’ve all done this before, you have your “user-list.php” page which lists all the users in the system then you have your “user-detail.php” page which allows the editing of a specific user object. Now, your User object may have a constructor defined like so:

  1. <?php
  2. class User {
  3.     function __construct($id = 0) {
  4.          /* connect to db */
  5.         $results = $db->query(
  6.             SELECT `id`, `name`, `email`, `live`
  7.             FROM `users`
  8.             WHERE `id` = ‘ . $id
  9.         );
  10.  
  11.         /*
  12.          * Put table data in to class vars for
  13.          * use in getter/setter functions.
  14.          */
  15.     }
  16. }
  17. ?>

So your $id is the record id in the “users” table, probably passed in as a GET variable from the listing page. So where are the issues here? Well what happens when you want to list all of the records on the listing page? You’ll might do something like the following (yes, I’ve done this before):

  1. <?php
  2. /* connect to db */
  3.  
  4. $results = $db->query(
  5.     SELECT `id`
  6.     FROM `users`
  7. );
  8.  
  9. while ($row = $results->fetch_row())
  10. {
  11.     $user = new User($row[0]);
  12.  
  13.     /* echo out the details for this user here */
  14. }
  15. ?>

So you select all the id values for the users in your system, pass them in to your User object which then does a lookup for the record in the “users” table relating to the id passed in - and if you’re displaying 50 users on the page that’s 50 SELECT statements per hit (assuming you’re not caching the page or data). You may be thinking “why bother creating an object when all you need is the data?” and fair point, if that’s the case then you wouldn’t need to instantiate a User object, but for the sake of this example lets assume that the User object contains some business logic unknown to the coder implementing this particular page and therefore a User object must be instantiated rather than outputting the raw table data directly.

So the problem we face is efficiently using the data we’ve already queried from the database in a manageable and consistent way?

Option 1: User constructor can accept an integer or an array

We could allow the user to pass in an integer record ID or an array of name/value pairs which map to User member variables. It might look something like this:

  1. <?php
  2. class User {
  3.     function __construct($idOrProperties = 0) {
  4.         if (is_array($idOrProperties)) {
  5.             /*
  6.              * Extract the data from the array and
  7.              * use it to populate class members.
  8.              */
  9.         }
  10.         else {
  11.             /*
  12.              * Record ID was passed in so query the
  13.              * database and get the required data.
  14.              */
  15.         }
  16.     }
  17. }
  18. ?>

That’d work quite nicely, however there’s two things that concerns me about this code. The first is the extensibility. If all of a sudden we’re to start accepting data from say a SimpleXML object the the constructor starts to grow:

  1. <?php
  2. class User {
  3.     function __construct($arg = 0) {
  4.         if (is_array($arg)) {
  5.             /*
  6.              * Extract the data from the array and
  7.              * use it to populate class members.
  8.              */
  9.         }
  10.         elseif ($arg instanceof SimpleXMLElement){
  11.             /*
  12.              * SimpleXMLElement was passed in so
  13.              * extract the data from that.
  14.              */
  15.         }
  16.         else {
  17.             /*
  18.              * Record ID was passed in so query the
  19.              * database and get the required data.
  20.              */
  21.         }
  22.     }
  23. }
  24. ?>

Ok so the input variable name could be a little prettier but you get my point. Each of those conditional blocks is extracting data from quite different sources with the same end result - several basic pieces of data relating to the user. Can you can imagine the phpdoc comment you’re going to need to write to explain this function?! One further crucial issue is the case where you want to instatiate with two different SimpleXMLObjects which contain different data formats which aren’t related?

Option 2: Static functions to create the User object

Using this method, the coder/implementer never constructs the User object directly, in fact they can’t because the constructor is made private. Instead of a constructor we use static methods, each method accepting one single format of data, however they all have the same end result, they construct and return a User object (static functions can instantiate an object of the class they belong too even if the constructor is private). The constructor is now altered to accept accept an array of name/value pairs and nothing else, this keeps the constructor definition clean and simple and free of the details of extracting from various data sources. Because the constructor is private and the static functions are apart of the User class we can rely on those static functions to pass in safe, reliable data.

This is how our new code might look:

  1. <?php
  2. class User {
  3.     private function __construct($properties = null) {
  4.         /*
  5.          * Extract the data from the array and
  6.          * use it to populate class members.
  7.          */
  8.     }
  9.  
  10.     public static function fromId($id) {
  11.         /*
  12.          * Lookup the record in the user table
  13.          * and popuplate populate the $data array
  14.          * with the appropriate data.
  15.          */
  16.  
  17.         return new User($data);
  18.     }
  19.  
  20.     public static function fromArray(array $properties) {
  21.         /*
  22.          * Extract the data from the array and
  23.          * use it to populate $data array. It may
  24.          * not need any manipulation at all infact.
  25.          */
  26.  
  27.         return new User($data);
  28.     }
  29.  
  30.     public static function fromXML(SimpleXMLElement $xml) {
  31.         /*
  32.          * Extract the data from the XML object
  33.          * and use it to populate $data array.
  34.          */
  35.  
  36.         return new User($data);
  37.     }
  38. }
  39. ?>

So we’ve taken care of the problem of all of our different data sources, we now have another problem however, how do we construct a new object without any data? My first thought would be to create another static function, lets call if “fromNew”:

  1. <?php
  2. class User {
  3.     private function __construct($properties = null) {
  4.         /*
  5.          * Extract the data from the array and
  6.          * use it to populate class members.
  7.          */
  8.     }
  9.  
  10.     /*
  11.      * All the other functions here.
  12.      */
  13.  
  14.     public static function fromNew() {
  15.         /*
  16.          * Create $data array but set the
  17.          * values to blank/zero etc as required
  18.          */
  19.  
  20.         return new User($data);
  21.     }
  22. }
  23. ?>

So using User::fromNew() will create a new instnance of the User object with all it’s variables initialised to their defaults. I personally don’t like the idea of a “fromNew()” function to create a new instance of the class, afterall that’s what the “new” keyword is for. So how can we use the “new” keyword to create a User class rather than all these static function?

Option 3: Static functions to return array to pass to constructor

The third optional is to use static functions which return an array which is then passed in to the constructor. The advantage with this method is that we can now instantiate a new User object using the “new” keyword. The disadvantage is that the syntax starts looking a little messy and bloated, it also relies on the user understanding that an object must be instantiated from a static method. Here’s how it might look:

  1. <?php
  2. class User {
  3.     function __construct($properties = null) {
  4.         /*
  5.          * Extract the data from the array and
  6.          * use it to populate class members.
  7.          */
  8.     }
  9.  
  10.     public static function fromId($id) {
  11.         /*
  12.          * Lookup the record in the user table
  13.          * and popuplate populate the $data array
  14.          * with the appropriate data.
  15.          */
  16.  
  17.         return $data;
  18.     }
  19.  
  20.     public static function fromArray(array $properties) {
  21.         /*
  22.          * Extract the data from the array and
  23.          * use it to populate $data array. It may
  24.          * not need any manipulation at all infact.
  25.          */
  26.  
  27.         return $data;
  28.     }
  29.  
  30.     public static function fromXML(SimpleXMLElement $xml) {
  31.         /*
  32.          * Extract the data from the XML object
  33.          * and use it to populate $data array.
  34.          */
  35.  
  36.         return $data;
  37.     }
  38. }
  39. ?>

And then to use it you might do the following:

  1. <?php
  2. include(‘class.User.php’);
  3.  
  4. /*
  5.  * Create a new user from an ID
  6.  */
  7. $userA = new User(User::fromId(5));
  8.  
  9. /*
  10.  * Create a new user from an XML object
  11.  */
  12. $userB = new User(User::fromXML($simpleXMLElement));
  13. ?>

A little over complicated don’t you think? Is there a cleaner and better way?

Option 4: Static function called from within the constructor

We’re almost going full circle with this one. It’s very similar to Option 1 with the exception that the the static functions are called from the constructor depending on the type of data sent in, i.e.:

  1. <?php
  2. class User {
  3.     function __construct($arg = null) {
  4.         if (is_array($arg){
  5.             $data = self::fromArray($arg);
  6.         }
  7.         elseif ($arg instanceof SimpleXMLElement) {
  8.             $data = self::fromXML($arg);
  9.         }
  10.         else {
  11.             $data = self::fromId($arg);
  12.         }
  13.  
  14.         /*
  15.          * Use the values from $data array to
  16.          * populate the object members.
  17.          */
  18.     }
  19.  
  20.     public static function fromId($id) {
  21.         /*
  22.          * Lookup the record in the user table
  23.          * and popuplate populate the $data array
  24.          * with the appropriate data.
  25.          */
  26.  
  27.         return $data;
  28.     }
  29.  
  30.     public static function fromArray(array $properties) {
  31.         /*
  32.          * Extract the data from the array and
  33.          * use it to populate $data array. It may
  34.          * not need any manipulation at all infact.
  35.          */
  36.  
  37.         return $data;
  38.     }
  39.  
  40.     public static function fromXML(SimpleXMLElement $xml) {
  41.         /*
  42.          * Extract the data from the XML object
  43.          * and use it to populate $data array.
  44.          */
  45.  
  46.         return $data;
  47.     }
  48. }
  49. ?>

Unfortunately this sports all the same problems as Option 1, all we’re doing is tidying things up a little by using static functions rather than embedding all the logic in the constructor.

Option 5: Tuned for integer and array to constructor

With this option we determine the type of the constructor argument which must be either an integer which represents the record ID, or it’s an array of User data. I think it’s a reasonable trade-off between complexity of implementation and ease of extensibility

This option has the following features:

  1. Allows instantiation with a record ID
  2. Allows instantiation with an array
  3. Is extensible by using static functions

Here’s the implementation:

  1. <?php
  2. class User {
  3.     function __construct($arg = null) {
  4.         if (is_integer($arg)){
  5.             $data = self::fromId($arg);
  6.         }
  7.         else {
  8.             $data = $arg;
  9.         }
  10.  
  11.         /*
  12.          * Use the values from $data array to
  13.          * populate the object members.
  14.          */
  15.     }
  16.  
  17.     public static function fromId($id) {
  18.         /*
  19.          * Lookup the record in the user table
  20.          * and popuplate populate the $data array
  21.          * with the appropriate data.
  22.          */
  23.  
  24.         return $data;
  25.     }
  26.  
  27.     public static function fromXML(SimpleXMLElement $xml) {
  28.         /*
  29.          * Extract the data from the XML object
  30.          * and use it to populate $data array.
  31.          */
  32.  
  33.         return $data;
  34.     }
  35. }
  36. ?>

An implementation might look something like this:

  1. <?php
  2. include(‘class.User.php’);
  3.  
  4. /*
  5.  * Create a new user from an ID
  6.  */
  7. $userA = new User(5);
  8.  
  9. /*
  10.  * Create an array of new User objects
  11.  * from a SELECT on the entire users table
  12.  */
  13.  
  14. $results = $db->query(
  15.     SELECT `id`, `name`, `email`, `live`
  16.     FROM `users`
  17. );
  18.  
  19. while ($row = $results->fetch_assoc())
  20. {
  21.     $user = new User($row);
  22.  
  23.     /* do stuff to the user */
  24. }
  25.  
  26. /*
  27.  * Create a new user from an XML object
  28.  */
  29. $userB = new User(User::fromXML($simpleXMLElement));
  30. ?>

It’s very important to define and document the name/value pairs and their types for the array that’s passed in to the User constructor. That way another coder can easily add in further static functions to retrieve data from other sources and pass return them according to your well defined array definition.

There’s one major flaw with this method - it breaks inheritance!

If we were to extend User with a class called AdminUser it might look something like the following, bear in mind I’m focusing purely on the fromXML() function in this example:

  1. <?php
  2. class AdminUser extends User {
  3.     function __construct($arg = null) {
  4.  
  5.         /*
  6.          * Construct the parent object
  7.          */
  8.         parent::__construct($arg);
  9.     }
  10.  
  11.     public static function fromXML($xml) {
  12.         /*
  13.          * Extract the data from the XML object
  14.          * and use it to populate $data array.
  15.          */
  16.  
  17.         /*
  18.          * Now call the same static function
  19.          * on the parent class (User)
  20.          */
  21.         $userData = User::fromXML($xml);
  22.  
  23.         /*
  24.          * Merge the base class and sub
  25.          * class data arrays.
  26.          */
  27.         $data = array_merge($userData, $data);
  28.  
  29.         return $data;
  30.     }
  31. }
  32.  
  33. /*
  34.  * Now instantiate an AdminUser with a
  35.  * SimpleXMLElement object.
  36.  */
  37. $a = new AdminUser(AdminUser::fromXML($simpleXMLElement));
  38. ?>

The problem with this is that it increases the coupling between base and sub-class, this is potentially dangerous. For example, there is nothing to stop User::fromXML() returning an array with an item which has the same name as one returned from AdminUser::fromXML(). If this did happen then if it’s coded as per my example then the duplicate name item from $userData would be overwritten by the duplicate name item in $data, such is the functionality of array_merge(). This would no doubt cause all sorts of problems. Next we look at a possible solution to these issues.

Option 6: Adding a type parameter to the constructor

The final option, my method of choice, determines what to do based on a type parameter passed in to the constructor. This type parameter may be defined as a static class constant, it’s also optional and defaults to a mode which still determines what to do based on the data type passed in. If we stick with the AdminUser class, it now looks like this:

  1. <?php
  2. class AdminUser extends User {
  3.     const DETERMINE = 0;
  4.  
  5.     function __construct($value = null, $type = self::DETERMINE) {
  6.  
  7.         if ($type == self::DETERMINE) {
  8.             /*
  9.              * Determine if $value is an int or an
  10.              * array. If it’s an int call AdminUser::fromId
  11.              * otherwise it must be an array in the
  12.              * required format.
  13.              */
  14.             if (is_integer($value)) {
  15.                 $data = self::fromId($value);
  16.             }
  17.             else {
  18.                 $data = $value;
  19.             }
  20.         }
  21.         elseif ($type == self::XML) {
  22.             $data = self::fromXML($value);
  23.         }
  24.  
  25.         /*
  26.          * Construct the parent object
  27.          */
  28.         parent::__construct($value, $type);
  29.     }
  30.  
  31.     public static function fromXML($xml) {
  32.         /*
  33.          * Extract the data from the XML object
  34.          * and use it to populate $data array.
  35.          */
  36.  
  37.         return $data;
  38.     }
  39. }
  40. ?>

As you can see the value and type parameters are passed straight through to the parent constructor. Yes, there’s going to be duplication of code between the base class and sub-class in terms of the conditional statements to determine which static function to use to extract the data from the $value variable but I don’t personally consider that too much of an issue given that we now have a solution which:

  1. Supports inheritance
  2. Allows initialisation with “new” keyword and no args to constructor
  3. Allows initialisation with a record ID
  4. Allows initialisation with an array from a SELECT query
  5. Is very exstensible

Here’s a snippet of how the User class might be used:

  1. <?php
  2. /*
  3.  * New blank user class.
  4.  */
  5. $user = new User();
  6.  
  7. /*
  8.  * Initialise from a record ID, the
  9.  * the most likely form of construction.
  10.  */
  11. $user = new User(4);
  12.  
  13. /*
  14.  * Initialise from an array generated
  15.  * from a sql SELECT statement.
  16.  */
  17. $results = $db->query(
  18.     SELECT `id`, `name`, `email`, `live`
  19.     FROM `users`
  20. );
  21.  
  22. while ($row = $results->fetch_assoc())
  23. {
  24.     $user = new User($row);
  25.  
  26.     /* echo out the details for this user here */
  27. }
  28.  
  29. /*
  30.  * Initialise with a SimpleXMLElement object.
  31.  */
  32. $user = new User($xmlObject, User::XML);
  33. ?>

In reality this is again very similar to Option 1. The difference is that we’ve put the data extraction functions in to separate static methods meaning more maintainable and readable code, we’ve also added in the type parameter too to make it more extensible. The disadvantage of this method is perhaps that the interface is a little confusing to the user as it could accep any type of data depending on the type parameter, however I’m sure with clear and thorough documentation this won’t be an issue.

I’d really like to hear your thoughts on any of the above, especially how you tackle the scenario I outlined at the start.

Note that none of the code above is tested so I expect I’ll get a whole host of posts pointing out my syntax errors ;)

James.

SEO Rank Progress

Monday, December 10th, 2007

From the start of November we’ve been working to climb the ranks of Google. This document details all the steps we’ve taken thus far to improve our ranking with and the effects these steps have had on our position.

Our SEO strategy

First of all we’re keen to show that you don’t need hidden content and custom search engine landing pages to get a good ranking. We intend to get a high natural search ranking with NO underhand techniques. Search engines are designed to find content relevant to your search term, by tricking them you are just making the results more inaccurate for other users and spoiling their web experience. This is why if and when a search engine finds out you’ve been deceiving them you are penalised and often banned. In our opinion the only way to achieve a consistently high ranking site is with good honest content. This forms the crux of our SEO strategy. On our main site we’ve made an effort to include as much relevant content to the user without it turning in to an essay. The blog forms another major part of our site content and it’s our hope that with enough decent blog entries we can generate a reasonable number of backlinks - the blog entries need to contain decent content though which will require time and effort - web karma.

Where we our now

As of today (10th December 2007) we are ranked in the following positions:

  • 2nd for “digital agency norwich”
  • 209th for “digital agency”
  • 25th for “technical agency norwich”

Norwich, in case you aren’t aware is the city we’re based in. Due to the amount of competition for “digital agency” we decided to focus on searches specific for our city. If we climb the ranks for “digital agency” too then that’s a bonus.

The story so far

So far we have been focusing on appropriate back links. This is normally in the form of articles written for other sites and their users. We have been tweaking our keywords and meta-data and yesterday we were ranked 5th for “digital agency norwich” showing that a little trial and error can go a long way.

Here is a list of what we did from day one to get our rank:

  1. Create a semantically marked up site
  2. Create a simple site map
  3. Hide duplicate content from search engines using robots.txt
  4. Tweak page titles and add in meta keywords and descriptions
  5. Ensure all site content is relevant to keywords and titles
  6. Remove duplication from meta data
  7. Create a blog
  8. Write articles that people will find useful, link to and tell people about
  9. Post on relevant forums

Ok so thats an overview of what we’ve done. We cannot really go into to much detail about our current back links and forum posts as we are not sure how many there are and Google takes a little while to update. With forum posts, always include your signature so people can take a look at your site.

What we’ve avoided

We’ve made an effort not to replicate page titles and meta-data across multiple pages. The worst thing we could do would be to have every page title the same, what’s preferable is a title relevant to the page the user is on, for example take a look at our Email Campaigns page - it’s title is “Email development and campaign delivery systems - Digital Overload”, I think this describes the page well with no “keyword stuffing”. Also notice that we put the company name at the end of the title, there are two reasons for this: 1) From an accessibility point of view it’s important to present the unique page title to your user as soon as possible. If you’re using a screenreader for example it’s annoying for sure to hear the company name at the start of every page you visit (though it is important that it’s there). 2) It’s been reported that the words towards the start of the title are given more weight by search engines so it’s important for these to be unique to the page.

There’s a temptation to comment on as many blogs as possible with the intention of gaining a backlink. Don’t. Most blogs will add in rel=”nofollow” to each anchor in the comments automatically so Google won’t pay any attention to them anyway - if the comments are moderated then the chances are that the moderator will see what you’re up to and delete the post anyway.

Forum posting however is one way to earn yourself brownie points and gain backlinks! Just make sure your posts are relevant and helpful, responding to someone’s request for help with “Well, good luck with that.” does not constitute a helpful post.

What next?

From this point on we’ll keep track of any amends and changes we make to our site and details of changes to our rank and thoughts on all of this! Watch this space.

Internet Explorer 8

Sunday, December 9th, 2007

Bill Gates recently announced that the next version of Microsoft’s web browser would be called, wait for it, Internet Explorer 8! It’ll no doubt be a few years before it’s released and will hopefully finish off what IE7 started, bug fixing! One step further to full CSS 2.1 compliance and true XHTML support would be diamond but no-ones holding their breath.

Lets look back at IE7 for a moment. It’s certainly a heck of a lot nicer to code for than IE6 with hack-related development time cut in half, support for Alpha transparency in PNGs is awesome too but while IE6 holds on to anything greater than 1% market share we’re still developing and testing for both. I’m glad that Microsoft pushed IE7 out as an automatic-download with option to install, I just hope with IE8 they go the whole hog and make it a critical-update!

See Microsoft’s blog for full details.

James.

Dreamweaver Shortcuts

Friday, December 7th, 2007

Here are the core Dreamweaver shortcuts we use to help us code even faster!

File Management

Action Shortcut
New document Control+N
Open an HTML file Control+O
Close Control+W
Save Control+S
Save as Control+Shift+S
Exit/Quit Alt+F4 or Control+Q

Code Editing

Action Shortcut
Redo Control+Y or Control+Shift+Z
Cut Control+X or Shift+Delete
Copy Control+C
Paste Control+V or Shift+Insert
Bold Control+B
Italic Control+I
Select All Control+A
Select to page up Shift+Page Up
Select to page down Shift+Page Down
Select line up/down Shift+Up/Down
Move to start of line Home
Move to end of line End
Select to start of line Shift+Home
Select to end of line Shift+End
Go to previous/next paragraph Control+Up/Down
Go to next/previous word Control+Right/Left
Delete word left Control+Backspace
Delete word right Control+Delete
Select character left/right Shift+Left/Right
Find and Replace Control+F
Find next/find again F3
Replace Control+H
Copy HTML (in Design view) Control+Shift+C
Paste HTML (in Design view) Control+Shift+V
Preferences Control
Switch to Design view Control+’
Print Code Control+P
Validate markup Shift+F6
Open Quick Tag Editor Control+T
Open Snippets panel Shift+F9
Show Code Hints Control+Spacebar
Indent Code Control+Shift+>
Outdent Code Control+Shift+<
Insert tag Control+E
Select parent tag Control+[
Select child Control+]
Balance Braces Control+’
Toggle breakpoint Control+Alt+B
Go to line Control+G
Move to top of code Control+Home
Move to end of code Control+End
Select to top of code Control+Shift+Home
Select to end of code Control+Shift+En

Text Editing

Action Shortcut
Create a new paragraph Enter
Insert a line break <BR> Shift+Enter
Insert a nonbreaking space Control+Shift+Spacebar
Move text or object to another place in the page Drag selected item to new location
Copy text or object to another place in the page Control-drag selected item to new location
Select a word Double-click
Add selected items to library Control+Shift+B
Open and close the Property inspector Control+Shift+J
Check spelling Shift+F7

Formatting Code

Action Shortcut
Indent Control+Alt+]
Outdent Control+Alt+[
Format > None Control+0 (zero)
Paragraph Format Control+Shift+P
Apply Headings 1 through 6 to a paragraph Control+1 through 6
Align > Left/Center/ Right/Justify Control+Alt+Shift+L/C/R/J
Edit Style Sheet Control+Shift+

Managing Hyperlinks

Action Shortcut
Check links sitewide Control+F8
Check selected links Shift+F8
Create hyperlink (select text, image, or object) Control+L
Remove hyperlink Control+Shift+

Site Management and FPT

Action Shortcut
Connect/Disconnect Control+Alt+Shift +F5
Refresh F5
Create new file Control+Shift+N
Create new folder Control+Alt+Shift+N
Open selection Control+Shift+Alt+O
Delete file Control+X
Copy file Control+C
Paste file Control+V
Duplicate file Control+D
Rename file F2
Get selected files or folders from remote site Control+Shift+D
Put selected files or folders to remote site Control+Shift+U
Check out Control+Alt+Shift+D
Check in Control+Alt+Shift+U
Put File Ctrl+Shift+U
Refresh Local pane Shift+F5
Refresh Remote pane Alt+F5

Opening and Closing Panels

Action Shortcut
Insert bar Control+F2
Properties Control+F3
Answers Alt+F1
CSS Styles Shift+F11
HTML Styles Control+F11
Behaviors Shift+F3
Tag Inspector F9
Snippets Shift+F9
Reference Shift+F1
Databases Control+Shift+F10
Bindings Control+F10
Server Behaviors Control+F9
Components Control+F7
Site F8
Assets F11
Results > Search Control+Shift+F
Results > Validation Control+Shift+F7
Results > Target Browser Check Control+Shift+F8
Results > Link Checker Control+Shift+F9
Results > Site Reports Control+Shift+F11
Results > FTP Log Control+Shift+F12
Results > Server Debug Control+Shift+F5
Others > Code inspector F10
Others > Frames Shift+F2
Others > History Shift+F10
Others > Layers F2
Others > Sitespring F7
Others > Timelines Alt+F9
Show/Hide panels F4

Something to take away…

You may want to take this away with you so feel free to download our PDF version.
Dreamweaver 8 Shortcuts PDF Download

Dreamweaver Code Hints

Friday, December 7th, 2007

As I’m sure a lot of people are aware, Macromedia’s Dreamweaver helps us all create rich web media quickly and easily, I would like to point out that I’m not referring to Dreamweaver’s built in WYSIWYG editor but rather some of the built in tricks that enable you to generate HTML and CSS with a few key presses.

The main feature that allows for rapid code generation is “Code Hints”, but quite often it can be a hinderance!

Here is how to customise your code hints to allow for a more powerful and graceful development platform:

Step 1: Find, Replace and Edit

First you need to take a copy of the old file, assuming your install is default you will find the code hints file in the following location.

C:\Program Files\Macromedia\Dreamweaver 8\Configuration\CodeHints\

Copy the “CodeHints.xml” and rename to “CodeHints.bkp” Dreamweaver will list any XML files that are in this directory so renaming without changing the extension is not adequate.

Now edit the origional file in a code editor (we choose to use Dreamweaver :P )

Step 2: The Padding Fix

When editing CSS in dreamweaver if you select the “padding” attribute you will notice the code hint “inherit” pop’s up. Because this is the only attribute defined its automatically selected meaning when you hit any key it pre-fills that value to screen. This is VERY aggravating when trying to write nicely tabbed code so here’s the fix.

Find the line starting with:

<menu pattern="padding:" additionaldismisschars=";:" allowwhitespaceprefix="true"...

Within this XML tag there is the following child element

<menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />

Before this tag add the following:

<menuitem label="auto" value="auto" icon="shared/mm/images/hintMisc.gif" />

This will mean you now have two values for this “Padding” definition and will prevent the issue mentioned earlier. You should also apply this fix to “padding-left” padding-right” padding-top” and “padding-bottom”. This will result in the following code:

  <menu pattern="padding:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css" allowmultiplevalues="true" description="padding-top || padding-right || padding-bottom || padding-left">
    <menuitem label="auto" value="auto" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
  </menu>
  <menu pattern="padding-bottom:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">
    <menuitem label="auto" value="auto" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
  </menu>
  <menu pattern="padding-left:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">
    <menuitem label="auto" value="auto" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
  </menu>
  <menu pattern="padding-right:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">
    <menuitem label="auto" value="auto" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
  </menu>
  <menu pattern="padding-top:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">
    <menuitem label="auto" value="auto" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
  </menu>

Step 3: Ultra Quick Testing Borders

Most CSS developers find it handy to apply a border to elements from time to time to allow them to debug layouts or check specificity. The idea is that when you type “border:” the first code hint that appears is “debug” when you select this attribute Dreamweaver will automatically place in “1px solid red;” making fast work of debugging, to get this working you need to do the following:

Find the line starting with:

<menu pattern="border:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css" allowmultiplevalues="true"...

Within this XML tag insert the following tag (before everything else):

<menuitem label="debug" value="1px solid red;" description="debug" icon="shared/mm/images/hintMisc.gif" />

Once this is complete you will be left with something that looks like the following:

  <menu pattern="border:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css" allowmultiplevalues="true" description="border-width || border-style || color">
    <menuitem label="debug" value="1px solid red;" description="debug" icon="shared/mm/images/hintMisc.gif" />
    <menuitem MMString:label="code_hints/pick_color" value="mm_menu_item:colorPicker" description="border-color" icon="shared/mm/images/hintColor.gif" />
    <menuitem label="medium" value="medium" description="border-width" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="thin" value="thin" description="border-width" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="thick" value="thick" description="border-width" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="dashed" value="dashed" description="border-style" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="dotted" value="dotted" description="border-style" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="double" value="double" description="border-style" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="groove" value="groove" description="border-style" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="hidden" value="hidden" description="border-style" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="inset" value="inset" description="border-style" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="none" value="none" description="border-style" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="outset" value="outset" description="border-style" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="ridge" value="ridge" description="border-style" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="solid" value="solid" description="border-style" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
  </menu>

Step 4: Where’s the Zoom?

The “zoom:1″ CSS statement is often used as an IE6/7 specific clear fix. It’s not an official W3C supported property and isn’t in the Dreamweaver code hints list…..so lets add it.

First find the following:

  <menu pattern="z-index:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">
    <menuitem label="auto" value="auto" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
  </menu>

Under this XML tag paste this:

  <menu pattern="zoom:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">
    <menuitem label="1" value="1" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
  </menu>

Thus leaving you with:

  <menu pattern="z-index:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">
    <menuitem label="auto" value="auto" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
  </menu>

  <menu pattern="zoom:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">
    <menuitem label="1" value="1" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
  </menu>

Step 5: Min Height and Width

As in Step 2, you may notice that when using “min-height” or “min-width” Dreamweaver will default to the value of “inherit” (same goes for the “max” versions). To fix this find the following code:

  <menu pattern="min-height:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
  </menu>

  <menu pattern="min-width:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
  </menu>

And replace with

  <menu pattern="min-height:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="auto" value="auto" icon="shared/mm/images/hintMisc.gif" />
  </menu>

  <menu pattern="min-width:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">
    <menuitem label="inherit" value="inherit" icon="shared/mm/images/hintMisc.gif" />
    <menuitem label="auto" value="auto" icon="shared/mm/images/hintMisc.gif" />
  </menu>

Step 6: Goodbye colour pallete

Something else I find a little annoying is the colour pallete popup which appears when you tab or press space after typing “color:” or any other attribute with “color” at the end. To remove this functionality for the “color” attribute you just search for the following code:

<menu pattern="color:" type="color" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">

And remove the “type” attribute and it’s value completely, leaving you with:

<menu pattern="color:" additionaldismisschars=";:" allowwhitespaceprefix="true" displayrestriction="css">

This will leave you with a list of a few standard colours which is acceptable to me, you might want to alter this further though.

And you’re done!

Well you will be pleased to know that we’re done, simply restart Dreamweaver and give it a bash! If you have any problems or questions please get in touch!

If you have any suggestions on other code hints please let us know!

Minify your Javascript with JSMin

Thursday, December 6th, 2007

Using Ryan Grove’s PHP port of Douglas Crockford’s JSMin filter we’ve hosted an interface allowing you to upload your javascript file and get the minified version returned via a textarea or file download on submission. To take a sentence from Douglas’ site: “It typically reduces filesize by half, resulting in faster downloads. It also encourages a more expressive programming style because it eliminates the download cost of clean, literate self-documentation.”

Try the tool: http://www.digitaloverload.co.uk/jsmin/

Image Rollovers with JQuery

Wednesday, December 5th, 2007

First apply a class of “rollover” to the image you want to have a rollover, also make sure you have 2 images states e.g.

button.gif and button-active.gif (use this naming convention)

Then make sure JQuery is present and add the following to your main javascript file

$(".rollover").hover(
	function(){
		if($(this).attr("src").indexOf("-active") == -1) {
			var newSrc = $(this).attr("src").replace(".gif","-active.gif#hover");
			$(this).attr("src",newSrc);
		}
	},
	function(){
		if($(this).attr("src").indexOf("-active.gif#hover") != -1) {
			var oldSrc = $(this).attr("src").replace("-active.gif#hover",".gif");
			$(this).attr("src",oldSrc);
		}
	}
);

Here is what your image should look like:

<img src="/media/images/button.gif" width="100" height="20" alt="My Button" class="rollover" />

Hope this helps, if you have any questions or suggestions please get in touch.