Complete Example of Tracking Start and End of Drag Operations with jQuery and Table Rows

After writing my previous post about “Keeping track of indexes while dragging in jQuery UI Sortable” I received a question about how this would work for sortable table rows. The code I included in the previous post should actually be enough to make everything work (.. as long as I understood the question correctly). I’ve created a minimal example as a complete implementation of the code from the previous post, and I’ve also included it as a live demo here.

This is based on jQuery 1.5.1 and jQuery UI 1.8.13. I’ve simply used the distribution versions of this example (you probably want to build your own UI bundle using the interface on the jQuery UI website).

Here’s a live demo that you can play with.

Inline javascript and WordPress could possibly be better friends.

The complete source for the minimal (but complete) example is included here:



    
        
        jQuery UI Example Page
        
        
        
        
    
    
        
Dragging from index
None
Hovering index
None
Keys Values
Key 1 Value 1
Key 2 Value 2
Key 3 Value 3
Key 4 Value 4
Key 5 Value 5
Key 6 Value 6

Relevant Meta Tags for Facebook Share

I spent the evening making sure the different pages on Gamer.no gave relevant titles and descriptions when shared on Facebook. The implementation was quite straight forward, but finding the finding the actual documentation for which elements Facebook supports ate a bit of development time. After navigating through four or five wiki pages at the development wiki describing various parts of the Facebook Share system, I finally found the page getting down to the metal about which elements you should include.

The page can be found at Facebook Share – Specifying Meta Tags.

We’ve currently implemented title, description and medium. We also had image_src, but decided against it at the moment – the first feedback we reserved made it clear people preferred to select their own image. This may however be because of the first batch of people being a bit too technical competent, so we’ll probably use image_src later (.. does Facebook support providing several images through image_src?).

Table 100% Width and Margins

While hacking aboooooot today I found the need for making a table behave like a regular block element. I have a section floated to the right of a table, and the table should occupy the rest of the available spot. Making the table’s width 100% would make it adjust it size according to its parent instead of the available place (.. with margin-right set to the width of the other element + whitespace).

The best solution I’ve found so far is to create a wrapper div around the table, and then setting the table width to 100%. This makes the table adjust its size according to the parent – which now is a block element.




...

Ready for 2010: HTTP Headers and Client Side Caching

There’s a few easy changes you can do to your website setup to speed up content delivery and eat up less bandwidth: configure proper expire values and if possible, keep your static resources on a separate domain.

The HTTP Expires Header

Expires tells the client how long it can keep the current version of a resource as the most recent one. If you set the Expires-header a while into the future, the browser will not make a new request for the file until the resource, well, expires (depending on the cache settings for the browser, requesting a reload (such as shift-reloading in a browser), etc. which can expire the resource earlier). The potential problem is the case where a resource actually changes, such as deploying a change to your stylesheet or external javascript files.

The fix for this is to include something about the file which changes when the file is physically updated on the disk. This can be the last modified time (please keep this cached in your web application, you do not want to hit the disk to retrieve the value for each page view), the current revision number from your revision control system (such as SVN – you can get the current revision of a file by using svn info, and please, cache that value to. You do not want to call svn for each page view :-)) or something else, such as the md5 or crc32 hash of the file. The important part is that you include this value as part of the request, making the URL to the resource unique depending on the version of the resource. You can safely ignore this part of the URL in your rewrite / controller routing magic / handling application, as the only function it has is to tell the browser that it has to request a new file and not use the old one anymore.

Examples of URL-schemes To Get Around Expires:-headers

  1. flickr uses as simple .v in their URLs to indicate the version of the file: http://l.yimg.com/g/css/c_sets.css.v74709.14
  2. On Gamer.no we use the current SVN revision: /css/main.css?v=1120M
  3. vg.no uses the current date, followed with an identifier that probably indicates the current revision for that day: css/frontpage.css?20091203-1

It’s important to remember that the identifier is not used to deliver an older version of the file depending on the parameter, just to make the browser see the new resource. The old URL can still serve the new resource – and if you need to keep old versions around, you’ve probably solved this issue already.

Use a Separate Domain for Static Resources

By using another, separate domain for your static resources, you’re letting browsers fetch the static resources while they’re still processing your HTML. The HTTP/1.1 specification says that browsers never should request more than two files at the same time from the same domain. When you host your static resources on another domain, you tell the browser that it can go ahead and fetch those resources while being busy with downloading other items from your main site.

After you’ve moved your static resources to a separate domain, you’ll usually also end up using less bandwidth. Since you’re now delivering the most requested content from another host, cookies will not be included in the request from the browser. When a browser makes a request for a resource on a certain host, it includes all the cookies that have been set for that domain. This happens independent of which files it’s requesting, and if you have a large number of separate files (which you probably could include into one larger file – resulting in fewer HTTP requests), these Cookie-headers can add up to a significant amount of bandwidth. The HTTP server will also have less work to do, making everyone happier!

If you use www. as a prefix for all your regular HTTP requests and take care of setting your cookies in the www.example.com domain, you should be able to simply use something like static.example.com for your static content and avoid leaking cookies into the other subdomain. If you have loads of static content, you can also use several separate subdomains for your files, but be sure to let the request for a certain file point to the same subdomain each time – otherwise you’ll end up with the browser requesting four copies of the same, identical file and actually breaking the regular cache in the browser (which uses If-Modified-Since to tell the server when it last downloaded the file. We want to avoid the browser making the request again at all). At pwned.no I calculate the crc32 of the filename and use that value to determine which static host the request should use. We also redirect any requests directly to pwned.no to www.pwned.no to make the cookie structure consistent. We do however not set the Expires-header yet, but that might be a part of the next update to the site.

Do you have a particular caching strategy you use for client side content? What kind of URL format works best for you? Leave a comment!

Read all the articles in the Ready for 2010-series

A Plugin for Paginating in Smarty

First I’d like to apologize for the lack of updates here in the last weeks, but the days have been very busy. I’ve bought a new car (more details about that as soon as the snow disappears), written a complete publishing platform from scratch in a weekend to help out when Gamer.no got in trouble and in general done a load of stuff. Anyways, this post isn’t about all that, but rather something else I wrote some time ago.

A use case you’ll encounter very often is the act of paginating items, i.e. including a simple “jump to page x, jump to the next page, jump to the previous page” footer. If you’ve ever tried to implement the logic around this in your view, you know that it can get quite extensive. You have several other solutions, such as the PEAR_Pager, which actually looks like a good solution now (with 2.x). Anyways, this is a plugin for Smarty to make generating pagination links easier.

Download the plugin, drop it into your plugins/ folder in your Smarty library directory, and voilá, you have access to the new {paginator} element.

The module is quite configurable, but as I’ve only extended the parts I’ve had use for it our projects, it may still lack a few simple keys.

{paginator hits=$hits offset=$offset total_hits=$articlesFound class_inactive=paginatorInactive class_active=paginatorActive}

The template variables used here are hits, the number of hits shown on this page, offset, the offset from 0 and total_hits, the total number of available hits in the current list. By default the plugin appends ?hits=<hits>&offset=<offset>+<hits> to the current URL, you can give another URL through the url attribute. The class_ attributes provide the CSS classes to use for the elements that enclose the page numbers or links. See the source code (!) for more information about attributes which work.

As I mentioned previously, the plugin is written for my own personal use, so it’s not as streamlined as it could be. Feel free to update it, dissect it, break it, claim it’s yours .. or anything. I’d be happy if you submit any patches to me so I can update the link here, or simply leave a comment.

Hack aways! You can see the plugin in action at the bottom of lovethatfun.com.

Upgrading to SWFUpload 2.2.0-beta

SWFUpload 2.2.0-beta has been released to fix the issue that has occured after the release of Flash 10, where regular SWFUpload-based applications ceases to work. This is because of a security update to Flash Player, where the plugin refuses to show the upload files dialog unless the plugin has focus — and the request to show the dialog is in response to an user event. This means that the user now has to click somewhere in the flash file before we’re able to show the upload dialog.

The way most file upload components solve this is by overlaying a transparent flash file over the regular user interface element, so that the user clicks the flash file instead of the visible HTML element. SWFUpload 2.2.0 supports this, in addition to creating a styled flash based button instead of the regular HTML elements.

To upgrade from our previous 2.1.0 based installation, I did the following:

Added in the settings object for SWFUpload:

        button_placeholder_id : "selectFilesButtonPlaceholder",
        button_disable : false,
    	minimum_flash_version : "9.0.28",
    	swfupload_pre_load_handler : swfUploadPreLoad,
    	swfupload_load_failed_handler : swfUploadLoadFailed

Swapped out the previous references to graceful degradation and SWFUpload 2.1.0:



Remembered to update flash_url in the settings object:

flash_url : "/flash/swfupload_f10.swf",

Added three new callbacks functions instead of using the references to the previous degradation elements (swfupload_element_id, degraded_element_id):

function swfUploadLoaded()
{
    // set the dimensions of the button to match the outer container element (jQuery!)
    swfu.setButtonDimensions($("#buttonContainer").width(), $("#buttonContainer").height());
}

function swfUploadPreLoad()
{
    // hide HTML interface, show Flash interface..
    $("#regularUploader").hide();
    $("#flashUploader").show();
}

function swfUploadLoadFailed()
{
}

Added a container element in the HTML that the Flash app will simply take control over (it will NOT lay itself transparent over this element!):

.. and remove the old reference to swfu.selectFiles();.

That should be all.