Wobbly Tech Tip #2: Running a function on just the dragged element of a script.aculo.us sortable

I recently implemented a change to my Track-Blaster software so that DJs can rearrange songs in a playlist by dragging and dropping them from one place to another. To do this, I incorporate the wonderful Scriptaculous javascript library.

I’m not going to give a tutorial of how this effect is achieved. If you want that, there’s a great one here. The reason for this post is to share a solution to a specific problem I was having using these “sortables”.

The problem is this: I drag an element within a sortable. Sometimes I drag it to a different position and sometimes I don’t. I want to run different functions on the dragged element depending on whether it has moved. There are functions included in the library that are useful — Sortable.serialize and Sortable.sequence — but they return a string or array containing the new order of all the elements. I am only interested in the element I’m dragging.

Sortable.create has a callback, OnUpdate, that’s triggered only when an element is moved to a different spot. However, it doesn’t know which element was dragged; the entire list is its parameter. Conversely, there is an option called reverteffect that knows which element was dragged, but it doesn’t know whether it’s gone to a different position or stayed put.

Conveniently, OnUpdate is called before reverteffect. My solution involves using OnUpdate to add a temporary class to the list container. Then, reverteffect is used to test whether that class exists. If it does, the class is removed and a function is called on the moved element. If it doesn’t, a different function can be called on the dragged-but-not-moved element. Here’s a sample page that shows you what I’m talking about. Put your mouse over the up/down arrows and drag that item to somewhere else within the list. Or put it back where you found it. After you’ve dropped it, a message will appear at the bottom identifying the item you just dragged and indicating whether it was moved.

You can examine the source code of this sample page, but here’s some annotations of the important bits. I’m assuming you’re familiar with the fabulous Prototype javascript library. Let’s start at the bottom:


Sortable.create('testlist',{
  constraint: false,tag: 'div', handle: 'moveme',
  reverteffect: moveRevertEffect,
  onUpdate: function(element) {  
    element.addClassName('sortUpdate');
  }
});

This script activates the sortable. The first parameter, “testlist” is the id of the object containing all the things you’ll be dragging. The next parameter is a hash of the various sortable options. The relevant ones to this post are the last two. “reverteffect: moveRevertEffect” replaces the standard Scriptaculous reverteffect with a custom one we’re arbitrarily calling moveRevertEffect. More on this below. “onUpdate: function(element)…” does what I said earlier. onUpdate is only called after you drop an element in a different spot from where it started. If it is called, the class “sortUpdate” (another arbitrary name) is added to the parent container (i.e. testlist).

After an element is dropped, and after onUpdate is called (if it is called), the reverteffect kicks in. Let’s look at that code (from near the top of the sample page):


var moveRevertEffect = 
function(element, top_offset, left_offset) {
  var dur = Math.sqrt(Math.abs(top_offset^2) + 
    Math.abs(left_offset^2))*0.02;
  new Effect.Move(element, { 
    x: -left_offset, y: -top_offset, duration: dur,
    queue: {scope:'_draggable', position:'end'}
  });  
  if (element.up().hasClassName('sortUpdate')) {
    element.up().removeClassName('sortUpdate');
    $('whahappened').innerHTML = element.id + ' has been moved';
  } else {
    $('whahappened').innerHTML = element.id + ' has not moved';
  }
}

This custom script creates the new variable that will replace the standard reverteffect. We want to keep the standard effect but then add to it. So the first part of the custom script (the dur and Effect.Move statements) is simply a copy of the standard version found deep in the bowels of Scriptaculous (in the file dragdrop.js). The if statement is our addition. It checks to see if the parent of the dragged element (i.e. testlist) has the class “sortUpdate”. If it does, that means the element was moved. The class name is removed and we can run whatever function we like using the dragged element as an argument. If the parent doesn’t have the “sortUpdate” class, that means the element was dropped off where it started. The else clause kicks in and we can run some other function using the dragged element as an argument. In the example, I use a (very) simple function that changes the innerHTML property of the div at the bottom, but you can put anything here including ajax calls or whatever.

And that’s it. A rather esoteric problem to be sure, but hopefully someone, somewhere, sometime, will find this useful.

Be Sociable, Share!

Wobbly Tech Tip #1: Extracting Audio from a CD’s “Track 0”

This past week, Dirty Three were my pick for Breakfast of Champions Band of the Week. I thought it would be fun to play a rare pair of songs by them — songs that appeared as secret bonus tracks of the album Songs In The Key Of X: Music From And Inspired By The X-Files. Here’s the deal: If you rewind the CD from the start of track 1, you’ll find that there’s an additional 9+ minutes of music hidden away. The first song has Nick Cave reciting some gothy spoken word piece over a reworked version of Dirty Three’s song “Better Go Home Now” that they’ve retitled “Time Jesum Transeuntum Et Non Riverentum” (Dread the Passage of Jesus, For He Will Not Return). The second is Dirty Three’s interpretation of the X-Files main theme.

While I could have simply rewound the CD in the studio as I was about to play it, I wondered if it would be possible to extract the audio from the original CD and burn a new one where the hidden songs showed up as normal tracks. My existing software was of no help, and I’ve got some muscular audio programs: Pro Tools, Sound Forge, etc. After some mad Thursday night googling, I was able to piece together the following method.

  1. Download and install cdrdao. It’s open source software, so if you’re a UNIX geek, you can compile it yourself. I use Windows, so I downloaded a pre-compiled binary. All the following steps assume you’re also using windows.
  2. Pop your X-Files CD into your CD drive.
  3. Open a command window and navigate to the folder that contains the cdrdao program files.
  4. Run the following from the command prompt:

    cdrdao read-cd --driver generic-mmc-raw --read-subchan rw_raw xfiles.toc

    If all goes well, this will create two new files in the cdrdao folder. The first, data.bin, contains extracted audio from the entire CD. The second, xfiles.toc, contains information on when the individual tracks start and stop.

  5. Open the file xfiles.toc in notepad, or your favorite text editor. At the bottom of the section for Track 1, you’ll see a line that reads “START 09:12:25”. Change this to “START 00:00:00” and save the file.
  6. Now put a blank CD in your CD-R drive and run the following from the same command prompt as before:

    cdrdao write --speed 16 xfiles.toc

    If you want to live dangerously, you can leave out the “–speed 16” part. This will allow your CD burner to run at full speed. When I tried this, I got a buffer underrun. Slowing it down to 16x with the extra parameter gave me a successful burn.

  7. Now you have a CD where the two hidden tracks along with the original proper first track are all bunched together on track 1. At this point, you can use almost any audio software to extract and edit track 1 to grab the two hidden tracks. Ta-Da!

Note: Further googling has revealed that this might be more easily done with the program EAC.

Be Sociable, Share!