The best media player money can't buy
It turns out that for my needs, the best x-format media player out there right now is the command-line version of MPlayer. It plays anything, is infinitely customisable, has proper support for subtitles (with live interactive retiming!) and most importantly, allows me to easily create the perfect UI for it, tailored exactly to my needs. In most cases this means "play this file" or "play this file and use this other file for the subtitles".

So I created an Automator action (just unpack to ~/Library/Workflows/Applications/Finder) which, after a "Get Selected Finder Items", runs this little AppleScript:
on run {input, parameters}
set args to ""

if length of input > 1 then
repeat with i in input
if name extension of i is in {"avi", "mp4", "ogg", "divx", "mkv"} then
set video to i as string
else
set subs to quoted form of POSIX path of (i as string)
end if
end repeat

set args to " -sub " & subs & " -subfont-text-scale 4"
else
set video to (input as string)
end if

set video to quoted form of POSIX path of video
do shell script "/usr/local/bin/mplayer -framedrop -mc 0.001 -af volnorm=2 " & video & args & " &> /dev/null &"
return true
end run

Now I simply select the file(s) in the Finder and Ctrl-click > Automator > Play in MPlayer.
|
Detecting RSS readers
I find it strange that there's no (AFAIK) standard way to differentiate between a regular web browser and an RSS aggregator. Ideally, you'd think that the client requests a certain mimetype so your webapp could make some decisions. This isn't much of an issue in most cases, since you just provide an alternate link the the RSS feed, but I ran into a problem with authentication.

I wanted RSS users to be able to authenticate using standard HTTP methods, while still giving browser users a nice login window through which they could also reset the password or whatever. The best solution I could come up with thus far is this crude little test that identifies every major browser:
$is_browser = preg_match ("/(^Mozilla|^MSIE|^Opera).*/", $_SERVER['HTTP_USER_AGENT']);

|
Cleaning up your Podcasts
I was running out of HD space again and for the heck of it checked how much space my downloaded podcasts were taking up - 1.9GB. This wasn't that surprising, but I did notice there were media files of podcasts lying around that I had unsubscribed from a looong time ago. So I needed something that would do two things:

a) Delete all the podcasts I had already listened to
b) Go through all the residue in the Podcast folder and delete whatever I had already unsubcribed from.

I subscribe to 14 shows so this wouldn't have been much of an issue to do by hand, but I decided to use AppleScript. Here's the script that does all that:
set podcastFolder to alias "hd:Users:filipp:Music:iTunes:iTunes Music:Podcasts:"
-- first get rid of played podcasts
tell application "iTunes"
set deleteThese to every file track of playlist "Podcasts" whose unplayed is false
set dontDelete to location of every file track of playlist "Podcasts" whose unplayed is true as list
repeat with thisPodcast in deleteThese
set thePath to (location of thisPodcast as string)
delete thisPodcast
tell application "Finder"
move item thePath to the trash
end tell
end repeat
end tell
-- now comb the fs for any rogue shows
tell application "Finder"
repeat with something in list folder podcastFolder without invisibles
set fullpath to (podcastFolder as string) & something
if dontDelete as string does not contain fullpath then
move item fullpath to the trash
end if
end repeat
end tell

It's not particularly clever or terse and turned out to be quite the overkill, but it works - my Podcasts take up 56 MB now. Plus I can safely check out all the shows I want and not worry about getting rid of them later (just drop the script into ~/Library/Scripts/iTunes). ;-) This could also be easaily improved to, for example, disregard shows that are unselected (if you have a played show that you really want to keep).

If you ask me, iTunes should just re-evaluate your "Keep" preference for podcasts every time you hit "Refresh". That way you could clean things up anytime you like, without resorting to this kind of "violence".
|
The LEFT OUTER JOIN
So let's say you're fetching posts from your blog app and also want to show the comment count for every post. You can't use a typical JOIN because some posts may not have any comments at all and doing a separate query in the comments table is just plain wasteful. Use a LEFT OUTER JOIN instead:
SELECT *, COUNT(comments.id) AS comment_count
FROM posts LEFT OUTER JOIN comments ON (posts.id = comments.post_id)
GROUP BY posts.id
ORDER BY date_added DESC
LIMIT 0, 20

|
A simple build system
Nowadays it seems that whenever you're working on something, you, at some point, have to start releasing stuff. Typically you can't just take the directory of your app, compress it and upload somewhere. And even if you could, doing it manually would be extremely munday at best.

In a typical webapp project, creating a "build" means: a) cleaning up our configuration of any sensitive information b) removing any data that should not be included in the distribution c) archiving and compressing the build d) id each archive (typically using a timestamp) the build e) uploading the archive somewhere.

Looking back at my previous projects, I think that those steps are pretty universal. So what I want is actually a tool that would do all of those for us. Something non-interactive that we could could be run both on-demand or as part of some scheduling system.
And I'm convinced there are a lot of fancy solutions out there. SVN would probably work (with possibly a bit of hand-coding added), so would probably Ant and a gazillion other SCM systems. Or we could just code one ourselves with a little bit of BASH scripting. This solution is neither particularly elegant nor very easily reusable, but it has worked very well for my current project and handles all the steps I mentioned before. And it doubles very nicely as a little backup scheme.

archive=`date +%y%m%d-%H%M-collective`.tar
config='./collective-build/lib/collective.yaml'

echo -n " > Copying..."; rsync -a ./collective/* ./collective-build/ --exclude "data/*/*" && echo " [OK]" && \
echo -n " > Cleaning config..."
sed -e 's/username: [a-z]*/username: username/' -e 's/password: [a-zA-Z]*/password: password/' $config > config.tmp && \
mv config.tmp $config && echo " [OK]"
echo -n " > Creating archive ..."
tar -cf $archive ./collective-build/ && gzip -c $archive > $archive.gz && echo " [OK]"
echo -n " > Uploading $archive.gz..."
curl -u user:pass -T $archive.gz 'ftp://server.com/' && echo " [OK]"
echo -n " > Cleaning up..."
rm -r $archive $archive.gz ./collective-build && echo " [OK]"
exit 0


I chose that timestamp format because it gives me nice granularity, at a stage when fixes and maybe even new features might be added every hour, I can just run this at any time. And the nice thing about this also that it's very flexible, for example the DB schema might be very important in some other project and so you could just drop a mysqldump in there too.
|
Audio playback with Python
I was really surprised there's seemingly no way to do cross-platform sound output with Python. Looks like you can definitely do it under Windows, and maybe even on Linux, but I couldn't find any info about OS X. There's the rather cryptic mention of Carbon.Snd Sound Manager in the Python docs but I honestly have no idea what that is or how to use it.

Anyways, since I (and probably any other Python user on OS X) had PyObjc installed, I decided to use Cocoa to do my sound playback. It turned out to be much easier than I anticipated:
import objc
from AppKit import NSSound

click = NSSound.alloc().initWithContentsOfFile_byReference_("click.wav", True)
click.play()

This is for a metronome app and is less than optimal because the wave file can't be played back fast enough (100 and 200 BPM sound the same), but it's a start! More info on using NSSound here.
|
Checking for existance
There's no built-in for checking if a file exists when developing Dashboard Widgets in JavaScript. But with a little help from the BSD subsystem we can do it easily. For this to work, you obviously need to have
<key>AllowSystem</key>
<true/>

set in your Info.plist. That opens up a whole world of possibilities. I find that the most effective way is to use file. This way we can even check if something is of the correct type. For example, for a recent UnitInfo update, I had to check if InCrease was installed:
foldingBase = "~/Library/InCrease/cpu1/";        
var fp = widget.system ('/usr/bin/file -bi ' + foldingBase + 'unitinfo.txt', null).outputString.replace ("\n", "");

if (fp.indexOf ("text/plain") != -1)
{
clientVersion = 'InCrease';
} else {
foldingBase = "~/Library/Folding@Home/";
clientVersion = 'Folding@Home';
}

unitInfoFile = foldingBase + 'unitinfo.txt';
clientConfigFile = foldingBase + 'client.cfg';

|
Data URL-s
I had known about this before, but never really tried them until yesterday. I needed a really quick way to post photos from my phone, so decided to write a little photogallery app. You just dump pictures off the phone to a certain directory and that's it. In the process of reading about imagecopyresized, I stumbled on this method of embedding images:
<img src='data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD' alt='Picture' />

That's called a data URL and they were first introduced in 1998.

In PHP, I couldn't find a way to read the data directly with imagejpeg(), so I end up overwriting 1 thumbnail for a whole directory of images. Working with images, the PHP memory limitations will quickly come into play. Anyways, here's the whole PHP code that goes through a directory and creates 80x80 px thumnails of a random portion of the picture. You can also see it in action. The source for that script is here.

$id = 'data/';
$dh = opendir($id);

while ($f = readdir($dh))
{
$fp = $id . $f;
if (is_file($fp) && $f != 'tmp')
{
list($w, $h) = getimagesize($fp);
$scale = 0.6;
$ns = 80;
$im = imagecreatetruecolor($ns, $ns);
imagecopyresized($im, imagecreatefromjpeg($fp), 0, 0,
rand(0, $w*$scale), rand(0, $h*$scale), $w*$scale, $h*$scale, $w, $h);
imagejpeg($im, 'data/tmp');
echo "
<a href='javascript:show(\"$fp\",$w,$h)' title='" . date('r', filectime($fp)) . "'>
<img src='data:image/jpg;base64," . base64_encode(file_get_contents('data/tmp')) . "' />
</a>";
}
}

|
with(something)
I discovered a nice little shortcut for setting element properties in JavaScroüt. Instead of saying:
nameDiv = document.createElement('div')
nameDiv.className = 'nameDiv';
nameDiv.innerText = showName;
nameDiv.title = showName;

You can "zoom into" the element:
with (nameDiv = document.createElement('div'))
{
className = 'nameDiv';
innerText = showName;
title = showName;
}

|
XML stuff
A (weird) way to convert PHP arrays to XML. I came up with this for Collective - passing media metadata (with getid3) from the server to JavaScript. It's not supposed to work with numeric indexes - the algorithm doesn't break, it just produces invalid XML (can't have numbers as element names):
function array2xml($array)
{
foreach ($array as $k => $v)
{
if (is_array($v))
$out .= "<$k " . array2xml($v) . "</$k>\n";
else {
$out .= "\n\t$k='$v'";
if (is_array(next($array)) || (array_pop(array_keys($array)) == $k))
$out .= ">\n";
}
}
return $out;
}

A way to convert a file system hierarchy into an XML document, in Python. This is for a simple file-cataloguer I desperately needed (more on this later, hopefully):
imp = minidom.getDOMImplementation()
ldoc = imp.createDocument(None, 'Library', None)

def parsedir(dirname, parent = ldoc.documentElement):
global ldoc
if os.path.isdir(dirname):
newDir = ldoc.createElement('Directory')
newDir.setAttribute('path', dirname)
parent.appendChild(newDir)
parent = newDir
for i in os.listdir(dirname):
fp = os.path.join(dirname, i)
if os.path.isdir(fp):
parsedir(fp, newDir)
else:
newFile = ldoc.createElement('File')
newFile.setAttribute('name', i)
parent.appendChild(newFile)

|
Punk 1.0.6
Released yesterday. The version number is completely made up. Supports the new IMDb layout and is rewritten to use XMLHttpRequest which should also work through proxies by using the system settings. XMLHttpRequest seems to also give you caching for free (at least it creates cruft in the Cache folder).

This is probably the most succesful thing I've ever made (thanks largely to Martin's nice UI) and this version even got some comments from actual users! 140 downloads in less than 24 hours is not exactly VLC rate, but makes me happy nevertheless. Apple actually featured it again in the Widgets section which always gives you that nice tingly feeling. :)

It's amazing how much energy some feedback gives to a freeware developer. If there's anyone reading this, I would encourage you to send even a small comment or thank-you to your favourite freeware developer(s). It makes all the difference in the world.
|
Scraping google.com
I don't know when this happened, but it seems you can't scrape (parse) google.com as easily as before. At least with Python, a simple
s=urllib.urlopen('http://www.google.com/search?q=define%3Aesoteric')
r=s.read()
print r

will give you this notice instead:

/snip/ Your client does not have permission to get URL /search?q=define%3Aesoteric from this server /snip/


And here's me not understanding why my regexes are not matching. :) Apparently, you should at least show some kind of User Agent. In Python you can do this easily by subclassing URLopener and setting it's version property to something, like:
class MyOpener(urllib.URLopener):
version = "InternetExploiter/666"
urllib._urlopener = MyOpener()


And I'm really starting to love TextMate. Just type your little Python script and hit Cmd-R. Brilliant.
|
Good Things pt 2: YAML
I got acquinted with YAML during my short run-in with Ruby on Rails (more on this some other day, hopefully). Their official description is:

YAML(tm) (rhymes with "camel") is a straightforward machine parsable data serialization format designed for human readability and interaction with scripting languages such as Perl and Python.


Absolutely brilliant stuff. What it gives you, is a simple (no joke!) portable data format that's truly human-readable (indentation, baby!) and can easily be parsed into virtually any programming language's native data structure.

For example, in PHP (via the excellent Spyc library), this is how my webapp's DB settings would look like:
database:
host: localhost
name: collective_development
username: name
password: passwd
type: mysql
charset: utf-8

Then you just do
include 'lib/spyc.php5';
$c = Spyc::YAMLLoad( 'lib/collective.yml' );

And your whole configuration is accessible in a PHP array:
$link = mysql_connect( $c['database']['host'], $c['database']['username'], $c['database']['password'] );

|
AppleScripting Keynote 3
And in particular the add chart command. At first it seems like a really cool thing - easily create beautiful charts out of virtually any source. I was excited to try this with some Webalizer output. Looks like you have two options - the Automator Action or Script Editor

The action produces a chart right off the bat, but the input is weird:

Input: (Anything) Two dimensional array of chart labels and data.


What is that? After trying every possible permutation of what I thought an AS 2D array would look like (some of which even compiled!) with different kinds of input sources (AS, text) I finally gave up the Automator Action option.

Using script editor seemed promising at first, but a simple add chart with all the properties produced nothing. Finally managed to find this really nice example, but it only worked with Pages. Digging in the action's bundle revealed that you're supposed to tell the slide to add the chart. OK, time to put this new-found knowledge to work:
tell application "Keynote"
set theData to {{1, 2, 3}, {4, 5, 6}}
set theSlide to (slide 1) of first slideshow
tell theSlide
add chart row names {"Dec", "Jan"} column names {"Machines", "Visits", "Hits"} ¬
data theData type "vertical_bar_3d" group by "column"
end tell
end tell

Pasted Graphic 2

Tadaa! Sweet. Now all that remains is to add the webalizer parsing code...

|
Building universal binaries
So far I've had the best success with defining the following before configure:
> export LDFLAGS="-Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386"
> export CFLAGS="-isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc"

Sometimes this lead to:
gcc: -E, -S, -save-temps and -M options are not allowed with multiple -arch flags

in which case passing --disable-dependency-tracking to configure seemed to help.
|
Remembering the QTVR Pan Angle
This only works with Safari:
QT_WriteOBJECT_XHTML (
'myVRmovie.mov', 800, 600, '',
'controller', 'true',
'id', 'myvrmovie',
'cache', 'true',
'pan', getAngle ());

... where getAngle() just uses a cookie:
function getAngle ()
{
return document.cookie.split ('=')[1];
}

... which is set with setAngle () like thus:
function setAngle ()
{
document.cookie = 'angle=' + Math.floor (document.myvrmovie.GetPanAngle ());
}

Finally, the hotspots are wired to go through this function:
function openPage (url)
{
setAngle ();
window.location (url);
}

|
Google search to RW (for lazy bums)
A really lazy way to add search to your RW page:
<div id="gsearch">
<form action="http://www.google.com/search">
<input type="text" name="q"/>
<input type="hidden" name="hl" value="en"/>
<input type="hidden" name="sitesearch" value="http://homepage.mac.com/filipp"/>
<input type="submit" value="Search with Google"/>
</form>
</div>


I think you could actually cook up something nice with a Google developer account and XMLHttpRequest...
|
Using script.aculo.us with XSLT
When using script.aculo.us with XSLT (XML to XHTML), don't include like this:
<script src="javascripts/scriptaculous.js" type="text/javascript">

This is because scriptaculous.js uses document.write () to include the components and that' apparently verboten when using XSL. The symptoms are weird too:
* Safari will acts as if all was OK, except Ajax.Request won't work
* Firefox/Gecko-based browsers will just hang on loading the document (an invinite "Reading") and you'll notice some class name related errors in the JS log.
In other words, the correct way to include, is by all the files separately and in the right order:
<script src="javascripts/effects.js" type="text/javascript"></script>
<script src="javascripts/builder.js" type="text/javascript"></script>
<script src="javascripts/dragdrop.js" type="text/javascript"></script>
...

|
Happy Holidays
The past few days have been weird. Got a really great idea for writing a hwmond replacement called servermond that would allow you to use at least some of the functionality of Server Monitor on any hardware. So far so not good. Getting the SSL decryption to work to reverse-engineer the Server Monitor protocol has been unsuccessful due to, quite frankly, lack of tools. ssldump won't compile straight, tried the macports version but that won't run. Finally managed to install ettercap (which seems like an awesome piece of engineering) but it hasn't helped me yet... This made me think why there's still no Mac native front end to any of these powerful free programs.

Plus, the cigarette addiction's kicking in again. I've been off it for about a year now but the past 5 days I've felt like I quit last weekend or something... Weird.

But to not feel totally unproductive, I've decided to roll out the first public version of Itch - my little Python script for XChat Aqua. Named thus not only because there were a few itches that I wanted to scratch in XChat, but also because it's my first ever Python script (and itchi means "one" in Japanese). Itch features:

  • Simple iTunes and QuickTime Player announcement. /np send nick will try and dcc the current tune to nick.

  • /away management (hitting /way toggles you away with a timer) with logging

  • Hardware (ioreg) and software (system_profiler) monitoring and specs. Just try /hw and /sp for the possible switches. These are not echoed to the channel.

  • iTalk. This was actually pea's brilliant idea. /isay will convert anything you say into "i-talk". iFor iExample iLike iThis.

  • Finally, /day will show you what's happened today in history (data from /usr/share/calendar/calendar.*)


There's a few other features that I want to add in there, but the existing ones were "stable" enough to release it into the general public. I consider it my little Christmas present to the Mac IRC population. ;-)
|
Modulo
This one is easy, but so important but used so rarely that you have time to forget it. How to build a table programmatically with a certain number of columns, with only one while - loop:
<table style="border: 1px solid black">
$i = 0;
$col = 4;
$val = 20;
$table = null;

while ($i < $val)
{
$i++;
$table .= '<tr>' + ($i % $col == 0) ? "<td>$i</td></tr>" : "<td>$i</td>";
}
print ($table);
?>
</table>

|
Variable Scopes
value = 0
def setValue():
value = 1
setValue ()
print value

What's that gonna print? That's right - 0. Must use global instead:
value = 0
def setValue():
global value
value = 1
setValue ()
print value

It's actually the exact same with PHP:
$value = 0;
function setValue () { $value = 1; }
setValue ();
print ("value: " . $value);

My brain tells me that if something's been defined previously, then we should be referencing that thing instead. It's not like I'm explicitly using different namespaces in - and outside my function. Like with JavaScript.
|
Two New Scripts
useradd.sh and freplace.sh. The first is a simple CLI utility for creating users and the second finds and replaces files.
> ./useradd.sh 
Usage: sudo useradd.sh [-u uid] [-g group] [-a] [-c] [-d home] [-s shell] [-rn realname] name

-a makes new user an administrator.
> ./freplace.sh
Usage: freplace.sh [-sb] [-o owner:group] -d indir target replacement

Replaces target in directory indir with replacement. Has a few extra features as well. Both are still pretty basic, but serve a purpose and I'll probably keep updating them. Handle with care.
|
Universal Hello World
I've been struggling with building universal (fat) binaries on OS X for quite a while now. Finally decided to sit down and figure this out. In a situation with many variables, it's often good to stop and try to "crystallise" the problem at hand. What better way to do this than with a Hello World - if that won't compile and link, then why should anything else, right?

#include <stdio.h>

int main (void)
{
printf ("Hello, world!\n");
return 0;
}

Then we compile:
> gcc hellow.c -o hellow
> ./hellow
Hello, world!
> file hellow
hellow: Mach-O executable ppc


So far so good. Now let's see if we can make a fat binary out of this sucker:
> gcc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386 -arch ppc64 hellow.c -o hellow-fat
> ./hellow-fat
Hello, world!

> file hellow-fat
hellow-fat: Mach-O fat file with 3 architectures
hellow-fat (for architecture ppc): Mach-O executable ppc
hellow-fat (for architecture i386): Mach-O executable i386
hellow-fat (for architecture ppc64): Mach-O 64-bit executable ppc64

Hmm, that wasn't so bad. I guess here is where the "fat" in "fat binary" comes from:
> ls -lhS hellow*
-rwxr-xr-x 1 filipp filipp 44K Dec 12 13:45 hellow-fat
-rwxr-xr-x 1 filipp filipp 13K Dec 12 13:46 hellow
-rw-r--r-- 1 filipp filipp 80B Dec 12 13:21 hellow.c

Well, that turned out to be a useless experiment. At least I know that my cross-compiling support works now...
|
BBEdit Session Restore
BBEdit's probably my most important tool. Even though an editor is such a basic thing that it's almost kind of silly to be attached to one, you just get used to it over time and it's nice to constantly be finding new features and ways to use a tool.

I can easily have upwards of 20 documents open simultaneously - sometimes part of the same project, mostly not. This makes you weary of actually closing the app because what if you want to pick up exactly where you left off? Well, luckily, like so many things in life, this problem too can be solved with a little bit of AppleScript:
property theDocuments : {}

tell application "BBEdit"
if (count of document) is greater than 1 then
set theDocuments to file of every document as list
else
open theDocuments
end if
end tell

Just run that after and before every session you want to restore. If you have documents open, it'll save them, if not, open the ones you had open the last time. It's very crude but gets the job done for now. Naturally they don't work with Trasmit links etc because those are just temporarily open.

Properties are wonderful in AS because they are reinitialised only when the script is compiled. That makes them perfect for any kind of "temporary" persistent storage.
|
PHP's file_get_contents () and cookies
Sending cookies - you can do it with file_get_contents () - just look at the stream functions' constants (the context parameter). But I found out it's much easier using the curl plugin (which isn't always available, btw, but neither is file_get_contents ()):
$c = curl_init ($url);
curl_setopt ($c, CURLOPT_VERBOSE, 1); // For testing
curl_setopt ($c, CURLOPT_COOKIE, "variable=value");
curl_exec ($c);


It's that simple. An awesome tool for creating all kinds of site parsers.

|
Something New Every Day...
JavaScript - Associative Arrays Considered Harmful

The reason I stumbled across this was that Apple's QuickTime Embedding JS has this problem. The symptoms look crazy - you'll have other JS code inside your embed tag. This happened to me when using Prototype. The fix is easy - just replace "new Array ();" on line 165 with "new Object ();"

|
AppleScript Tidbits
AppleScript URL protocol support

Getting the home directory:
set theHomeDir to the POSIX path of home directory of (system info)


Getting and setting the clipboard:
set whatever to the clipboard
set the clipboard to "whatever"


Encoding URLs
Personally, I think you're best off just piping it through PHP:

do shell script "echo myurl | /usr/bin/php -r \"urlencode(fgets(STDIN));\""

Bu there's also some info on Apple's website.

How does ScriptEditor know which app is scriptable?
By looking for the NSAppleScriptEnabled key in an application bundle's Info.plist

|
Automate Out-of-Office Reply Toggling
This one actually made it to the site:

If there's one thing that computers are better at than humans, it's remembering things. Take for example the typical Out Of Office email reply - you go on vacation and set a rule in Mail.app to automatically respond of your absence to any email with a certain criteria. Then you come back and a day or two later remember to turn the notification back off again.

Well if You use iCal, your Mac most likely already knows when you're leaving and coming back so let's simply tie that information with Mail.app. Here's how:

Open Automator and from the Automator library add a "Run AppleScript" step.
Replace the code with the following, putting in the name of your Out of Office rule:
set myRule to "Name of my Out of Office rule"
tell application "Mail"
set enabled of rule myRule to not enabled of rule myRule
end tell

3) File > Save As Plug-In, give it a name (like "Toggle Out-Of-Office"), Plug-in for: iCal Alarm. Save.

iCal will open with a new event which you can simply delete. Then just create events on the start and end dates of your vacation (if you haven't already) and set our newly created script as the alarm (which you'll find in the alarm "Open file" dropdown menu).

The reply will now activate when yo leave and deactivate when you return. Just make sure your rule is Inactive before you go. :)
|
Flannel 1.0
This isn't much of a release announcement since it's not even available for download yet, but Flannel's demo page is finally up now.
This is all part of a not-so-elaborate scheme to get Flannel out there in the hands of the users. Not knowing the current state of true WYSIWYG publishers out there, I still think it could be very useful for people who just want their stuff online quickly and easily.

There's still alot of work to be done and the version that's running the demo's not the one I had in mind for the 1.0 release, but still...

|
Search & Replace with nano!
This is the coolest thing:
Ctrl - R - string to search, enter, to replace, + a
boom.

|
A Random Quote For Site Slogan
window.onload = function()
{
var quotes = ["I wanna be a racecar passenger.", "Alright, you're a cook - can you farm?"];
var i = Math.floor(Math.random()*quotes.length);
document.getElementsByTagName('h2')[0].innerHTML = '"' + quotes[i] + '"';
}

Don't forget to add blank lines before and after the JS!
|
A Teacher's Pet
Punk made it to the Staff Favorites list. It will also be included on the cover CD of Univers Mac, a French Mac Magazine! Thanks!


tp

|
sortUsingSelector
After spending 4 days with the problem of sorting an array of NSDictionaries (!!) using every kind of method imaginable, I finally found the solution:

You can't use sortUsingSelector because that operates on the object in the array (in this case an NSDictionary). That explains the countless ("[CFDictionary compareScores] selector not recognized etc...") I was getting. Phew

So use sortUsingFunction instead. And as the guys at cocoadev so eloquently showed:
int someSort (id obj1, id obj2, void *context)
{
return [[(NSDictionary *)obj1 objectForKey:(NSString *)context] compare:[(NSDictionary *)obj2 objectForKey:(NSString *)context]];
}


Where "context" is the NSDictionary key you want to sort by.

|
Getting Stuff Into MySQL
Forget clumsy scripts to load SQL files into MySQL. Here's how it worked for me:

check your php.ini "upload_max_filesize"

$newFile = move_uploaded file ($FILES['yourfile']['tmp_name'], '/var/tmp/yourfile');
$sql = "LOAD DATA INFILE '$newFile'
INTO TABLE db.table
FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\"' LINES TERMINATED BY '\r'";


Strangely, MySQL 4.x doesn't like the "ENCLOSED BY" statement (it works, but will only import the first row)

|
PHP weirdness
This doesn't work as expected:
echo "foo:bar" | php -r "print str_replace(':','-','php://stdin');"


Must investigate. Maybe try with another machine...

UPDATE:
echo "foo:bar" | php -r "print str_replace(':','-',trim(fgets(STDIN)));"

|
Symbian madness
http://discussion.forum.nokia.com/forum/archive/index.php/t-66906.html
http://www.symbian-toys.com/
http://cms.planeetta.fi/symbian/labs

|
More On Spelling
http://www.cocoabuilder.com/archive/message/cocoa/2005/2/4/127543

|
phpBB privmsgs to PunBB
DELETE FROM punmessages; 
INSERT INTO punmessages (subject,sender_id, sender, owner, posted, message, sender_ip,smileys,showed)
(SELECT privmsgs_subject, privmsgs_from_userid,
(SELECT username FROM phpbb_users WHERE phpbb_users.user_id = privmsgs_from_userid),
privmsgs_to_userid, privmsgs_date, privmsgs_text, INET_NTOA(CONV(privmsgs_ip,16,10)), privmsgs_enable_smilies,
(SELECT user_new_privmsg FROM phpbb_users WHERE phpbb_users.user_id = privmsgs_from_userid)
FROM phpbb_privmsgs, phpbb_privmsgs_text
WHERE privmsgs_id = privmsgs_text_id);