Index: .gitignore
===================================================================
--- .gitignore	(revision 31e18129ea9937a3c686baf5ac27b535746cf048)
+++ .gitignore	(revision 0237078c137da62703c024b5068c515c8086916f)
@@ -0,0 +1,11 @@
+*~
+vm*
+ksplice*
+voldaemon
+log
+printcap
+debian/files
+debian/*.debhelper
+debian/gutenbach.substvars
+debian/gutenbach
+debian/gutenbach.debhelper.log
Index: .gitmodules
===================================================================
--- .gitmodules	(revision 9a301c8a994ce93b00343df3e3aeac5989d0aeba)
+++ .gitmodules	(revision 9a301c8a994ce93b00343df3e3aeac5989d0aeba)
@@ -0,0 +1,18 @@
+[submodule "plugins/itunes"]
+	path = plugins/itunes
+	url = /afs/sipb/project/sipbmp3/itunes-plugin.git/
+[submodule "web"]
+	path = web
+	url = /afs/sipb/project/sipbmp3/sipbmp3web.git/
+[submodule "gutenbach/debian/lib"]
+	path = gutenbach/debian/lib
+	url = /afs/sipb/project/sipbmp3/sipbmp3.git/
+[submodule "gutenbach-queue"]
+	path = gutenbach-queue
+	url = /afs/sipb/project/sipbmp3/gutenbach-queue.git/
+[submodule "gutenbach-remctl"]
+	path = gutenbach-remctl
+	url = /afs/sipb/project/sipbmp3/sipbmp3-remctl.git
+[submodule "plugins/gutenbach-rhythmbox-plugin"]
+	path = plugins/gutenbach-rhythmbox-plugin
+	url = /afs/sipb/project/sipbmp3/rhythmbox-plugin.git
Index: debian/.gitignore
===================================================================
--- debian/.gitignore	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ debian/.gitignore	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
@@ -0,0 +1,1 @@
+*~
Index: debian/changelog
===================================================================
--- debian/changelog	(revision e87e682008b20061e88abcb383650485c76c3f88)
+++ debian/changelog	(revision 773766631830ea5f12a8d625adc90e5e2a66b594)
@@ -1,2 +1,70 @@
+gutenbach (2.03) unstable; urgency=low
+
+  * Update permissions again, to allow everybody to access the server
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Tue, 08 Jun 2010 19:07:09 -0400
+
+gutenbach (2.02) unstable; urgency=low
+
+  * Fix print server permissions problems such that remote
+  users can user cups-lpq and cups-lprm
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Mon, 31 May 2010 23:51:52 -0400
+
+gutenbach (2.01) unstable; urgency=low
+
+  * Handle SIGTERM in the gutenbach backend, so jobs don't
+  keep playing even after being dequeued.
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Mon, 31 May 2010 22:23:53 -0400
+
+gutenbach (2.0) unstable; urgency=low
+
+  * Changed from using LPRng to CUPS
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Thu, 13 May 2010 17:40:53 -0400
+
+gutenbach (1.01) unstable; urgency=low
+
+  * Implemented debconf to retrieve printer and zephyr class names.
+  * Made uninstall cleaner (and when you purge, it purges
+  configuration files and changes)
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Wed, 16 Sep 2009 15:58:40 -0400
+
+gutenbach (1.0) unstable; urgency=low
+
+  * Initial release.
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Tue, 08 Sep 2009 18:38:12 -0400
+
+gutenbach-client (1.01-1) unstable; urgency=low
+
+  * Make package no longer Debian native.
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Sat, 26 Jun 2010 19:20:08 -0400
+
+gutenbach-client (1.01) unstable; urgency=low
+
+  * Fix debhelper compatibility to 5 (for backwards compatibility)
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Sat, 26 Jun 2010 18:01:02 -0400
+
+gutenbach-client (1.0) unstable; urgency=low
+
+  * Initial package build
+  * gbq - show queue
+  * gbr - queue file
+  * gbrm - remove file
+  * gutenbach-client-config - add, list, or delete queues
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Sat, 26 Jun 2010 16:56:28 -0400
+
+gutenbach-client (0.01) unstable; urgency=low
+
+  * Created skeleton package.
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Sat, 19 Jun 2010 00:23:53 -0400
+
 gutenbach-queue (1.02) unstable; urgency=low
 
@@ -18,2 +86,20 @@
 
  -- Jessica Hamrick <jhamrick@mit.edu>  Mon, 14 Dec 2009 20:03:48 -0500
+
+gutenbach-remctl (1.02) unstable; urgency=low
+
+  * Depend on version 2.0 or greater of gutenbach
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Thu, 13 May 2010 17:42:53 -0400
+
+gutenbach-remctl (1.01) unstable; urgency=low
+
+  * Just say the new volume, not who changed it.
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Mon, 14 Dec 2009 22:04:43 -0500
+
+gutenbach-remctl (1.0) unstable; urgency=low
+
+  * Initial release.
+
+ -- Jessica Hamrick <jhamrick@mit.edu>  Mon, 14 Dec 2009 22:03:44 -0500
Index: debian/control
===================================================================
--- debian/control	(revision e87e682008b20061e88abcb383650485c76c3f88)
+++ debian/control	(revision 773766631830ea5f12a8d625adc90e5e2a66b594)
@@ -1,8 +1,25 @@
-Source: gutenbach-queue
+Source: gutenbach
 Section: sound
 Priority: extra
 Maintainer: Jessica Hamrick <jhamrick@mit.edu>
-Build-Depends: cdbs (>= 0.4.23-1.1), debhelper (>= 5)
-Standards-Version: 3.8.0
+Build-Depends: cdbs, debhelper (>= 7.4.15ubuntu1)
+Standards-Version: 3.8.4
+
+Package: gutenbach
+Architecture: all
+Pre-Depends: debconf
+Depends: ${shlibs:Depends}, ${misc:Depends}, mplayer, libimage-exiftool-perl, cups, debathena-cupsys-config, sysv-rc, adduser, debathena-zephyr-config, libzephyr4-krb5, alsa-base
+Conflicts: pulseaudio
+Provides: pulseaudio
+Description: Server for playing music files from a remote machine
+ Allows remote machines to play music files on this machine
+ by "printing" them with the Common UNIX Printing System (CUPS).
+
+Package: gutenbach-client
+Architecture: all
+Pre-Depends: debconf
+Depends: ${shlibs:Depends}, ${misc:Depends}, cups, libnet-cups-perl
+Description: Client-side helper scripts for interacting with
+ the Gutenbach music player.
 
 Package: gutenbach-queue
@@ -13,2 +30,9 @@
  queue for the gutenbach music spooler, and also the song that
  is currently playing.
+
+Package: gutenbach-remctl
+Architecture: all
+Pre-Depends: debconf
+Depends: ${shlibs:Depends}, ${misc:Depends}, gutenbach (>= 2.0), alsa-utils, cd-discid, remctl-server
+Description: Allows a remote user to control the volume, view
+ the current status of the queue, or play tracks from a cd.
Index: debian/gutenbach-remctl.dirs
===================================================================
--- debian/gutenbach-remctl.dirs	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ debian/gutenbach-remctl.dirs	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
@@ -0,0 +1,3 @@
+usr/lib/gutenbach
+usr/lib/gutenbach/remctl
+etc/remctl/conf.d
Index: debian/gutenbach-remctl.init
===================================================================
--- debian/gutenbach-remctl.init	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ debian/gutenbach-remctl.init	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
@@ -0,0 +1,114 @@
+#!/bin/bash
+### BEGIN INIT INFO
+# Provides:          gutenbach-remctl
+# Required-Start:    $remote_fs $syslog
+# Required-Stop:     $remote_fs $syslog
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: Starts remctld.
+# Description:       Starts remctld.
+### END INIT INFO
+
+case "$1" in
+    start)
+	if [ -e /var/run/gutenbach/remctld.pid ]; then
+	    pid=$(cat /var/run/gutenbach/remctld.pid)
+	    if [ -n "$pid" ]; then
+		kill -0 "$pid"
+		if [ "$?" == 0 ]; then
+		    echo "Ending remctld..." >&2
+		    kill "$pid" || true
+		fi
+	    fi
+	fi
+
+	pid=$(sudo lsof -i :4373 | grep remctld | sed s/remctld\ // | sed s/\ .*//)
+	if [ -n "$pid" ]; then
+	    kill -0 "$pid"
+	    if [ "$?" == 0 ]; then
+		echo "Ending remctld socket binding..." >&2
+		kill "$pid" || true
+	    fi
+	fi
+
+	echo "Starting remctld..." >&2
+	remctld -m -P /var/run/gutenbach/remctld.pid &
+	;;
+
+    stop)
+	if [ -e /var/run/gutenbach/remctld.pid ]; then
+	    pid=$(cat /var/run/gutenbach/remctld.pid)
+	    if [ -n "$pid" ]; then
+		kill -0 "$pid"
+		if [ "$?" == 0 ]; then
+		    echo "Ending remctld..." >&2
+		    kill "$pid" || true
+		fi
+	    fi
+	fi
+
+	pid=$(sudo lsof -i :4373 | grep remctld | sed s/remctld\ // | sed s/\ .*//)
+	if [ -n "$pid" ]; then
+	    kill -0 "$pid"
+	    if [ "$?" == 0 ]; then
+		echo "Ending remctld socket binding..." >&2
+		kill "$pid" || true
+	    fi
+	fi
+	;;
+
+    restart)
+	if [ -e /var/run/gutenbach/remctld.pid ]; then
+	    pid=$(cat /var/run/gutenbach/remctld.pid)
+	    if [ -n "$pid" ]; then
+		kill -0 "$pid"
+		if [ "$?" == 0 ]; then
+		    echo "Ending remctld..." >&2
+		    kill "$pid" || true
+		fi
+	    fi
+	fi
+
+	pid=$(sudo lsof -i :4373 | grep remctld | sed s/remctld\ // | sed s/\ .*//)
+	if [ -n "$pid" ]; then
+	    kill -0 "$pid"
+	    if [ "$?" == 0 ]; then
+		echo "Ending remctld socket binding..." >&2
+		kill "$pid" || true
+	    fi
+	fi
+
+	echo "Starting remctld..." >&2
+	remctld -m -P /var/run/gutenbach/remctld.pid &
+	;;
+
+    force-reload)
+	if [ -e /var/run/gutenbach/remctld.pid ]; then
+	    pid=$(cat /var/run/gutenbach/remctld.pid)
+	    if [ -n "$pid" ]; then
+		kill -0 "$pid"
+		if [ "$?" == 0 ]; then
+		    echo "Ending remctld..." >&2
+		    kill "$pid" || true
+		fi
+	    fi
+	fi
+
+	pid=$(sudo lsof -i :4373 | grep remctld | sed s/remctld\ // | sed s/\ .*//)
+	if [ -n "$pid" ]; then
+	    kill -0 "$pid"
+	    if [ "$?" == 0 ]; then
+		echo "Ending remctld socket binding..." >&2
+		kill "$pid" || true
+	    fi
+	fi
+
+	echo "Starting remctld..." >&2
+	remctld -m -P /var/run/gutenbach/remctld.pid &
+	;;
+
+    *)
+	echo "Usage: $0 {start|stop|restart}"
+	exit 1
+	;;
+esac
Index: debian/gutenbach-remctl.install
===================================================================
--- debian/gutenbach-remctl.install	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ debian/gutenbach-remctl.install	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
@@ -0,0 +1,2 @@
+debian/lib/for-gutenbach/* /usr/lib/gutenbach/remctl/
+debian/lib/for-remctl/* /etc/remctl/conf.d/
Index: debian/gutenbach-remctl.postinst
===================================================================
--- debian/gutenbach-remctl.postinst	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ debian/gutenbach-remctl.postinst	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
@@ -0,0 +1,52 @@
+#!/bin/sh
+# postinst script for gutenbach-remctl
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <postinst> `configure' <most-recently-configured-version>
+#        * <old-postinst> `abort-upgrade' <new version>
+#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+#          <new-version>
+#        * <postinst> `abort-remove'
+#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+#          <failed-install-package> <version> `removing'
+#          <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+case "$1" in
+    configure|reconfigure)
+	chmod +x /usr/lib/gutenbach/remctl/cd-cddb
+	chmod +x /usr/lib/gutenbach/remctl/cd-cddb-get
+	chmod +x /usr/lib/gutenbach/remctl/cd-play
+	chmod +x /usr/lib/gutenbach/remctl/status-clear
+	chmod +x /usr/lib/gutenbach/remctl/status-get
+	chmod +x /usr/lib/gutenbach/remctl/volume-down
+	chmod +x /usr/lib/gutenbach/remctl/volume-get
+	chmod +x /usr/lib/gutenbach/remctl/volume-help
+	chmod +x /usr/lib/gutenbach/remctl/volume-is-muted
+	chmod +x /usr/lib/gutenbach/remctl/volume-mute
+	chmod +x /usr/lib/gutenbach/remctl/volume-set
+	chmod +x /usr/lib/gutenbach/remctl/volume-show
+	chmod +x /usr/lib/gutenbach/remctl/volume-up
+	chmod +x /usr/lib/gutenbach/remctl/volume-zephyr
+    ;;
+
+    abort-upgrade|abort-remove|abort-deconfigure)
+    ;;
+
+    *)
+        echo "postinst called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
Index: debian/gutenbach-remctl.postrm
===================================================================
--- debian/gutenbach-remctl.postrm	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
+++ debian/gutenbach-remctl.postrm	(revision 78ebe7526b641cb0177050f6a78fb68c506ffbeb)
@@ -0,0 +1,49 @@
+#!/bin/sh
+# postrm script for gutenbach-remctl
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <postrm> `remove'
+#        * <postrm> `purge'
+#        * <old-postrm> `upgrade' <new-version>
+#        * <new-postrm> `failed-upgrade' <old-version>
+#        * <new-postrm> `abort-install'
+#        * <new-postrm> `abort-install' <old-version>
+#        * <new-postrm> `abort-upgrade' <old-version>
+#        * <disappearer's-postrm> `disappear' <overwriter>
+#          <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+    purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+	if [ "$1" = "purge" ]; then
+	    if [ -e /usr/lib/gutenbach/remctl ]; then
+		echo "Removing /usr/lib/gutenbach/remctl/ ..." >&2
+		rm -r  /usr/lib/gutenbach/remctl
+	    fi
+
+	    if [ -e /etc/remctl/conf.d ]; then
+		echo "Removing /etc/remctl/conf.d ..." >&2
+		rm /etc/remctl/conf.d
+		mkdir /etc/remctl/conf.d
+	    fi
+     	fi
+    ;;
+
+    *)
+        echo "postrm called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
Index: debian/gutenbach.config
===================================================================
--- debian/gutenbach.config	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ debian/gutenbach.config	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,29 @@
+#!/bin/sh -e
+
+# Source debconf library.
+. /usr/share/debconf/confmodule
+
+printername=""
+zephyrclass=""
+hostname=""
+
+if [ -e /usr/lib/gutenbach/config/printername ]; then
+    printername=$(cat /usr/lib/gutenbach/config/printername)
+    db_set gutenbach/printername "$printername"
+fi
+
+if [ -e /usr/lib/gutenbach/config/zephyrclass ]; then
+    zephyrclass=$(cat /usr/lib/gutenbach/config/zephyrclass)
+    db_set gutenbach/zephyrclass "$zephyrclass"
+fi
+
+if [ -e /usr/lib/gutenbach/config/hostname ]; then
+    hostname=$(cat /usr/lib/gutenbach/config/hostname)
+    db_set gutenbach/hostname "$hostname"
+fi
+
+db_input high gutenbach/printername || true
+db_input high gutenbach/zephyrclass || true
+db_input high gutenbach/hostname || true
+db_go || true
+
Index: debian/gutenbach.dirs
===================================================================
--- debian/gutenbach.dirs	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ debian/gutenbach.dirs	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,5 @@
+usr/lib/gutenbach
+usr/lib/gutenbach/inst
+usr/lib/gutenbach/rm
+usr/lib/gutenbach/config
+etc/
Index: debian/gutenbach.init
===================================================================
--- debian/gutenbach.init	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ debian/gutenbach.init	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,49 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides:          gutenbach
+# Required-Start:    $remote_fs $syslog cups
+# Required-Stop:     $remote_fs $syslog
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: Starts lpd for gutenbach.
+# Description:       Starts lpd for gutenbach and creates
+#  files for use by gutenbach.
+### END INIT INFO
+
+case "$1" in
+  start)
+    mkdir /var/run/gutenbach
+    touch /var/run/gutenbach/status
+    chmod 0777 /var/run/gutenbach/status
+    
+    ;;
+
+  stop)
+    
+    if [ -e /var/run/gutenbach ]; then
+	rm -r /var/run/gutenbach
+    fi
+
+    ;;
+
+  restart)
+    
+    if [ -e /var/run/gutenbach ]; then
+	rm -r /var/run/gutenbach
+    fi
+
+
+    mkdir /var/run/gutenbach
+    touch /var/run/gutenbach/status
+    chmod 0777 /var/run/gutenbach/status
+
+    ;;
+
+  reload|force-reload|status)
+    ;;
+
+  *)
+    echo "Usage: /etc/init.d/gutenbach {start|stop|restart}" >&2
+    exit 1
+    ;;
+esac
Index: debian/gutenbach.install
===================================================================
--- debian/gutenbach.install	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ debian/gutenbach.install	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,4 @@
+debian/lib/gutenbach /usr/lib/cups/backend
+debian/lib/* /usr/lib/gutenbach/
+debian/inst/* /usr/lib/gutenbach/inst/
+debian/rm/* /usr/lib/gutenbach/rm/
Index: debian/gutenbach.postinst
===================================================================
--- debian/gutenbach.postinst	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ debian/gutenbach.postinst	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,65 @@
+#!/bin/sh
+# postinst script for gutenbach
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <postinst> `configure' <most-recently-configured-version>
+#        * <old-postinst> `abort-upgrade' <new version>
+#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+#          <new-version>
+#        * <postinst> `abort-remove'
+#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+#          <failed-install-package> <version> `removing'
+#          <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+. /usr/share/debconf/confmodule
+
+case "$1" in
+    configure|reconfigure)
+	if [ ! -d /usr/lib/gutenbach/config ]; then
+	    mkdir /usr/lib/gutenbach/config
+	fi
+
+	db_get gutenbach/zephyrclass
+	echo "$RET" > /usr/lib/gutenbach/config/zephyrclass
+	db_get gutenbach/printername
+	echo "$RET" > /usr/lib/gutenbach/config/printername
+	db_get gutenbach/hostname
+	echo "$RET" > /usr/lib/gutenbach/config/hostname
+
+	chmod +x /usr/lib/gutenbach/inst/create-filter-config
+	chmod +x /usr/lib/gutenbach/inst/cups-add-printer
+	chmod +x /usr/lib/gutenbach/inst/cups-set-perms
+	/usr/lib/gutenbach/inst/create-filter-config
+	/usr/lib/gutenbach/inst/cups-add-printer
+	/usr/lib/gutenbach/inst/cups-set-perms
+
+
+	inaudio=$(groups lp | grep audio || true)
+	if [ -z "$inaudio" ]; then
+	    adduser lp audio
+	fi
+
+	db_stop
+    ;;
+
+    abort-upgrade|abort-remove|abort-deconfigure)
+    ;;
+
+    *)
+        echo "postinst called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
Index: debian/gutenbach.postrm
===================================================================
--- debian/gutenbach.postrm	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ debian/gutenbach.postrm	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,54 @@
+#!/bin/sh
+# postrm script for gutenbach
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <postrm> `remove'
+#        * <postrm> `purge'
+#        * <old-postrm> `upgrade' <new-version>
+#        * <new-postrm> `failed-upgrade' <old-version>
+#        * <new-postrm> `abort-install'
+#        * <new-postrm> `abort-install' <old-version>
+#        * <new-postrm> `abort-upgrade' <old-version>
+#        * <disappearer's-postrm> `disappear' <overwriter>
+#          <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+    purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+	if [ "$1" = "purge" ]; then
+	    if [ -d /usr/lib/gutenbach ]; then
+		echo "Removing /usr/lib/gutenbach/..." >&2
+		rm -r /usr/lib/gutenbach
+	    fi
+
+	    if [ -e /usr/share/debconf/confmodule ]; then
+		echo "Purging debconf database entries..." >&2
+		. /usr/share/debconf/confmodule
+		db_purge
+	    fi
+	fi
+
+	inaudio=$(groups daemon | grep audio || true)
+	if [ ! -z "$inaudio" ]; then
+	    deluser daemon audio
+	fi
+    ;;
+
+    *)
+        echo "postrm called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
Index: debian/gutenbach.preinst
===================================================================
--- debian/gutenbach.preinst	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ debian/gutenbach.preinst	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,36 @@
+#!/bin/sh
+# preinst script for gutenbach
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <new-preinst> `install'
+#        * <new-preinst> `install' <old-version>
+#        * <new-preinst> `upgrade' <old-version>
+#        * <old-preinst> `abort-upgrade' <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+. /usr/share/debconf/confmodule
+
+case "$1" in
+    install|upgrade)
+    ;;
+
+    abort-upgrade)
+    ;;
+
+    *)
+        echo "preinst called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
Index: debian/gutenbach.prerm
===================================================================
--- debian/gutenbach.prerm	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ debian/gutenbach.prerm	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,48 @@
+#!/bin/sh
+# prerm script for gutenbach
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <prerm> `remove'
+#        * <old-prerm> `upgrade' <new-version>
+#        * <new-prerm> `failed-upgrade' <old-version>
+#        * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
+#        * <deconfigured's-prerm> `deconfigure' `in-favour'
+#          <package-being-installed> <version> `removing'
+#          <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+    remove|upgrade|deconfigure)
+
+	if [ -e /usr/lib/gutenbach/rm/cups-revert-perms ]; then
+	    chmod +x /usr/lib/gutenbach/rm/cups-revert-perms
+	    /usr/lib/gutenbach/rm/cups-revert-perms
+	fi
+
+	if [ -e /usr/lib/gutenbach/rm/cups-remove-printer ]; then
+	    chmod +x /usr/lib/gutenbach/rm/cups-remove-printer
+	    /usr/lib/gutenbach/rm/cups-remove-printer
+	fi
+    ;;
+
+    failed-upgrade)
+    ;;
+
+    *)
+        echo "prerm called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
Index: debian/gutenbach.templates
===================================================================
--- debian/gutenbach.templates	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ debian/gutenbach.templates	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,25 @@
+Template: gutenbach/printername
+Type: string
+_Description: Your gutenbach printer name:
+ gutenbach creates a printer to which you can print music
+ files.  gutenbach needs to know what you want to call the 
+ printer, and then after installation, you will be able
+ to do:
+ lpr -Pprintername@hostname your-music-file-.mp3 
+
+Template: gutenbach/zephyrclass
+Type: string
+_Description: Your gutenbach zephyr class:
+ When gutenbach plays a song, it sends a message to a zephyr
+ class with the song information (title, artist, album,
+ etc).  Please specify which class you would you these 
+ zephyrs to be sent to.  If you don't know what this is,
+ or you don't want zephyrs to be sent, just leave this 
+ field blank.
+
+Template: gutenbach/hostname
+Type: string
+_Description: The hostname to be displayed:
+ When gutenbach sends a zephyr, it will display the hostname.
+ If your hostname is particularly long, you may want to set
+ this to an abbreviation or something else shorter.
Index: debian/po/POTFILES.in
===================================================================
--- debian/po/POTFILES.in	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ debian/po/POTFILES.in	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,1 @@
+[type: gettext/rfc822deb] gutenbach.templates
Index: debian/po/templates.pot
===================================================================
--- debian/po/templates.pot	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ debian/po/templates.pot	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,64 @@
+# Gutenbach Translation File.
+#
+# This file is distributed under the same license as the GUTENBACH package.
+# Jessica B. Hamrick <jhamrick@mit.edu>, 2010.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: gutenbach@mit.edu\n"
+"POT-Creation-Date: 2010-06-14 18:36-0600\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: string
+#. Description
+#: ../gutenbach.templates:1001
+msgid "Your gutenbach printer name:"
+msgstr ""
+
+#. Type: string
+#. Description
+#: ../gutenbach.templates:1001
+msgid ""
+"gutenbach creates a printer to which you can print music files.  gutenbach "
+"needs to know what you want to call the  printer, and then after "
+"installation, you will be able to do: lpr -Pprintername@hostname your-music-"
+"file-.mp3"
+msgstr ""
+
+#. Type: string
+#. Description
+#: ../gutenbach.templates:2001
+msgid "Your gutenbach zephyr class:"
+msgstr ""
+
+#. Type: string
+#. Description
+#: ../gutenbach.templates:2001
+msgid ""
+"When gutenbach plays a song, it sends a message to a zephyr class with the "
+"song information (title, artist, album, etc).  Please specify which class "
+"you would you these  zephyrs to be sent to.  If you don't know what this is, "
+"or you don't want zephyrs to be sent, just leave this  field blank."
+msgstr ""
+
+#. Type: string
+#. Description
+#: ../gutenbach.templates:3001
+msgid "The hostname to be displayed:"
+msgstr ""
+
+#. Type: string
+#. Description
+#: ../gutenbach.templates:3001
+msgid ""
+"When gutenbach sends a zephyr, it will display the hostname. If your "
+"hostname is particularly long, you may want to set this to an abbreviation "
+"or something else shorter."
+msgstr ""
Index: debian/rules
===================================================================
--- debian/rules	(revision e87e682008b20061e88abcb383650485c76c3f88)
+++ debian/rules	(revision 7cf3a465c72c2c72d74a3df48b5f6e8e378cfb54)
@@ -1,3 +1,6 @@
 #!/usr/bin/make -f
 
+DEB_MAKE_INSTALL_TARGET=install DESTDIR=$(DEB_DESTDIR)
+
 include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/makefile.mk
Index: debian/source/format
===================================================================
--- debian/source/format	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ debian/source/format	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,1 @@
+1.0
Index: gutenbach-client/Makefile
===================================================================
--- gutenbach-client/Makefile	(revision 0deef9e258fce28cac566b226c4b623057566190)
+++ gutenbach-client/Makefile	(revision 0deef9e258fce28cac566b226c4b623057566190)
@@ -0,0 +1,5 @@
+all:
+
+install:
+	mkdir -p $(DESTDIR)/usr/bin/
+	install -m 755 bin/* $(DESTDIR)/usr/bin/
Index: gutenbach-client/bin/gbq
===================================================================
--- gutenbach-client/bin/gbq	(revision 0deef9e258fce28cac566b226c4b623057566190)
+++ gutenbach-client/bin/gbq	(revision 0deef9e258fce28cac566b226c4b623057566190)
@@ -0,0 +1,54 @@
+#!/usr/bin/perl
+
+# Written by Jessica Hamrick (C) 2010
+
+use strict;
+use warnings;
+
+use Net::CUPS;
+use Net::CUPS::Destination;
+
+my $usage = "Usage: gbq QUEUE\n";
+
+my $q = $ARGV[0];
+
+if (!$q) {
+    print $usage;
+    exit 1
+}
+
+my $configpath = "$ENV{'HOME'}/.gutenbach/$q";
+if (! -e $configpath) {
+    print "Queue '$q' does not exist!\n";
+    exit 1;
+}
+
+my ($host, $queue);
+
+if (-r $configpath) {
+    local $/;
+    my $fh;
+    open $fh, $configpath;
+    eval <$fh>;
+}
+
+my $cups = Net::CUPS->new();
+$cups->setServer("$host");
+my $printer = $cups->getDestination("$queue");
+
+print "Queue listing for queue '$queue' on '$host'\n\n";
+print "Job ID\t\tOwner\t\tFile\n";
+print "-----------------------------------------------------------------\n";
+
+my @jobs = $printer->getJobs(0, 0);
+my ($job_ref, $jobid);
+
+foreach $jobid(@jobs) 
+{       
+	$job_ref = $printer->getJob($jobid);
+	my $id = $job_ref->{'id'};
+	my $user = $job_ref->{'user'};
+	my $title = $job_ref->{'title'};
+
+	print "$id\t\t$user\t\t$title\n";
+}
Index: gutenbach-client/bin/gbr
===================================================================
--- gutenbach-client/bin/gbr	(revision 0deef9e258fce28cac566b226c4b623057566190)
+++ gutenbach-client/bin/gbr	(revision 0deef9e258fce28cac566b226c4b623057566190)
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+
+# Written by Jessica Hamrick (C) 2010
+
+use strict;
+use warnings;
+
+my $usage = "Usage: gbr QUEUE FILE\n";
+
+my $q = $ARGV[0];
+my $file = $ARGV[1];
+
+if (!$q or !$file) {
+    print $usage;
+    exit 1
+}
+
+my $configpath = "$ENV{'HOME'}/.gutenbach/$q";
+if (! -e $configpath) {
+    print "Queue '$q' does not exist!\n";
+    exit 1;
+}
+
+my ($host, $queue);
+
+if (-r $configpath) {
+    local $/;
+    my $fh;
+    open $fh, $configpath;
+    eval <$fh>;
+}
+
+my @args = ("lp", "-h$host", "-d$queue", "$file");
+exec (@args) or die "Couldn't execute lp command";
Index: gutenbach-client/bin/gbrm
===================================================================
--- gutenbach-client/bin/gbrm	(revision 0deef9e258fce28cac566b226c4b623057566190)
+++ gutenbach-client/bin/gbrm	(revision 0deef9e258fce28cac566b226c4b623057566190)
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+
+# Written by Jessica Hamrick (C) 2010
+
+use strict;
+use warnings;
+
+my $usage = "Usage: gbq QUEUE ID\n";
+
+my $q = $ARGV[0];
+my $id = $ARGV[1];
+
+if (!$q or !$id) {
+    print $usage;
+    exit 1
+}
+
+my $configpath = "$ENV{'HOME'}/.gutenbach/$q";
+if (! -e $configpath) {
+    print "Queue '$q' does not exist!\n";
+    exit 1;
+}
+
+my ($host, $queue);
+
+if (-r $configpath) {
+    local $/;
+    my $fh;
+    open $fh, $configpath;
+    eval <$fh>;
+}
+
+my @args;
+
+if ($id eq "all") {
+    @args = ("cancel", "-a", "-h$host", "$queue");
+}
+else {
+    @args = ("cancel", "-h$host", "$id", "$queue");
+}
+exec (@args) or die "Couldn't execute cancel command";
Index: gutenbach-client/bin/gutenbach-client-config
===================================================================
--- gutenbach-client/bin/gutenbach-client-config	(revision 0deef9e258fce28cac566b226c4b623057566190)
+++ gutenbach-client/bin/gutenbach-client-config	(revision 0deef9e258fce28cac566b226c4b623057566190)
@@ -0,0 +1,84 @@
+#!/usr/bin/perl
+
+# Written by Jessica Hamrick, (C) 2010
+
+use strict;
+use warnings;
+use Getopt::Long;
+
+my $usage = 
+    "Usage: gutenbach-client-config [-l|--list|-a|--add|-d|--delete] [QUEUE] [--host=HOST]\n" .
+    "\n" .
+    "\t-l, --list\t\tList available queues\n" .
+    "\t-a, --add QUEUE\t\tAdd a queue (must be used with -h)\n" .
+    "\t-d, --delete QUEUE\tDelete a queue)\n" .
+    "\t-h, --host HOST\t\tHostname for the queue\n";
+
+my $list = 0;
+my $add = "";
+my $delete = "";
+my $host = "";
+
+GetOptions ('l|list' => \$list,
+            'a|add=s' => \$add,
+            'd|delete=s' => \$delete,
+	    'h|host=s' => \$host);
+
+my $configpath = "$ENV{'HOME'}/.gutenbach";
+
+if (! -e $configpath) {
+    mkdir "$configpath";
+}
+
+# list the existing queues
+if ($list and !$add and !$delete) {
+    my @queues = glob("$configpath/*") or die "Couldn't find configuration files at '$configpath'";
+
+    print "Queue\t\tHost\n";
+    foreach my $q (@queues) {
+	my ($host, $queue);
+
+	if (-r $q) {
+	    local $/;
+	    my $fh;
+	    open $fh, $q;
+	    eval <$fh>;
+	}
+
+	print "$queue\t\t$host\n";
+    }
+}
+
+# add a new queue
+elsif (!$list and $add and !$delete) {
+    if (!$host) {
+	print $usage;
+	exit 1;
+    }
+
+    if (-e "$configpath/$add") {
+	print "Warning: queue '$add' already exists\n";
+    }
+
+    open CONFIG, "> $configpath/$add" or die "Couldn't open config file '$configpath/$add'";
+    print CONFIG "\$host = \"$host\";\n";
+    print CONFIG "\$queue = \"$add\";\n";
+    close CONFIG;
+
+    print "Added queue '$add' on host '$host'\n"
+}
+
+# delete an existing queue
+elsif (!$list and !$add and $delete) {
+    if (! -e "$configpath/$delete") {
+	print "Error: queue '$delete' already exists\n";
+	exit 1;
+    }
+
+    unlink("$configpath/$delete") or die "Couldn't remove config file '$configpath/$delete'";
+}
+
+else {
+    print $usage;
+    exit 1;
+}
Index: gutenbach-itunes-plugin/README
===================================================================
--- gutenbach-itunes-plugin/README	(revision a411a9fcaa6fb77ba124f135279f79fcb52a367d)
+++ gutenbach-itunes-plugin/README	(revision a411a9fcaa6fb77ba124f135279f79fcb52a367d)
@@ -0,0 +1,58 @@
+-----------------
+iTunes -> gutenbach
+-----------------
+This is a simple little script which sends
+music from an iTunes library to the gutenbach
+lpr server.
+
+* Changelog
+29 Nov 2009 -> changed the name from sipbmp3 to gutenbach
+23 Aug 2009 -> broder spun loop into shell script instead of
+    applescript so that iTunes doesn't hang
+9 Jan 2009 -> price added 'quoted form'
+7 Jan 2009 -> kmill created initial version
+
+* System Requirements
+This script will only work under Mac OS X as
+it uses AppleScript.
+
+* Installation
+There are two parts:
+
+1) Launch the Printer Setup Utility and add
+   an IP Printer with the LPD protocol with
+   the following information:
+    Address: hostname
+    Queue: gutenbach
+   It is not necessary to specify the driver.
+
+   *** Note that you MUST give the printer name as the same name as
+       the queue, NOT the address (which it seems to default to). ***
+
+2) Create the directory ~/Library/iTunes/Scripts
+   and place the scripts you want within.
+
+3) Edit those scripts such, replacing "hostname" with the hostname of
+your printer and "gutenbach" with the printername.
+
+* Usage
+When in iTunes, select the songs which you
+would like to hear in the office, and click
+the script you want from the script menu from
+the menu bar.  The script menu looks like a
+little scroll icon.  There will be no
+feedback beyond the pleasant sounds you now
+hear around you.
+
+* Todo
+As it is, there is no feedback, even in the
+event of failure.  This is decidedly not
+optimal.
+
+* And so...
+Do whatever you want with it.  I'd like to
+have a copy if you extend it.
+
+Kyle Miller
+kmill@mit.edu
+
Index: gutenbach-itunes-plugin/gutenbach - Get Queue.applescript
===================================================================
--- gutenbach-itunes-plugin/gutenbach - Get Queue.applescript	(revision e52000947ec07db3b47b94c45736b6ed62a55860)
+++ gutenbach-itunes-plugin/gutenbach - Get Queue.applescript	(revision e52000947ec07db3b47b94c45736b6ed62a55860)
@@ -0,0 +1,21 @@
+-- -----------------
+-- iTunes -> gutenbach
+-- -----------------
+-- This is a simple little script which gets the current queue on gutenbach--
+-- Changelog:
+--
+-- 10 Oct 2009 -> pquimby created this script
+--
+-- Installation:
+-- For installation instructions see the INSTALL file in the snippets/sipbmp3-iTunes folder--
+
+--
+-- Usage:
+--
+-- Run this script from iTunes
+
+set message to (do shell script "lpq -Pgutenbach")
+tell application "iTunes"
+	display dialog "gutenbach currently is playing: " & "
+" & message buttons "OK" default button "OK"
+end tell
Index: gutenbach-itunes-plugin/gutenbach - Queue Music.applescript
===================================================================
--- gutenbach-itunes-plugin/gutenbach - Queue Music.applescript	(revision e52000947ec07db3b47b94c45736b6ed62a55860)
+++ gutenbach-itunes-plugin/gutenbach - Queue Music.applescript	(revision e52000947ec07db3b47b94c45736b6ed62a55860)
@@ -0,0 +1,50 @@
+-- -----------------
+-- iTunes -> gutenbach
+-- -----------------
+-- This is a simple little script which sends
+-- music from an iTunes library to the gutenbach
+-- lpr server.
+--
+-- Changelog:
+--
+-- 23 Aug 2009 -> broder spun loop into shell script instead of
+--     applescript so that iTunes doesn't hang
+-- 9 Jan 2009 -> price added 'quoted form'
+-- 7 Jan 2009 -> kmill created initial version
+--
+-- Installation:
+--
+-- 1) Launch the Printer Setup Utility and add
+--    an IP Printer with the LPD protocol with
+--    the following information:
+--     Address: zygorthian-space-raiders.mit.edu
+--     Queue: gutenbach
+--    It is not necessary to specify the driver.
+--
+-- 2) Create the directory ~/Library/iTunes/Scripts
+--    and place the "Send to gutenbach.scpt" file
+--    within.
+--
+-- Usage:
+--
+-- When in iTunes, select the songs which you
+-- would like to hear in the office, and click
+-- "Send to gutenbach" in the script menu from
+-- the menu bar.  The script menu looks like a
+-- little scroll icon.  There will be no
+-- feedback beyond the pleasant sounds you now
+-- hear around you.
+
+set ts to ""
+
+tell application "iTunes"
+	repeat with t in selection
+		if class of t is (file track) then
+			set loc to POSIX path of (get location of t)
+			set ts to ts & " " & (quoted form of loc)
+		end if
+	end repeat
+end tell
+
+set command to "(for t in " & ts & "; do lpr -o raw -Pgutenbach \"$t\"; done) >/dev/null 2>&1 </dev/null &"
+do shell script command
Index: gutenbach-itunes-plugin/gutenbach - Remove All.applescript
===================================================================
--- gutenbach-itunes-plugin/gutenbach - Remove All.applescript	(revision e52000947ec07db3b47b94c45736b6ed62a55860)
+++ gutenbach-itunes-plugin/gutenbach - Remove All.applescript	(revision e52000947ec07db3b47b94c45736b6ed62a55860)
@@ -0,0 +1,15 @@
+-- ------------------
+-- iTunes -> gutenbach
+-- ------------------
+
+-- Changelog:
+--
+-- 10 Oct 2009 -> pquimby created initial version
+--
+
+-- For installation instructions see the INSTALL file in the snippets/sipbmp3-iTunes folder--
+
+-- Usage:
+--  This script will remove all of your songs queued to gutenbach
+
+do shell script "lprm -Pgutenbach -"
Index: gutenbach-itunes-plugin/gutenbach - Toggle Mute.applescript
===================================================================
--- gutenbach-itunes-plugin/gutenbach - Toggle Mute.applescript	(revision e52000947ec07db3b47b94c45736b6ed62a55860)
+++ gutenbach-itunes-plugin/gutenbach - Toggle Mute.applescript	(revision e52000947ec07db3b47b94c45736b6ed62a55860)
@@ -0,0 +1,20 @@
+-- -----------------
+-- iTunes -> gutenbach
+-- -----------------
+
+-- Changelog:
+-- 10 Nov 2009 -> pquimby created initial version
+
+-- Installation:
+-- For installation instructions see the INSTALL file in the snippets/sipbmp3-iTunes folder--
+
+-- Usage:
+--
+-- Running this script will toggle the mute on gutenbach on or off.
+
+tell application "iTunes"
+	set vol to sound volume
+end tell
+
+set command to "/usr/local/bin/remctl hostname volume set " & vol
+do shell script command
Index: gutenbach-itunes-plugin/gutenbach - Volume Get.applescript
===================================================================
--- gutenbach-itunes-plugin/gutenbach - Volume Get.applescript	(revision e52000947ec07db3b47b94c45736b6ed62a55860)
+++ gutenbach-itunes-plugin/gutenbach - Volume Get.applescript	(revision e52000947ec07db3b47b94c45736b6ed62a55860)
@@ -0,0 +1,20 @@
+-- ------------------
+-- iTunes -> gutenbach
+-- ------------------
+
+-- Changelog:
+--
+-- 10 Oct 2009 -> pquimby created initial version
+--
+
+--For installation instructions see the INSTALL file in the snippets/sipbmp3-iTunes folder
+
+-- Usage:
+--  This script will get the volume of gutenbach and put it on your iTunes volume scaled to the max value of 31 (which is the max volume for gutenbach at the time this script was written).
+
+
+tell application "iTunes"
+	set currentVolume to (do shell script "/usr/local/bin/remctl hostname volume get")
+	set currentVolume to currentVolume / 31 * 100
+	set the sound volume to currentVolume
+end tell
Index: gutenbach-itunes-plugin/gutenbach - Volume Set.applescript
===================================================================
--- gutenbach-itunes-plugin/gutenbach - Volume Set.applescript	(revision e52000947ec07db3b47b94c45736b6ed62a55860)
+++ gutenbach-itunes-plugin/gutenbach - Volume Set.applescript	(revision e52000947ec07db3b47b94c45736b6ed62a55860)
@@ -0,0 +1,16 @@
+-- Changelog:
+--
+-- 5 April 2009 -> pquimby created initial version
+--
+
+-- Usage:
+--  This script will set the volume of gutenbach (assuming you have remctl) to your iTunes volume scaled to the max value of 31 (which is the max volume for gutenbach at the time this script was written).
+
+-- Ex:// If your iTunes volume is set at 50% then you will set the gutenbach volume to .50*31 or roughly 15.
+
+tell application "iTunes"
+	set vol to (sound volume * 31 / 100)
+end tell
+
+set command to "/usr/local/bin/remctl hostname volume set " & vol
+do shell script command
Index: gutenbach-remctl/README
===================================================================
--- gutenbach-remctl/README	(revision cc0d7053781e98c7ca1c2ab048d9941ddb3866c0)
+++ gutenbach-remctl/README	(revision cc0d7053781e98c7ca1c2ab048d9941ddb3866c0)
@@ -0,0 +1,10 @@
+gutenbach-remctl is a package which provides a remctl configuration,
+enabling users to remotely adjust settings such as volume.
+
+Questions and comments should be directed to gutenbach@mit.edu
+
+TODO:
+- need to add logging to see what's going wrong if a command fails
+- figure out how to determine the correct channel/mixer
+- better yet, figure out how to use pulse, and don't have to worry
+  about what the correct channel or mixer is at all
Index: gutenbach-remctl/cd
===================================================================
--- gutenbach-remctl/cd	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ gutenbach-remctl/cd	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
@@ -0,0 +1,3 @@
+cd cddb     /usr/lib/gutenbach/remctl/cd-cddb     ANYUSER
+cd cddb-get /usr/lib/gutenbach/remctl/cd-cddb-get ANYUSER
+cd play     /usr/lib/gutenbach/remctl/cd-play     ANYUSER
Index: gutenbach-remctl/cd-cddb
===================================================================
--- gutenbach-remctl/cd-cddb	(revision 6bb3ffb249dc254896b2f89be72367ae76fad5e9)
+++ gutenbach-remctl/cd-cddb	(revision 6bb3ffb249dc254896b2f89be72367ae76fad5e9)
@@ -0,0 +1,20 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+
+die () {
+    echo "$@";
+    exit 1;
+}
+
+CACHE=$(cd-cddb-get) || die "Unable to fetch CDDB information."
+
+. "$CACHE"
+
+echo "Artist: $DARTIST";
+echo "Album: $DALBUM";
+echo "Tracks: $TRACKS";
+
+for i in $(seq 1 $TRACKS); do
+    echo -n "Track $i: "
+    eval 'echo $TRACK'$i
+done
Index: gutenbach-remctl/cd-cddb-get
===================================================================
--- gutenbach-remctl/cd-cddb-get	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ gutenbach-remctl/cd-cddb-get	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
@@ -0,0 +1,37 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+
+CDDBURL="http://freedb.freedb.org/~cddb/cddb.cgi"
+MYUSER="gutenbach"
+MYHOST=`hostname`".mit.edu"
+
+DISCID=$(cd-discid /dev/cdrom 2>/dev/null) || exit 1
+
+set -- $DISCID
+
+ID="$1"
+TRACKS="$2"
+
+mkdir -p /var/cache/gutenbach
+
+CACHEFILE="/var/cache/gutenbach/$ID"
+
+if [ ! -e "$CACHEFILE" ]; then
+
+    CD=$(cddb-tool query "$CDDBURL" 3 "$MYUSER" "$MYHOST" $DISCID) \
+	|| exit 2;
+
+    file=$(tempfile)
+
+    cddb-tool read "$CDDBURL" 3 "$MYUSER" "$MYHOST" \
+	$(echo "$CD" | head -1 | awk '{print $2 " " $3}') > "$file"  \
+	|| exit 3;
+
+    echo "TRACKS=$TRACKS"   >  "$CACHEFILE"
+    cddb-tool parse "$file" >> "$CACHEFILE"
+
+    rm "$file"
+fi
+
+echo "$CACHEFILE"
+exit 0;
Index: gutenbach-remctl/cd-play
===================================================================
--- gutenbach-remctl/cd-play	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ gutenbach-remctl/cd-play	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
@@ -0,0 +1,37 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+
+CDDB=
+
+CACHE=$(cd-cddb-get)
+
+printername=$(/usr/lib/gutenbach/gutenbach-get-config queue)
+
+if [ "$?" = 0 ]; then
+    CDDB=1
+fi
+
+. "$CACHE"
+
+play_one () {
+    T=$1
+    if [ -n "$CDDB" ]; then
+	DESC=" - "$(eval 'echo $TRACK'$T)
+    fi
+
+    echo "cdda://$(($T - 1))" | mit-lpr -P"$queue"@localhost -J "Track $T$DESC";
+    echo "Queued Track $T$DESC";
+}
+
+TOPLAY="$2"
+
+if [ -z "$TOPLAY" -o "$TOPLAY" = "all" ]; then
+    for i in $(seq 1 "$TRACKS"); do
+	play_one $i
+    done
+else
+    shift;
+    for i in "$@"; do
+	play_one $i
+    done
+fi
Index: gutenbach-remctl/status
===================================================================
--- gutenbach-remctl/status	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ gutenbach-remctl/status	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
@@ -0,0 +1,2 @@
+status get   /usr/lib/gutenbach/remctl/status-get   ANYUSER
+status clear /usr/lib/gutenbach/remctl/status-clear ANYUSER
Index: gutenbach-remctl/status-clear
===================================================================
--- gutenbach-remctl/status-clear	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ gutenbach-remctl/status-clear	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
@@ -0,0 +1,3 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+> /var/run/gutenbach/status
Index: gutenbach-remctl/status-get
===================================================================
--- gutenbach-remctl/status-get	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ gutenbach-remctl/status-get	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
@@ -0,0 +1,3 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+cat /var/run/gutenbach/status
Index: gutenbach-remctl/voldaemon.c
===================================================================
--- gutenbach-remctl/voldaemon.c	(revision 9c6635060a1adeb1457077d8413e99210ccd878d)
+++ gutenbach-remctl/voldaemon.c	(revision 9c6635060a1adeb1457077d8413e99210ccd878d)
@@ -0,0 +1,319 @@
+/*
+ * $Id: voldaemon.c,v 1.1 2008-09-27 08:49:10 root Exp $
+ * $Source: /tmp/tmp.UFBNno9997/RCS/voldaemon.c,v $
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <assert.h>
+#include <netinet/in.h>
+
+
+#define LISTEN_PORT 8930
+#define MULTI_PORT 8931
+#define MULTI_ADDR "224.0.1.20"
+
+/* Block queries from off this subnet net 18.*/
+#define OUR_SUBNET      187
+
+typedef struct volmessage{
+  char A;
+  char request;
+  char value;
+  unsigned char ip_3;
+  unsigned char ip_2;
+  unsigned char ip_1;
+  unsigned char ip_0;
+  char ZE;
+} volmessage;
+
+#define MESSAGE_SIZE (sizeof(volmessage))
+
+/* The client makes requests using the following packets.  Responses
+   to queries and status reports from the server follow the same
+   pattern
+
+  Request/Response
+
+  'AVxiiiiZ'     Volume
+  'AMxiiiiZ'     Mute
+  'AQQiiiiZ'     Query 
+
+  Response Only
+  'AVeiiiiE'     Volume Set Error 
+  'AMeiiiiE'     Mute Set Error 
+  'AEeiiiiE'     Unknown error 
+
+   e=1  Permission denied (invalid IP)
+   e=2  Invalid request (Bad Packet)
+   
+*/
+
+void message(char *m){
+  write(STDERR_FILENO,m,strlen(m));
+}
+
+void abortm(char *m){
+  message(m);
+  message("Errno = ");
+  message(strerror(errno));
+  message("\n");
+  exit(1);
+}
+
+struct in_addr multiaddr={0};
+
+int multicast(volmessage *m){
+  static int sockfd=-1;
+  static struct sockaddr_in dest;
+  int destlen=sizeof(dest);
+
+  if(sockfd==-1)
+    if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0)
+      abortm("Could not open multicast socket\n");
+
+  if(multiaddr.s_addr==0)
+    multiaddr.s_addr = inet_addr(MULTI_ADDR);
+      /*inet_aton(MULTI_ADDR,&multiaddr);*/
+
+  memset(&dest,0,sizeof(dest));
+  dest.sin_family=AF_INET;
+  dest.sin_addr.s_addr=multiaddr.s_addr;
+  dest.sin_port=htons(MULTI_PORT);
+
+  if(sendto(sockfd,m,MESSAGE_SIZE,0,(struct sockaddr *)&dest,destlen)<0){
+    message("Multicast send error\n");
+    return(1);
+  }
+  return(0);
+}
+
+int sockfd=-1,volfd;
+static struct sockaddr_in me,you;
+static int youlen=sizeof(you);
+struct in_addr multiaddr;
+
+int opensock(){
+  if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0)
+    abortm("Could not open listen socket\n");
+
+  memset(&me,0,sizeof(me));
+  me.sin_family=AF_INET;
+  me.sin_addr.s_addr=htonl(INADDR_ANY);
+  me.sin_port=htons(LISTEN_PORT);
+
+  if(bind(sockfd,(struct sockaddr *)&me,sizeof(me))<0)
+    abortm("Could not bind to listen port\n");
+
+  listen(sockfd,5);
+  return(sockfd);
+}
+
+/*  -1 blocks
+    0  nonblocking
+    +n timeout 
+*/
+
+int
+fetch (int usec, volmessage *m) {
+  fd_set fdset;
+  int retval;
+  struct timeval tv;
+  if(sockfd==-1)
+    opensock();
+
+  FD_ZERO(&fdset);
+  FD_SET(sockfd, &fdset);
+
+  tv.tv_sec = 0;
+  tv.tv_usec = usec;
+  retval = select(sockfd+1,
+		  &fdset, NULL, NULL,
+		  ((usec == -1) ? NULL : &tv));
+  if (retval == 1) {
+    /* Select got us a single descriptor to play with, as we
+     * expected. Pull a message from it. */
+    int youlen = sizeof(you);
+    retval = recvfrom(sockfd,
+		      m, MESSAGE_SIZE,
+		      0,
+		      (struct sockaddr *)&you, &youlen);
+    if(retval < MESSAGE_SIZE || m->A != 'A' || m->ZE != 'Z') {
+      message("Packet receive error\n");
+      return(0);
+    } else {
+      return(1);
+    }
+  } else {
+    if (retval == 0) {
+      /* This means the timeout finished. */
+    } else if (retval == -1) {
+      fprintf(stderr, "Select error. errno = %d (%s).\n",
+	      errno, strerror(errno));
+    } else {
+      fprintf(stderr, "Select returned unexpected value %d.\n", retval);
+    }
+    /* Whatever error we get, we just return 0. */
+    return(0);
+  }
+}
+
+
+int
+reply (volmessage *m) {
+  int retval;
+  assert(sockfd != -1);
+  
+  retval = sendto(sockfd,
+		  m, MESSAGE_SIZE,
+		  0,
+		  (struct sockaddr *)&you, youlen);
+  if(retval < 0){
+    message("Reply error\n");
+    return(1);
+  }
+  return(0);
+}
+  
+int vol_iterate(int newval){
+  static int val=0;
+  char command[100];
+  
+
+  if (val==newval)
+    return(0);
+
+  if (val<newval) {
+    val+=8;
+    if(val>newval)
+      val=newval;
+  } else {
+    val-=8;
+    if(val<newval)
+      val=newval;
+  }
+  sprintf(command,"/usr/bin/aumix -v %d",val);
+  fprintf(stderr,"Setting volume to %d\n",val);
+  system(command);
+  return(1);
+}
+
+int main(){
+  unsigned char volume=32,mute=0,ip_3=0,ip_2=0,ip_1=0,ip_0=0;
+  int messagestatus=1,volstatus=1;
+  volmessage m;
+
+  system("/usr/bin/aumix -v 0");
+  system("/usr/bin/aumix -i 100");
+  system("/usr/bin/aumix -l 100");
+
+  message("\nSIPB volume daemon running\n");
+
+  /* We want to process messages quickly, then get to the business of
+     controlling ther hardware when no messages are in the queue. 
+     The select on the message fetch also does our timing for volume
+     iteration */
+
+  while(1){
+    if(messagestatus==1)
+      /* Messages likely pending.  Process quickly */
+      messagestatus=fetch(0,&m);
+    else
+      if(volstatus==1){
+	/* No messages, but volume to be updated */
+	messagestatus=fetch(50,&m);
+	if(messagestatus==0)volstatus=vol_iterate(mute?0:volume);
+      }else{
+	/* Steady state.  Wait (almost) forever until a message */
+	m.ip_0=ip_0;
+	m.ip_1=ip_1;
+	m.ip_2=ip_2;
+	m.ip_3=ip_3;
+	m.A='A';
+	m.request='V';
+	m.value=volume;
+	m.ZE='Z';
+    	multicast(&m);
+	m.A='A';
+	m.request='M';
+	m.value=mute;
+	m.ZE='Z';
+    	multicast(&m);
+	messagestatus=fetch(400000,&m);
+      }
+
+    if(messagestatus){
+      int error=0;
+      m.ip_3=(ntohl(you.sin_addr.s_addr)>>24)&0xff;
+      m.ip_2=(ntohl(you.sin_addr.s_addr)>>16)&0xff;
+      m.ip_1=(ntohl(you.sin_addr.s_addr)>>8)&0xff;
+      m.ip_0=ntohl(you.sin_addr.s_addr)&0xff;
+
+      if(m.ip_3!=18 || m.ip_2!=OUR_SUBNET){
+	error=1;
+      }
+
+      switch(m.request){
+      case 'V':
+	if(error){
+	  m.value=error;
+	  m.ZE='E';
+	  reply(&m);
+	  message("Request from off subnet rejected\n");
+	}else{
+	  ip_0=m.ip_0;
+	  ip_1=m.ip_1;
+	  ip_2=m.ip_2;
+	  ip_3=m.ip_3;
+	  volume=m.value;
+	  reply(&m);
+	  multicast(&m);
+	  volstatus=1;
+	}
+	break;
+      case 'M':
+	if(error){
+	  m.value=error;
+	  m.ZE='E';
+	  reply(&m);	  
+	  message("Request from off subnet rejected\n");
+	}else{
+	  ip_0=m.ip_0;
+	  ip_1=m.ip_1;
+	  ip_2=m.ip_2;
+	  ip_3=m.ip_3;
+	  mute=(m.value?1:0);
+	  m.value=mute;
+	  reply(&m);
+	  multicast(&m);
+	  volstatus=1;
+	}
+	break;
+      case 'Q':
+	m.ip_0=ip_0;
+	m.ip_1=ip_1;
+	m.ip_2=ip_2;
+	m.ip_3=ip_3;
+	m.request='V';
+	m.value=volume;
+	reply(&m);
+	m.request='M';
+	m.value=mute;
+	reply(&m);
+	break;
+      default:
+	reply((volmessage *)"AE20000E");
+	break;
+      }
+    }
+  }
+}
Index: gutenbach-remctl/volume
===================================================================
--- gutenbach-remctl/volume	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ gutenbach-remctl/volume	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
@@ -0,0 +1,25 @@
+volume set  /usr/lib/gutenbach/remctl/volume-set	 ANYUSER 
+volume get  /usr/lib/gutenbach/remctl/volume-get    ANYUSER
+volume show /usr/lib/gutenbach/remctl/volume-show   ANYUSER
+volume mute /usr/lib/gutenbach/remctl/volume-mute   ANYUSER 
+volume up   /usr/lib/gutenbach/remctl/volume-up     ANYUSER 
+volume down /usr/lib/gutenbach/remctl/volume-down   ANYUSER 
+volume is-muted /usr/lib/gutenbach/remctl/volume-is-muted   ANYUSER 
+
+v set  /usr/lib/gutenbach/remctl/volume-set    ANYUSER 
+v get  /usr/lib/gutenbach/remctl/volume-get    ANYUSER
+v show /usr/lib/gutenbach/remctl/volume-show   ANYUSER
+v mute /usr/lib/gutenbach/remctl/volume-mute   ANYUSER 
+v up   /usr/lib/gutenbach/remctl/volume-up     ANYUSER 
+v down /usr/lib/gutenbach/remctl/volume-down   ANYUSER 
+v is-muted /usr/lib/gutenbach/remctl/volume-is-muted   ANYUSER 
+
+v u /usr/lib/gutenbach/remctl/volume-up   ANYUSER
+v d /usr/lib/gutenbach/remctl/volume-down ANYUSER
+
+volume u /usr/lib/gutenbach/remctl/volume-up   ANYUSER
+volume d /usr/lib/gutenbach/remctl/volume-down ANYUSER
+
+volume help /usr/lib/gutenbach/remctl/volume-help ANYUSER
+v      help /usr/lib/gutenbach/remctl/volume-help ANYUSER
+help   ALL  /usr/lib/gutenbach/remctl/volume-help ANYUSER
Index: gutenbach-remctl/volume-down
===================================================================
--- gutenbach-remctl/volume-down	(revision 6bb3ffb249dc254896b2f89be72367ae76fad5e9)
+++ gutenbach-remctl/volume-down	(revision 6bb3ffb249dc254896b2f89be72367ae76fad5e9)
@@ -0,0 +1,3 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+volume-set . $(( $(volume-get .) - 1 ))
Index: gutenbach-remctl/volume-get
===================================================================
--- gutenbach-remctl/volume-get	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ gutenbach-remctl/volume-get	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
@@ -0,0 +1,8 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+
+mixer=$(/usr/lib/gutenbach/gutenbach-get-config mixer)
+channel=$(/usr/lib/gutenbach/gutenbach-get-config channel)
+
+amixer get $mixer | grep "$channel" \
+ | perl -lne 'print $1 if (/: Playback ([0-9]+)/)'
Index: gutenbach-remctl/volume-help
===================================================================
--- gutenbach-remctl/volume-help	(revision 6bb3ffb249dc254896b2f89be72367ae76fad5e9)
+++ gutenbach-remctl/volume-help	(revision 6bb3ffb249dc254896b2f89be72367ae76fad5e9)
@@ -0,0 +1,15 @@
+#!/bin/bash
+PATH="$(dirname $0):$PATH"
+cat <<EOF
+Usage: remctl zsr.mit.edu <command> <subcommand> [..args..]
+Commands:
+  volume up        - increment volume
+  volume down      - decrement volume
+  volume get       - get volume
+  volume set [vol] - set volume, in range 0..31
+  volume mute      - zero volume
+  volume help,
+   help <anything> - print this help
+
+'v', 'u', 'd' abbreviate 'volume', 'up', 'down' respectively.
+EOF
Index: gutenbach-remctl/volume-is-muted
===================================================================
--- gutenbach-remctl/volume-is-muted	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ gutenbach-remctl/volume-is-muted	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
@@ -0,0 +1,17 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+
+mixer=$(/usr/lib/gutenbach/gutenbach-get-config mixer)
+channel=$(/usr/lib/gutenbach/gutenbach-get-config channel)
+
+volstat=$(amixer get $mixer | grep "$channel" \
+ | perl -lne 'print $1 if (/: Playback .*\[(on|off)\]$/)'
+)
+
+case "$volstat" in 
+    on)  result=unmuted;;
+    off) result=muted;;
+    *)   result=unknown;;
+esac
+
+echo $result
Index: gutenbach-remctl/volume-mute
===================================================================
--- gutenbach-remctl/volume-mute	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ gutenbach-remctl/volume-mute	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
@@ -0,0 +1,10 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+
+mixer=$(/usr/lib/gutenbach/gutenbach-get-config mixer)
+channel=$(/usr/lib/gutenbach/gutenbach-get-config channel)
+
+amixer set $mixer toggle > /dev/null
+volume-is-muted
+
+volume-zephyr
Index: gutenbach-remctl/volume-set
===================================================================
--- gutenbach-remctl/volume-set	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ gutenbach-remctl/volume-set	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
@@ -0,0 +1,10 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+
+mixer=$(/usr/lib/gutenbach/gutenbach-get-config mixer)
+channel=$(/usr/lib/gutenbach/gutenbach-get-config channel)
+
+amixer set $mixer "$2" | grep "$channel" \
+ | perl -lne 'print $1 if (/: Playback ([0-9]+)/)'
+
+volume-zephyr
Index: gutenbach-remctl/volume-show
===================================================================
--- gutenbach-remctl/volume-show	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
+++ gutenbach-remctl/volume-show	(revision 53936a5ce93f322effc45c0b2fadff6a4d0478ec)
@@ -0,0 +1,7 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+
+mixer=$(/usr/lib/gutenbach/gutenbach-get-config mixer)
+channel=$(/usr/lib/gutenbach/gutenbach-get-config channel)
+
+amixer get $mixer
Index: gutenbach-remctl/volume-up
===================================================================
--- gutenbach-remctl/volume-up	(revision 6bb3ffb249dc254896b2f89be72367ae76fad5e9)
+++ gutenbach-remctl/volume-up	(revision 6bb3ffb249dc254896b2f89be72367ae76fad5e9)
@@ -0,0 +1,3 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+volume-set . $(( $(volume-get .) + 1 ))
Index: gutenbach-remctl/volume-zephyr
===================================================================
--- gutenbach-remctl/volume-zephyr	(revision 4c91fc6e62ec5d8ebeb5fdf1c2ac40118489f184)
+++ gutenbach-remctl/volume-zephyr	(revision 4c91fc6e62ec5d8ebeb5fdf1c2ac40118489f184)
@@ -0,0 +1,28 @@
+#!/bin/sh
+PATH="$(dirname $0):$PATH"
+class=$(/usr/lib/gutenbach/gutenbach-get-config zephyr-class)
+host=$(/usr/lib/gutenbach/gutenbach-get-config host)
+queue=$(/usr/lib/gutenbach/gutenbach-get-config queue)
+instance="$queue-volume@$host"
+
+mixer=$(/usr/lib/gutenbach/gutenbach-get-config mixer)
+channel=$(/usr/lib/gutenbach/gutenbach-get-config channel)
+
+echo "Class: $class" >> /tmp/gutenbach-remctl.log
+echo "Host: $host" >> /tmp/gutenbach-remctl.log
+echo "Queue: $queue" >> /tmp/gutenbach-remctl.log
+echo "Instance: $instance" >> /tmp/gutenbach-remctl.log
+echo "Mixer: $mixer" >> /tmp/gutenbach-remctl.log
+echo "Channel: $channel" >> /tmp/gutenbach-remctl.log
+
+muted=$(volume-is-muted)
+mute_str=" [$muted]"
+if [ "$muted" = "unmuted" ]; then
+    mute_str=""
+fi
+
+start-stop-daemon --stop --oknodo --pidfile /usr/lib/gutenbach/gutenbach-zephyr >/dev/null 2>&1
+start-stop-daemon --start --pidfile /usr/lib/gutenbach/gutenbach-zephyr --make-pidfile --background --exec /bin/sh -- -c "sleep 10 && zwrite -c $class -i $instance -d -n -m 'volume changed to $(volume-get)$mute_str' > /dev/null" >/dev/null 2>&1
+
+echo "sleep 10 && zwrite -c $class -i $instance -d -n -m 'volume changed to $(volume-get)$mute_str' > /dev/null" >> /tmp/gutenbach-remctl.log
+echo "" >> /tmp/gutenbach-remctl.log
Index: gutenbach-rhythmbox-plugin/README
===================================================================
--- gutenbach-rhythmbox-plugin/README	(revision 5224c0758df7bd0ad87826cab8999d99c420d5af)
+++ gutenbach-rhythmbox-plugin/README	(revision 5224c0758df7bd0ad87826cab8999d99c420d5af)
@@ -0,0 +1,23 @@
+This is a plugin for the Rhythmbox music player, which enables
+Gutenbach users to easily queue music.
+
+Plugin written by Nimrod Gileadi, copyright 2010.
+
+Questions or comments should be directed at gutenbach@mit.edu
+
+INSTALLATION:
+
+1) Move the folder 'pikamp3' and its contents to
+~/.gnome2/rhythmbox/plugins/.  You will have to edit __init.py__,
+replacing "printername" and "hostname" with the desired values (e.g.,
+"sipbmp3" and "zsr.mit.edu").
+
+2) Open rhythmbox, and navigate the 'Edit' menu.  Select 'Plugins'.
+
+3) Check the box next to 'gutenbach'.
+
+4) To queue a song/songs, highlight them, and right click and select
+'Send to gutenbach'.
+
+TODO:
+- turn this into a debian package to auto generate the config file
Index: gutenbach-rhythmbox-plugin/__init__.py
===================================================================
--- gutenbach-rhythmbox-plugin/__init__.py	(revision 61c50434af4f740357ebd00742384ea7fddd18d3)
+++ gutenbach-rhythmbox-plugin/__init__.py	(revision 61c50434af4f740357ebd00742384ea7fddd18d3)
@@ -0,0 +1,69 @@
+"""
+This module implements a Rhythmbox plugin that sends selected track to a
+printer queue. It's meant to be used with gutenbach, a music spooling printer
+server.
+
+The plugin uses lpr to send the files to gutenbach.
+"""
+import gtk
+import rb
+import subprocess
+import urllib
+import urlparse
+
+# UI Change is an extra item added to right-click menu when clicking on tracks
+popup_ui = """
+<ui> 
+  <popup name="BrowserSourceViewPopup"> 
+    <menuitem name="Gutenbach" action="SendToGutenbach"/>
+  </popup>
+</ui>
+"""
+class GutenbachPlugin(rb.Plugin):
+    def __init__(self):
+        rb.Plugin.__init__(self)
+
+    def activate(self, shell):
+        """Adds a new item to the right-click menu which allows
+        sending music files to the gutenbach server.
+        """
+        self.shell = shell
+        # Create action for sending a track to gutenbach
+        action = gtk.Action('SendToGutenbach', _('Send to gutenbach'),
+                _("Queue selected tracks to the gutenbach server."),
+                "")
+        action.connect('activate', self.gutenbach_action, shell)
+        action_group = gtk.ActionGroup('GutenbachActionGroup')
+        action_group.add_action(action)
+        manager = shell.get_ui_manager()
+        manager.insert_action_group(action_group)
+        manager.add_ui_from_string(popup_ui)
+
+        # Default configuration options
+        self.printer = "printername"
+        self.printer_host = "hostname"
+
+    def deactivate(self, shell):
+        del self.shell
+
+    def gutenbach_action(self, action, shell):
+        """An action called when the user clicks the right-click menu item
+        to send a track to gutenbach.
+        This function calls an instance of lpr for each file to be added.
+        """
+        source = shell.get_property("selected-source")
+        # For each track currently selected in the song browser
+        for entry in source.get_entry_view().get_selected_entries():
+            # Only play files that are stored on the user's computer
+            uri = entry.get_playback_uri()
+            p = urlparse.urlparse(urllib.unquote(uri))
+            if p.scheme == "file":
+                path = p.path
+                if self.printer_host:
+                    printer = '@'.join([self.printer, self.printer_host])
+                else:
+                    printer = self.printer
+                command = 'lpr -P %s "%s"' % (printer, path)
+                print "About to run command '%s'" % command
+                subprocess.Popen(command, shell=True)
+
Index: gutenbach-rhythmbox-plugin/gutenbach-prefs.ui
===================================================================
--- gutenbach-rhythmbox-plugin/gutenbach-prefs.ui	(revision 0159c0a25905932d89659336d73d160f46a6b9f1)
+++ gutenbach-rhythmbox-plugin/gutenbach-prefs.ui	(revision 0159c0a25905932d89659336d73d160f46a6b9f1)
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkDialog" id="gutenbach-prefs">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">gutenbach Configuration</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkVBox" id="vbox2">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkLabel" id="printer_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Printer name:</property>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="printer_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                    <property name="text" translatable="yes">pikamp3</property>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox3">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkLabel" id="host_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Printer host:</property>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="host_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">&#x25CF;</property>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="OK">
+                <property name="label" translatable="yes">OK</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">OK</action-widget>
+    </action-widgets>
+  </object>
+</interface>
Index: gutenbach-rhythmbox-plugin/gutenbach.rb-plugin
===================================================================
--- gutenbach-rhythmbox-plugin/gutenbach.rb-plugin	(revision 0159c0a25905932d89659336d73d160f46a6b9f1)
+++ gutenbach-rhythmbox-plugin/gutenbach.rb-plugin	(revision 0159c0a25905932d89659336d73d160f46a6b9f1)
@@ -0,0 +1,7 @@
+[RB Plugin]
+Loader=python
+Module=gutenbach
+IAge=1
+Name=gutenbach
+Description="Print" tunes to the gutenbach server
+Authors=Nimrod Gileadi <nimrodg@mit.edu>
Index: gutenbach-web/gutenbach-web.pl
===================================================================
--- gutenbach-web/gutenbach-web.pl	(revision 3947653fc70ced5e55cda2a368a8d3079602b8eb)
+++ gutenbach-web/gutenbach-web.pl	(revision 3947653fc70ced5e55cda2a368a8d3079602b8eb)
@@ -0,0 +1,57 @@
+use Net::CUPS;
+use Net::CUPS::Destination;
+use Image::ExifTool qw(ImageInfo);
+use CGI ':standard';
+
+use strict;
+use warnings;
+
+my $cups = Net::CUPS->new();
+
+my $printer = $cups->getDestination("buildsmp3");
+#print header();
+print start_html();
+my @jobs = $printer->getJobs( 0, 0 );
+my $job_ref;
+my $jobid;
+my $attr;
+print  <<EOF;
+<TABLE SUMMARY="Job List"> 
+<THEAD> 
+<TR><TH> USER</TH><TH>TITLE</TH><TH>ARTIST</TH><TH>ALBUM</TH></TR> 
+</THEAD>
+<TBODY>  
+EOF
+foreach $jobid(@jobs) 
+{       
+	$job_ref = $printer->getJob($jobid);
+	#print "$job_ref->{ 'id' }\t\t$job_ref->{ 'user'}\t\t$job_ref->{ 'title' }\n";
+	my $filepath = "/var/spool/cups/d00$job_ref->{ 'id' }-001";
+	my $fileinfo = ImageInfo($filepath);
+	my $magic = $fileinfo->{FileType};
+	#print"$job_ref->{ 'user' } is playing:\n";
+	print "<TR VALIGN=\"TOP\">";
+	print "<TD>$job_ref->{ 'user'}</TD>\n";
+	if($magic)
+	{
+	   # print "MAGIC";
+	    if (exists $fileinfo->{'Title'}) {
+		printf("<TD>%s</TD>\n", $fileinfo->{'Title'});
+	    }
+	    foreach my $key (qw/Artist Album AlbumArtist/) {
+		if (exists $fileinfo->{$key}) {
+		    printf("<TD>%s</TD>\n", $fileinfo->{$key}) if exists $fileinfo->{$key};
+		    
+		}
+	    }
+	}
+	else
+	{
+	    print "<TD>$job_ref->{ 'title' }</TD><TD></TD><TD></TD> \n";
+	}
+	print "</TR>";
+
+}
+print "</TBODY>\n</TABLE>";
+	
+print end_html();	    
Index: gutenbach-web/old/.gitignore
===================================================================
--- gutenbach-web/old/.gitignore	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/.gitignore	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,8 @@
+*.pyc
+dispatch.fcgi
+tg2env
+*.pyo
+*~
+.python-eggs/*
+devdata.db
+production.ini
Index: gutenbach-web/old/.htaccess
===================================================================
--- gutenbach-web/old/.htaccess	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/.htaccess	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,22 @@
+AddDefaultCharset utf-8
+
+Options +FollowSymLinks +ExecCGI
+
+# It's very useful to run dispatch.fcgi as a regular CGI module when
+# doing development work, as useful error messages are then dumped
+# to error.log
+#AddHandler cgi-script .fcgi
+#AddHandler fastcgi-script .fcgi
+
+RewriteEngine On
+RewriteRule ^(dispatch\.fcgi/.*)$ - [L]
+RewriteRule ^(static/.*)$ - [L]
+RewriteRule ^toscawidgets/resources/(sipbmp3web/public/.*)$ $1
+RewriteRule ^sipbmp3web/.*$ - [L]
+RewriteRule ^(.*)$ dispatch\.fcgi/$1 [L]
+
+ExpiresActive On
+ExpiresByType image/png "access plus 1 month"
+ExpiresByType application/x-javascript "access plus 1 month"
+ExpiresByType text/css "access plus 1 month"
+
Index: gutenbach-web/old/AUTHORS.txt
===================================================================
--- gutenbach-web/old/AUTHORS.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/AUTHORS.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,30 @@
+﻿jQuery UI Authors (http://ui.jquery.com/about)
+
+This software consists of voluntary contributions made by many
+individuals. For exact contribution history, see the revision history
+and logs, available at http://jquery-ui.googlecode.com/svn/
+
+Brandon Aaron
+Paul Bakaus (paulbakaus.com)
+David Bolter
+Rich Caloggero
+Chi Cheng (cloudream@gmail.com)
+Colin Clark (http://colin.atrc.utoronto.ca/)
+Michelle D'Souza
+Aaron Eisenberger (aaronchi@gmail.com)
+Ariel Flesler
+Bohdan Ganicky
+Scott González
+Marc Grabanski (m@marcgrabanski.com)
+Klaus Hartl (stilbuero.de)
+Scott Jehl
+Cody Lindley
+Eduardo Lundgren (eduardolundgren@gmail.com)
+Todd Parker
+John Resig
+Patty Toland
+Ca-Phun Ung (yelotofu.com)
+Keith Wood (kbwood@virginbroadband.com.au)
+Maggie Costello Wachs
+Richard D. Worth (rdworth.org)
+Jörn Zaefferer (bassistance.de)
Index: gutenbach-web/old/GPL-LICENSE.txt
===================================================================
--- gutenbach-web/old/GPL-LICENSE.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/GPL-LICENSE.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,278 @@
+        GNU GENERAL PUBLIC LICENSE
+           Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+          Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+        GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+          NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
Index: gutenbach-web/old/MANIFEST.in
===================================================================
--- gutenbach-web/old/MANIFEST.in	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/MANIFEST.in	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,4 @@
+recursive-include sipbmp3web/public *
+include sipbmp3web/public/favicon.ico
+recursive-include sipbmp3web/i18n *
+recursive-include sipbmp3web/templates *
Index: gutenbach-web/old/MIT-LICENSE.txt
===================================================================
--- gutenbach-web/old/MIT-LICENSE.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/MIT-LICENSE.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,25 @@
+Copyright (c) 2009 Paul Bakaus, http://ui.jquery.com/
+
+This software consists of voluntary contributions made by many
+individuals (AUTHORS.txt, http://ui.jquery.com/about) For exact
+contribution history, see the revision history and logs, available
+at http://jquery-ui.googlecode.com/svn/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Index: gutenbach-web/old/PKG-INFO
===================================================================
--- gutenbach-web/old/PKG-INFO	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/PKG-INFO	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: sipbmp3-web
+Version: 0.1dev
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
Index: gutenbach-web/old/README.txt
===================================================================
--- gutenbach-web/old/README.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/README.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,14 @@
+This directory exists so that Subversion-based projects can share a single
+copy of the ``ez_setup`` bootstrap module for ``setuptools``, and have it
+automatically updated in their projects when ``setuptools`` is updated.
+
+For your convenience, you may use the following svn:externals definition::
+
+    ez_setup svn://svn.eby-sarna.com/svnroot/ez_setup
+
+You can set this by executing this command in your project directory::
+
+    svn propedit svn:externals .
+
+And then adding the line shown above to the file that comes up for editing.
+Then, whenever you update your project, ``ez_setup`` will be updated as well.
Index: gutenbach-web/old/SOURCES.txt
===================================================================
--- gutenbach-web/old/SOURCES.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/SOURCES.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,100 @@
+MANIFEST.in
+README.txt
+setup.cfg
+setup.py
+sipbmp3_web.egg-info/PKG-INFO
+sipbmp3_web.egg-info/SOURCES.txt
+sipbmp3_web.egg-info/dependency_links.txt
+sipbmp3_web.egg-info/entry_points.txt
+sipbmp3_web.egg-info/paster_plugins.txt
+sipbmp3_web.egg-info/requires.txt
+sipbmp3_web.egg-info/top_level.txt
+sipbmp3web/__init__.py
+sipbmp3web/websetup.py
+sipbmp3web/config/__init__.py
+sipbmp3web/config/app_cfg.py
+sipbmp3web/config/environment.py
+sipbmp3web/config/middleware.py
+sipbmp3web/controllers/__init__.py
+sipbmp3web/controllers/error.py
+sipbmp3web/controllers/root.py
+sipbmp3web/controllers/secure.py
+sipbmp3web/controllers/template.py
+sipbmp3web/i18n/ru/LC_MESSAGES/sipbmp3web.po
+sipbmp3web/lib/__init__.py
+sipbmp3web/lib/app_globals.py
+sipbmp3web/lib/base.py
+sipbmp3web/lib/helpers.py
+sipbmp3web/model/__init__.py
+sipbmp3web/model/auth.py
+sipbmp3web/public/favicon.ico
+sipbmp3web/public/css/sample.css
+sipbmp3web/public/css/style.css
+sipbmp3web/public/images/contentbg.png
+sipbmp3web/public/images/error.png
+sipbmp3web/public/images/header_inner2.png
+sipbmp3web/public/images/headerbg.png
+sipbmp3web/public/images/info.png
+sipbmp3web/public/images/inputbg.png
+sipbmp3web/public/images/loginbg.png
+sipbmp3web/public/images/loginbottombg.png
+sipbmp3web/public/images/loginheader-left.png
+sipbmp3web/public/images/loginheader-right.png
+sipbmp3web/public/images/menu-item-actibg-first.png
+sipbmp3web/public/images/menu-item-actibg.png
+sipbmp3web/public/images/menu-item-border.png
+sipbmp3web/public/images/menubg.png
+sipbmp3web/public/images/ok.png
+sipbmp3web/public/images/pagebg.png
+sipbmp3web/public/images/star.png
+sipbmp3web/public/images/strype2.png
+sipbmp3web/public/images/under_the_hood_blue.png
+sipbmp3web/public/jquery/AUTHORS.txt
+sipbmp3web/public/jquery/GPL-LICENSE.txt
+sipbmp3web/public/jquery/MIT-LICENSE.txt
+sipbmp3web/public/jquery/example.html
+sipbmp3web/public/jquery/jquery-1.3.2.js
+sipbmp3web/public/jquery/jquery-ui-personalized-1.6rc6.js
+sipbmp3web/public/jquery/jquery-ui-personalized-1.6rc6.min.js
+sipbmp3web/public/jquery/version.txt
+sipbmp3web/public/jquery/theme/ui.accordion.css
+sipbmp3web/public/jquery/theme/ui.all.css
+sipbmp3web/public/jquery/theme/ui.base.css
+sipbmp3web/public/jquery/theme/ui.core.css
+sipbmp3web/public/jquery/theme/ui.datepicker.css
+sipbmp3web/public/jquery/theme/ui.dialog.css
+sipbmp3web/public/jquery/theme/ui.progressbar.css
+sipbmp3web/public/jquery/theme/ui.resizable.css
+sipbmp3web/public/jquery/theme/ui.slider.css
+sipbmp3web/public/jquery/theme/ui.tabs.css
+sipbmp3web/public/jquery/theme/ui.theme.css
+sipbmp3web/public/jquery/theme/images/ui-bg_flat_0_aaaaaa_40x100.png
+sipbmp3web/public/jquery/theme/images/ui-bg_flat_75_ffffff_40x100.png
+sipbmp3web/public/jquery/theme/images/ui-bg_glass_55_fbf9ee_1x400.png
+sipbmp3web/public/jquery/theme/images/ui-bg_glass_65_ffffff_1x400.png
+sipbmp3web/public/jquery/theme/images/ui-bg_glass_75_dadada_1x400.png
+sipbmp3web/public/jquery/theme/images/ui-bg_glass_75_e6e6e6_1x400.png
+sipbmp3web/public/jquery/theme/images/ui-bg_glass_95_fef1ec_1x400.png
+sipbmp3web/public/jquery/theme/images/ui-bg_highlight-soft_75_cccccc_1x100.png
+sipbmp3web/public/jquery/theme/images/ui-icons_222222_256x240.png
+sipbmp3web/public/jquery/theme/images/ui-icons_2e83ff_256x240.png
+sipbmp3web/public/jquery/theme/images/ui-icons_454545_256x240.png
+sipbmp3web/public/jquery/theme/images/ui-icons_888888_256x240.png
+sipbmp3web/public/jquery/theme/images/ui-icons_cd0a0a_256x240.png
+sipbmp3web/templates/__init__.py
+sipbmp3web/templates/__init__.pyc
+sipbmp3web/templates/about.html
+sipbmp3web/templates/debug.html
+sipbmp3web/templates/error.html
+sipbmp3web/templates/footer.html
+sipbmp3web/templates/header.html
+sipbmp3web/templates/index.html
+sipbmp3web/templates/master.html
+sipbmp3web/templates/todo.html
+sipbmp3web/tests/__init__.py
+sipbmp3web/tests/test_models.py
+sipbmp3web/tests/functional/__init__.py
+sipbmp3web/tests/functional/test_root.py
+sipbmp3web/widgets/__init__.py
+sipbmp3web/widgets/slider.py
+sipbmp3web/widgets/templates/__init__.py
Index: gutenbach-web/old/about.html
===================================================================
--- gutenbach-web/old/about.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/about.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <xi:include href="master.html" />
+
+<head>
+  <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+  <title>About / sipbmp3 web</title>
+</head>
+
+<body>
+    <div>
+        <p>
+            sipbmp3 is the MP3 spooler for SIPB office, which allows office
+            users to print MP3 files to a server and have them be played
+            on external speakers.
+        </p>
+        <p>
+            To play something, issue the following command on an Athena
+            system:
+        </p>
+        <pre>lpr -Psipbmp3 <kbd>filename.mp3</kbd></pre>
+    </div>
+</body>
+</html>
Index: gutenbach-web/old/app_cfg.py
===================================================================
--- gutenbach-web/old/app_cfg.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/app_cfg.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,46 @@
+from tg.configuration import AppConfig, Bunch
+import sipbmp3web
+from sipbmp3web import model
+from sipbmp3web.lib import app_globals, helpers
+
+base_config = AppConfig()
+base_config.renderers = []
+
+base_config.package = sipbmp3web
+
+#Set the default renderer
+base_config.default_renderer = 'genshi'
+base_config.renderers.append('genshi') 
+# if you want raw speed and have installed chameleon.genshi
+# you should try to use this renderer instead.
+# warning: for the moment chameleon does not handle i18n translations
+#base_config.renderers.append('chameleon_genshi') 
+
+#Configure the base SQLALchemy Setup
+base_config.use_sqlalchemy = True
+base_config.model = sipbmp3web.model
+base_config.DBSession = sipbmp3web.model.DBSession
+
+# Configure the authentication backend
+base_config.auth_backend = 'sqlalchemy'
+base_config.sa_auth.dbsession = model.DBSession
+# what is the class you want to use to search for users in the database
+base_config.sa_auth.user_class = model.User
+# what is the class you want to use to search for groups in the database
+base_config.sa_auth.group_class = model.Group
+# what is the class you want to use to search for permissions in the database
+base_config.sa_auth.permission_class = model.Permission
+
+base_config.sa_auth.cookie_secret = "thisistotallysecretyo"
+
+# override this if you would like to provide a different who plugin for 
+# managing login and logout of your application
+base_config.sa_auth.form_plugin = None
+
+# You may optionally define a page where you want users to be redirected to
+# on login:
+base_config.sa_auth.post_login_url = '/post_login'
+
+# You may optionally define a page where you want users to be redirected to
+# on logout:
+base_config.sa_auth.post_logout_url = '/post_logout'
Index: gutenbach-web/old/app_globals.py
===================================================================
--- gutenbach-web/old/app_globals.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/app_globals.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,13 @@
+"""The application's Globals object"""
+
+class Globals(object):
+    """Globals acts as a container for objects available throughout the
+    life of the application
+    """
+
+    def __init__(self):
+        """One instance of Globals is created during application
+        initialization and is available during requests via the 'g'
+        variable
+        """
+        pass
Index: gutenbach-web/old/auth.py
===================================================================
--- gutenbach-web/old/auth.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/auth.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,161 @@
+import sys
+try:
+    from hashlib import sha1
+except ImportError:
+    sys.exit('ImportError: no module named hashlib\nIf you are on python2.4 this library is not part of python. Please install it. Example: easy_install hashlib')
+import os
+from datetime import datetime
+
+from sqlalchemy import Table, ForeignKey, Column
+from sqlalchemy.types import String, Unicode, UnicodeText, Integer, DateTime, \
+                             Boolean, Float
+from sqlalchemy.orm import relation, backref, synonym
+
+from sipbmp3web.model import DeclarativeBase, metadata, DBSession
+
+
+# This is the association table for the many-to-many relationship between
+# groups and permissions.
+group_permission_table = Table('tg_group_permission', metadata,
+    Column('group_id', Integer, ForeignKey('tg_group.group_id',
+        onupdate="CASCADE", ondelete="CASCADE")),
+    Column('permission_id', Integer, ForeignKey('tg_permission.permission_id',
+        onupdate="CASCADE", ondelete="CASCADE"))
+)
+
+# This is the association table for the many-to-many relationship between
+# groups and members - this is, the memberships.
+user_group_table = Table('tg_user_group', metadata,
+    Column('user_id', Integer, ForeignKey('tg_user.user_id',
+        onupdate="CASCADE", ondelete="CASCADE")),
+    Column('group_id', Integer, ForeignKey('tg_group.group_id',
+        onupdate="CASCADE", ondelete="CASCADE"))
+)
+
+# auth model
+
+class Group(DeclarativeBase):
+    """An ultra-simple group definition.
+    """
+    __tablename__ = 'tg_group'
+
+    group_id = Column(Integer, autoincrement=True, primary_key=True)
+    group_name = Column(Unicode(16), unique=True, nullable=False)
+    display_name = Column(Unicode(255))
+    created = Column(DateTime, default=datetime.now)
+    users = relation('User', secondary=user_group_table, backref='groups')
+
+    def __repr__(self):
+        return '<Group: name=%s>' % self.group_name
+
+    def __unicode__(self):
+        return self.group_name
+
+#
+# The 'info' argument we're passing to the email_address and password columns
+# contain metadata that Rum (http://python-rum.org/) can use generate an
+# admin interface for your models.
+#
+class User(DeclarativeBase):
+    """Reasonably basic User definition. Probably would want additional
+    attributes.
+    """
+    __tablename__ = 'tg_user'
+
+    user_id = Column(Integer, autoincrement=True, primary_key=True)
+    user_name = Column(Unicode(16), unique=True, nullable=False)
+    email_address = Column(Unicode(255), unique=True, nullable=False,
+                           info={'rum': {'field':'Email'}})
+    display_name = Column(Unicode(255))
+    _password = Column('password', Unicode(80),
+                       info={'rum': {'field':'Password'}})
+    created = Column(DateTime, default=datetime.now)
+
+    def __repr__(self):
+        return '<User: email="%s", display name="%s">' % (
+                self.email_address, self.display_name)
+
+    def __unicode__(self):
+        return self.display_name or self.user_name
+
+    @property
+    def permissions(self):
+        perms = set()
+        for g in self.groups:
+            perms = perms | set(g.permissions)
+        return perms
+
+    @classmethod
+    def by_email_address(cls, email):
+        """A class method that can be used to search users
+        based on their email addresses since it is unique.
+        """
+        return DBSession.query(cls).filter(cls.email_address==email).first()
+
+    @classmethod
+    def by_user_name(cls, username):
+        """A class method that permits to search users
+        based on their user_name attribute.
+        """
+        return DBSession.query(cls).filter(cls.user_name==username).first()
+
+
+    def _set_password(self, password):
+        """Hash password on the fly."""
+        hashed_password = password
+
+        if isinstance(password, unicode):
+            password_8bit = password.encode('UTF-8')
+        else:
+            password_8bit = password
+        
+        salt = sha1()
+        salt.update(os.urandom(60))
+        hash = sha1()
+        hash.update(password_8bit + salt.hexdigest())
+        hashed_password = salt.hexdigest() + hash.hexdigest()
+
+        # make sure the hased password is an UTF-8 object at the end of the
+        # process because SQLAlchemy _wants_ a unicode object for Unicode columns
+        if not isinstance(hashed_password, unicode):
+            hashed_password = hashed_password.decode('UTF-8')
+
+        self._password = hashed_password
+
+    def _get_password(self):
+        """returns password
+        """
+        return self._password
+
+    password = synonym('_password', descriptor=property(_get_password,
+                                                        _set_password))
+
+    def validate_password(self, password):
+        """Check the password against existing credentials.
+
+        :param password: the password that was provided by the user to
+            try and authenticate. This is the clear text version that we will
+            need to match against the hashed one in the database.
+        :type password: unicode object.
+        :return: Whether the password is valid.
+        :rtype: bool
+        
+        """
+        hashed_pass = sha1()
+        hashed_pass.update(password + self.password[:40])
+        return self.password[40:] == hashed_pass.hexdigest()
+
+
+class Permission(DeclarativeBase):
+    """A relationship that determines what each Group can do
+    """
+    __tablename__ = 'tg_permission'
+
+    permission_id = Column(Integer, autoincrement=True, primary_key=True)
+    permission_name = Column(Unicode(16), unique=True, nullable=False)
+    description = Column(Unicode(255))
+    groups = relation(Group, secondary=group_permission_table,
+                      backref='permissions')
+
+    def __unicode__(self):
+        return self.permission_name
Index: gutenbach-web/old/base.py
===================================================================
--- gutenbach-web/old/base.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/base.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,36 @@
+"""The base Controller API
+
+Provides the BaseController class for subclassing.
+"""
+from tg import TGController, tmpl_context
+from tg.render import render
+from tg import request
+
+import sipbmp3web.model as model
+
+from pylons.i18n import _, ungettext, N_
+from tw.api import WidgetBunch
+
+class Controller(object):
+    """Base class for a web application's controller.
+
+    Currently, this provides positional parameters functionality
+    via a standard default method.
+    """
+
+class BaseController(TGController):
+    """Base class for the root of a web application.
+
+    Your web application should have one of these. The root of
+    your application is used to compute URLs used by your app.
+    """
+
+    def __call__(self, environ, start_response):
+        """Invoke the Controller"""
+        # TGController.__call__ dispatches to the Controller method
+        # the request is routed to. This routing information is
+        # available in environ['pylons.routes_dict']
+        
+        request.identity = request.environ.get('repoze.who.identity')
+        tmpl_context.identity = request.identity
+        return TGController.__call__(self, environ, start_response)
Index: gutenbach-web/old/controller.template
===================================================================
--- gutenbach-web/old/controller.template	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/controller.template	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,22 @@
+"""Main Controller"""
+# standard library imports
+
+# turbogears imports
+from tg import expose
+#from tg import redirect, validate, flash
+
+# third party imports
+#from pylons.i18n import ugettext as _
+#from repoze.what import predicates
+
+# project specific imports
+from sipbmp3web.lib.base import BaseController
+#from sipbmp3web.model import DBSession, metadata
+
+class SampleController(BaseController):
+    #Uncomment this line if your controller requires an authenticated user
+    #allow_only = authorize.not_anonymous()
+
+    @expose('sipbmp3web.templates.index')
+    def index(self):
+        return dict(page='index')
Index: gutenbach-web/old/debug.html
===================================================================
--- gutenbach-web/old/debug.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/debug.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<xi:include href="master.html" />
+
+<head>
+  <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+  <title>Sample Template, for looking at template locals</title>
+</head>
+
+<body>
+    <h1>All objects from locals():</h1>
+
+    <div py:for="item in sorted(locals()['data'].keys())">
+      ${item}: ${repr(locals()['data'][item])}</div>
+</body>
+</html>
Index: gutenbach-web/old/dependency_links.txt
===================================================================
--- gutenbach-web/old/dependency_links.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/dependency_links.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,1 @@
+
Index: gutenbach-web/old/deployment.ini
===================================================================
--- gutenbach-web/old/deployment.ini	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/deployment.ini	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,95 @@
+#
+# sipbmp3-web - TurboGears configuration
+#
+# The %(here)s variable will be replaced with the parent directory of this file
+#
+[DEFAULT]
+# WARGING == If debug is not set to false, you'll get the interactive
+# debugger on production, which is a huge security hole. 
+
+debug = false
+email_to = you@yourdomain.com
+smtp_server = localhost
+error_email_from = paste@localhost
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 8080
+
+[app:main]
+use = egg:sipbmp3-web
+full_stack = true
+cache_dir = %(here)s/data
+beaker.session.key = sipbmp3web
+beaker.session.secret = ${app_instance_secret}
+app_instance_uuid = ${app_instance_uuid}
+
+# If you'd like to fine-tune the individual locations of the cache data dirs
+# for the Cache data, or the Session saves, un-comment the desired settings
+# here:
+#beaker.cache.data_dir = %(here)s/data/cache
+#beaker.session.data_dir = %(here)s/data/sessions
+# Specify the database for SQLAlchemy to use via
+# turbogears.database
+# %(here) may include a ':' character on Windows environments; this can
+# invalidate the URI when specifying a SQLite db via path name
+sqlalchemy.url = sqlite:///%(here)s/somedb.db
+sqlalchemy.echo = False
+
+# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
+# Debug mode will enable the interactive debugging tool, allowing ANYONE to
+# execute malicious code after an exception is raised.
+#set debug = false
+
+# Logging configuration
+# Add additional loggers, handlers, formatters here
+# Uses python's logging config file format
+# http://docs.python.org/lib/logging-config-fileformat.html
+
+[loggers]
+keys = root, sipbmp3web, sqlalchemy, auth
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+# If you create additional loggers, add them as a key to [loggers]
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sipbmp3web]
+level = INFO
+handlers =
+qualname = sipbmp3web
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither.  (Recommended for production systems.)
+
+
+# A logger for authentication, identification and authorization -- this is
+# repoze.who and repoze.what:
+[logger_auth]
+level = WARN
+handlers = 
+qualname = auth
+
+# If you create additional handlers, add them as a key to [handlers]
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+# If you create additional formatters, add them as a key to [formatters]
+[formatter_generic]
+format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
Index: gutenbach-web/old/development.ini
===================================================================
--- gutenbach-web/old/development.ini	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/development.ini	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,108 @@
+#
+# sipbmp3-web - Pylons development environment configuration
+#
+# The %(here)s variable will be replaced with the parent directory of this file
+#
+# This file is for deployment specific config options -- other configuration
+# that is always required for the app is done in the config directory, 
+# and generally should not be modified by end users. 
+
+[DEFAULT]
+debug = true
+# Uncomment and replace with the address which should receive any error reports
+#email_to = you@yourdomain.com
+smtp_server = localhost
+error_email_from = paste@localhost
+
+[server:main]
+use = egg:Paste#http
+host = 127.0.0.1 
+port = 8080
+
+[app:main]
+use = egg:sipbmp3-web
+full_stack = true
+#lang = ru
+cache_dir = %(here)s/data
+beaker.session.key = sipbmp3web
+beaker.session.secret = somesecret
+sipbmp3.server = zygorthian-space-raiders.mit.edu
+
+# If you'd like to fine-tune the individual locations of the cache data dirs
+# for the Cache data, or the Session saves, un-comment the desired settings
+# here:
+#beaker.cache.data_dir = %(here)s/data/cache
+#beaker.session.data_dir = %(here)s/data/sessions
+
+# pick the form for your database
+# %(here) may include a ':' character on Windows environments; this can
+# invalidate the URI when specifying a SQLite db via path name
+# sqlalchemy.url=postgres://username:password@hostname:port/databasename 
+# sqlalchemy.url=mysql://username:password@hostname:port/databasename
+
+
+# If you have sqlite, here's a simple default to get you started
+# in development
+
+sqlalchemy.url = sqlite:///%(here)s/devdata.db
+#echo shouldn't be used together with the logging module.
+sqlalchemy.echo = false
+sqlalchemy.echo_pool = false
+sqlalchemy.pool_recycle = 3600
+
+# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
+# Debug mode will enable the interactive debugging tool, allowing ANYONE to
+# execute malicious code after an exception is raised.
+#set debug = false
+
+# Logging configuration
+# Add additional loggers, handlers, formatters here
+# Uses python's logging config file format
+# http://docs.python.org/lib/logging-config-fileformat.html
+
+[loggers]
+keys = root, sipbmp3web, sqlalchemy, auth
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+# If you create additional loggers, add them as a key to [loggers]
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sipbmp3web]
+level = DEBUG
+handlers =
+qualname = sipbmp3web
+
+[logger_sqlalchemy]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither.  (Recommended for production systems.)
+
+
+# A logger for authentication, identification and authorization -- this is
+# repoze.who and repoze.what:
+[logger_auth]
+level = WARN
+handlers = 
+qualname = auth
+
+# If you create additional handlers, add them as a key to [handlers]
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+# If you create additional formatters, add them as a key to [formatters]
+[formatter_generic]
+format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
Index: gutenbach-web/old/dispatch.fcgi.in
===================================================================
--- gutenbach-web/old/dispatch.fcgi.in	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/dispatch.fcgi.in	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+# If you're using virtualenv, make sure you edit the shebang to use
+# that binary.
+
+# This is a sample dispatch.fcgi file. You will need to copy it to
+# a web accessible directory, and change some of the configuration
+# options.
+
+# This is an ugly hack for some Fedora specific beahvior
+__requires__ = ''
+
+import sys, os
+
+# Where is the actual application turbogears code located?
+turbogears = "/path/to/turbogears"
+
+sys.path.insert(0, turbogears)
+
+# Load the WSGI application from the config file
+from paste.deploy import loadapp
+wsgi_app = loadapp('config:' + turbogears + '/production.ini')
+
+# Deploy it using FastCGI
+if __name__ == '__main__':
+    from flup.server.fcgi import WSGIServer
+    WSGIServer(wsgi_app).run()
+
+
Index: gutenbach-web/old/entry_points.txt
===================================================================
--- gutenbach-web/old/entry_points.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/entry_points.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,7 @@
+
+    [paste.app_factory]
+    main = sipbmp3web.config.middleware:make_app
+
+    [paste.app_install]
+    main = pylons.util:PylonsInstaller
+    
Index: gutenbach-web/old/environment.py
===================================================================
--- gutenbach-web/old/environment.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/environment.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,4 @@
+from sipbmp3web.config.app_cfg import base_config
+
+#Use base_config to setup the environment loader function
+load_environment = base_config.make_load_environment()
Index: gutenbach-web/old/error.html
===================================================================
--- gutenbach-web/old/error.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/error.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,19 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <xi:include href="master.html" />
+
+<head>
+  <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+  <title>A ${code} Error has Occurred </title>
+</head>
+
+<body>
+<h1>Error ${code}</h1>
+
+<div>${XML(message)}</div>
+</body>
+</html>
Index: gutenbach-web/old/error.py
===================================================================
--- gutenbach-web/old/error.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/error.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,29 @@
+import os.path
+
+import paste.fileapp
+import tg
+from pylons.controllers.util import forward
+from pylons.middleware import error_document_template, media_path
+
+from sipbmp3web.lib.base import BaseController
+
+class ErrorController(object):
+    """Generates error documents as and when they are required.
+
+    The ErrorDocuments middleware forwards to ErrorController when error
+    related status codes are returned from the application.
+
+    This behaviour can be altered by changing the parameters to the
+    ErrorDocuments middleware in your config/middleware.py file.
+    """
+
+    @tg.expose('sipbmp3web.templates.error')
+    def document(self, *args, **kwargs):
+        """Render the error document"""
+        resp = tg.request.environ.get('pylons.original_response')
+        default_message = ("<p>We're sorry but we weren't able to process "
+        " this request.</p>")
+        values = dict(prefix=tg.request.environ.get('SCRIPT_NAME', ''),
+                 code=tg.request.params.get('code', resp.status_int),
+                 message=tg.request.params.get('message', default_message))
+        return values
Index: gutenbach-web/old/example.html
===================================================================
--- gutenbach-web/old/example.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/example.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,267 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+		<title>jQuery UI Example Page</title>
+		<link type="text/css" href="theme/ui.all.css" rel="Stylesheet" />	
+		<script type="text/javascript" src="jquery-1.3.1.js"></script>
+		<script type="text/javascript" src="jquery-ui-personalized-1.6rc6.js"></script>
+		<script type="text/javascript">
+			$(function(){
+				
+				// Slider
+				$('#slider').slider({
+					range: true,
+					values: [17, 67]
+				});
+				
+				//hover states on the static widgets
+				$('#dialog_link, ul#icons li').hover(
+					function() { $(this).addClass('ui-state-hover'); }, 
+					function() { $(this).removeClass('ui-state-hover'); }
+				);
+				
+			});
+		</script>
+		<style type="text/css">
+			/*demo page css*/
+			body{ font: 62.5% "Trebuchet MS", sans-serif; margin: 50px;}
+			.demoHeaders { margin-top: 2em; }
+			#dialog_link {padding: .4em 1em .4em 20px;text-decoration: none;position: relative;}
+			#dialog_link span.ui-icon {margin: 0 5px 0 0;position: absolute;left: .2em;top: 50%;margin-top: -8px;}
+			ul#icons {margin: 0; padding: 0;}
+			ul#icons li {margin: 2px; position: relative; padding: 4px 0; cursor: pointer; float: left;  list-style: none;}
+			ul#icons span.ui-icon {float: left; margin: 0 4px;}
+		</style>	
+	</head>
+	<body>
+	<h1>Welcome to jQuery UI!</h1>
+	<p style="font-size: 1.3em; line-height: 1.5; margin: 1em 0; width: 50%;">This page demonstrates the widgets you downloaded using the theme you selected in the download builder. We've included and linked to uncompressed versions of <a href="jquery-1.3.1.js">jQuery</a>, your personalized copy of <a href="jquery-ui-personalized-1.6rc6.js">jQuery UI (jquery-ui-personalized-1.6rc6.js)</a>, and <a href="theme/ui.all.css">ui.all.css</a> which imports the entire jQuery UI CSS Framework. You can choose to link a subset of the CSS Framework depending on your needs. </p>
+	<p style="font-size: 1.2em; line-height: 1.5; margin: 1em 0; width: 50%;">You've downloaded components and a theme that are compatible with jQuery 1.3+. Please make sure you are using jQuery 1.3+ in your production environment. <em>If you need jQuery UI components that work with an earlier version of jQuery, you can choose an older version in the <a href="http://jqueryui.com/download">jQuery UI download builder</a>.</em></p>	
+
+	<p style="font-weight: bold; margin: 2em 0 1em; font-size: 1.3em;">YOUR COMPONENTS:</p>
+			
+				
+				
+		<h2 class="demoHeaders">Framework Icons (content color preview)</h2>
+		<ul id="icons" class="ui-widget ui-helper-clearfix">
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-n"><span class="ui-icon ui-icon-carat-1-n"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-ne"><span class="ui-icon ui-icon-carat-1-ne"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-e"><span class="ui-icon ui-icon-carat-1-e"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-se"><span class="ui-icon ui-icon-carat-1-se"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-s"><span class="ui-icon ui-icon-carat-1-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-sw"><span class="ui-icon ui-icon-carat-1-sw"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-w"><span class="ui-icon ui-icon-carat-1-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-nw"><span class="ui-icon ui-icon-carat-1-nw"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-carat-2-n-s"><span class="ui-icon ui-icon-carat-2-n-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-carat-2-e-w"><span class="ui-icon ui-icon-carat-2-e-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-n"><span class="ui-icon ui-icon-triangle-1-n"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-ne"><span class="ui-icon ui-icon-triangle-1-ne"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-e"><span class="ui-icon ui-icon-triangle-1-e"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-se"><span class="ui-icon ui-icon-triangle-1-se"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-s"><span class="ui-icon ui-icon-triangle-1-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-sw"><span class="ui-icon ui-icon-triangle-1-sw"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-w"><span class="ui-icon ui-icon-triangle-1-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-nw"><span class="ui-icon ui-icon-triangle-1-nw"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-2-n-s"><span class="ui-icon ui-icon-triangle-2-n-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-2-e-w"><span class="ui-icon ui-icon-triangle-2-e-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-n"><span class="ui-icon ui-icon-arrow-1-n"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-ne"><span class="ui-icon ui-icon-arrow-1-ne"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-e"><span class="ui-icon ui-icon-arrow-1-e"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-se"><span class="ui-icon ui-icon-arrow-1-se"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-s"><span class="ui-icon ui-icon-arrow-1-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-sw"><span class="ui-icon ui-icon-arrow-1-sw"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-w"><span class="ui-icon ui-icon-arrow-1-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-nw"><span class="ui-icon ui-icon-arrow-1-nw"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-2-n-s"><span class="ui-icon ui-icon-arrow-2-n-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-2-ne-sw"><span class="ui-icon ui-icon-arrow-2-ne-sw"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-2-e-w"><span class="ui-icon ui-icon-arrow-2-e-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-2-se-nw"><span class="ui-icon ui-icon-arrow-2-se-nw"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowstop-1-n"><span class="ui-icon ui-icon-arrowstop-1-n"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowstop-1-e"><span class="ui-icon ui-icon-arrowstop-1-e"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowstop-1-s"><span class="ui-icon ui-icon-arrowstop-1-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowstop-1-w"><span class="ui-icon ui-icon-arrowstop-1-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-n"><span class="ui-icon ui-icon-arrowthick-1-n"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-ne"><span class="ui-icon ui-icon-arrowthick-1-ne"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-e"><span class="ui-icon ui-icon-arrowthick-1-e"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-se"><span class="ui-icon ui-icon-arrowthick-1-se"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-s"><span class="ui-icon ui-icon-arrowthick-1-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-sw"><span class="ui-icon ui-icon-arrowthick-1-sw"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-w"><span class="ui-icon ui-icon-arrowthick-1-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-nw"><span class="ui-icon ui-icon-arrowthick-1-nw"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-2-n-s"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-2-ne-sw"><span class="ui-icon ui-icon-arrowthick-2-ne-sw"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-2-e-w"><span class="ui-icon ui-icon-arrowthick-2-e-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-2-se-nw"><span class="ui-icon ui-icon-arrowthick-2-se-nw"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthickstop-1-n"><span class="ui-icon ui-icon-arrowthickstop-1-n"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthickstop-1-e"><span class="ui-icon ui-icon-arrowthickstop-1-e"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthickstop-1-s"><span class="ui-icon ui-icon-arrowthickstop-1-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthickstop-1-w"><span class="ui-icon ui-icon-arrowthickstop-1-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturnthick-1-w"><span class="ui-icon ui-icon-arrowreturnthick-1-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturnthick-1-n"><span class="ui-icon ui-icon-arrowreturnthick-1-n"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturnthick-1-e"><span class="ui-icon ui-icon-arrowreturnthick-1-e"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturnthick-1-s"><span class="ui-icon ui-icon-arrowreturnthick-1-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturn-1-w"><span class="ui-icon ui-icon-arrowreturn-1-w"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturn-1-n"><span class="ui-icon ui-icon-arrowreturn-1-n"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturn-1-e"><span class="ui-icon ui-icon-arrowreturn-1-e"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturn-1-s"><span class="ui-icon ui-icon-arrowreturn-1-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowrefresh-1-w"><span class="ui-icon ui-icon-arrowrefresh-1-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowrefresh-1-n"><span class="ui-icon ui-icon-arrowrefresh-1-n"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowrefresh-1-e"><span class="ui-icon ui-icon-arrowrefresh-1-e"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowrefresh-1-s"><span class="ui-icon ui-icon-arrowrefresh-1-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-4"><span class="ui-icon ui-icon-arrow-4"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-4-diag"><span class="ui-icon ui-icon-arrow-4-diag"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-extlink"><span class="ui-icon ui-icon-extlink"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-newwin"><span class="ui-icon ui-icon-newwin"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-refresh"><span class="ui-icon ui-icon-refresh"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-shuffle"><span class="ui-icon ui-icon-shuffle"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-transfer-e-w"><span class="ui-icon ui-icon-transfer-e-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-transferthick-e-w"><span class="ui-icon ui-icon-transferthick-e-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-folder-collapsed"><span class="ui-icon ui-icon-folder-collapsed"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-folder-open"><span class="ui-icon ui-icon-folder-open"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-document"><span class="ui-icon ui-icon-document"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-document-b"><span class="ui-icon ui-icon-document-b"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-note"><span class="ui-icon ui-icon-note"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-mail-closed"><span class="ui-icon ui-icon-mail-closed"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-mail-open"><span class="ui-icon ui-icon-mail-open"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-suitcase"><span class="ui-icon ui-icon-suitcase"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-comment"><span class="ui-icon ui-icon-comment"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-person"><span class="ui-icon ui-icon-person"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-print"><span class="ui-icon ui-icon-print"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-trash"><span class="ui-icon ui-icon-trash"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-locked"><span class="ui-icon ui-icon-locked"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-unlocked"><span class="ui-icon ui-icon-unlocked"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-bookmark"><span class="ui-icon ui-icon-bookmark"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-tag"><span class="ui-icon ui-icon-tag"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-home"><span class="ui-icon ui-icon-home"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-flag"><span class="ui-icon ui-icon-flag"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-calculator"><span class="ui-icon ui-icon-calculator"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-cart"><span class="ui-icon ui-icon-cart"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-pencil"><span class="ui-icon ui-icon-pencil"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-clock"><span class="ui-icon ui-icon-clock"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-disk"><span class="ui-icon ui-icon-disk"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-calendar"><span class="ui-icon ui-icon-calendar"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-zoomin"><span class="ui-icon ui-icon-zoomin"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-zoomout"><span class="ui-icon ui-icon-zoomout"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-search"><span class="ui-icon ui-icon-search"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-wrench"><span class="ui-icon ui-icon-wrench"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-gear"><span class="ui-icon ui-icon-gear"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-heart"><span class="ui-icon ui-icon-heart"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-star"><span class="ui-icon ui-icon-star"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-link"><span class="ui-icon ui-icon-link"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-cancel"><span class="ui-icon ui-icon-cancel"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-plus"><span class="ui-icon ui-icon-plus"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-plusthick"><span class="ui-icon ui-icon-plusthick"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-minus"><span class="ui-icon ui-icon-minus"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-minusthick"><span class="ui-icon ui-icon-minusthick"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-close"><span class="ui-icon ui-icon-close"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-closethick"><span class="ui-icon ui-icon-closethick"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-key"><span class="ui-icon ui-icon-key"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-lightbulb"><span class="ui-icon ui-icon-lightbulb"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-scissors"><span class="ui-icon ui-icon-scissors"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-clipboard"><span class="ui-icon ui-icon-clipboard"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-copy"><span class="ui-icon ui-icon-copy"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-contact"><span class="ui-icon ui-icon-contact"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-image"><span class="ui-icon ui-icon-image"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-video"><span class="ui-icon ui-icon-video"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-script"><span class="ui-icon ui-icon-script"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-alert"><span class="ui-icon ui-icon-alert"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-info"><span class="ui-icon ui-icon-info"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-notice"><span class="ui-icon ui-icon-notice"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-help"><span class="ui-icon ui-icon-help"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-check"><span class="ui-icon ui-icon-check"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-bullet"><span class="ui-icon ui-icon-bullet"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-radio-off"><span class="ui-icon ui-icon-radio-off"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-radio-on"><span class="ui-icon ui-icon-radio-on"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-pin-w"><span class="ui-icon ui-icon-pin-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-pin-s"><span class="ui-icon ui-icon-pin-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-play"><span class="ui-icon ui-icon-play"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-pause"><span class="ui-icon ui-icon-pause"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-seek-next"><span class="ui-icon ui-icon-seek-next"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-seek-prev"><span class="ui-icon ui-icon-seek-prev"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-seek-end"><span class="ui-icon ui-icon-seek-end"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-seek-first"><span class="ui-icon ui-icon-seek-first"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-stop"><span class="ui-icon ui-icon-stop"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-eject"><span class="ui-icon ui-icon-eject"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-volume-off"><span class="ui-icon ui-icon-volume-off"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-volume-on"><span class="ui-icon ui-icon-volume-on"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-power"><span class="ui-icon ui-icon-power"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-signal-diag"><span class="ui-icon ui-icon-signal-diag"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-signal"><span class="ui-icon ui-icon-signal"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-battery-0"><span class="ui-icon ui-icon-battery-0"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-battery-1"><span class="ui-icon ui-icon-battery-1"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-battery-2"><span class="ui-icon ui-icon-battery-2"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-battery-3"><span class="ui-icon ui-icon-battery-3"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-plus"><span class="ui-icon ui-icon-circle-plus"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-minus"><span class="ui-icon ui-icon-circle-minus"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-close"><span class="ui-icon ui-icon-circle-close"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-triangle-e"><span class="ui-icon ui-icon-circle-triangle-e"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-triangle-s"><span class="ui-icon ui-icon-circle-triangle-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-triangle-w"><span class="ui-icon ui-icon-circle-triangle-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-triangle-n"><span class="ui-icon ui-icon-circle-triangle-n"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-e"><span class="ui-icon ui-icon-circle-arrow-e"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-s"><span class="ui-icon ui-icon-circle-arrow-s"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-w"><span class="ui-icon ui-icon-circle-arrow-w"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-n"><span class="ui-icon ui-icon-circle-arrow-n"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-zoomin"><span class="ui-icon ui-icon-circle-zoomin"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-zoomout"><span class="ui-icon ui-icon-circle-zoomout"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-check"><span class="ui-icon ui-icon-circle-check"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circlesmall-plus"><span class="ui-icon ui-icon-circlesmall-plus"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circlesmall-minus"><span class="ui-icon ui-icon-circlesmall-minus"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-circlesmall-close"><span class="ui-icon ui-icon-circlesmall-close"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-squaresmall-plus"><span class="ui-icon ui-icon-squaresmall-plus"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-squaresmall-minus"><span class="ui-icon ui-icon-squaresmall-minus"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-squaresmall-close"><span class="ui-icon ui-icon-squaresmall-close"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-grip-dotted-vertical"><span class="ui-icon ui-icon-grip-dotted-vertical"></span></li>
+		
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-grip-dotted-horizontal"><span class="ui-icon ui-icon-grip-dotted-horizontal"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-grip-solid-vertical"><span class="ui-icon ui-icon-grip-solid-vertical"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-grip-solid-horizontal"><span class="ui-icon ui-icon-grip-solid-horizontal"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-gripsmall-diagonal-se"><span class="ui-icon ui-icon-gripsmall-diagonal-se"></span></li>
+		<li class="ui-state-default ui-corner-all" title=".ui-icon-grip-diagonal-se"><span class="ui-icon ui-icon-grip-diagonal-se"></span></li>
+		</ul>
+
+	
+		<!-- Slider -->
+		<h2 class="demoHeaders">Slider</h2>
+		<div id="slider"></div>
+			
+		<!-- Highlight / Error -->
+		<h2 class="demoHeaders">Highlight / Error</h2>
+		<div class="ui-widget">
+			<div class="ui-state-highlight ui-corner-all" style="margin-top: 20px; padding: 0 .7em;"> 
+				<p><span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>
+				<strong>Hey!</strong> Sample ui-state-highlight style.</p>
+			</div>
+		</div>
+		<br/>
+		<div class="ui-widget">
+			<div class="ui-state-error ui-corner-all" style="padding: 0 .7em;"> 
+				<p><span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span> 
+				<strong>Alert:</strong> Sample ui-state-error style.</p>
+			</div>
+		</div>
+
+	</body>
+</html>
+
+
Index: gutenbach-web/old/footer.html
===================================================================
--- gutenbach-web/old/footer.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/footer.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,10 @@
+<html xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      py:strip="">
+<py:def function="footer">
+<div id="footer"> 
+    <div><a href="http://www.turbogears.org/2.0/">Powered by TurboGears 2</a></div>
+    <div class="clearingdiv"></div>
+</div>
+</py:def>
+</html>
Index: gutenbach-web/old/header.html
===================================================================
--- gutenbach-web/old/header.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/header.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,12 @@
+<html xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      py:strip="">
+<py:def function="header">
+  <div id="header">
+  	<h1>
+            sipbmp3 <span class="web">web</span>
+	</h1>
+		<div class="subtitle">what's playing?</div>
+  </div>
+</py:def>
+</html>
Index: gutenbach-web/old/helpers.py
===================================================================
--- gutenbach-web/old/helpers.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/helpers.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,1 @@
+from webhelpers import date, feedgenerator, html, number, misc, text 
Index: gutenbach-web/old/index.html
===================================================================
--- gutenbach-web/old/index.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/index.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <xi:include href="master.html" />
+
+<head>
+  <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+  <title>sipbmp3 web</title>
+</head>
+
+<body>
+    <div id="status">
+        <p>Currently playing:</p>
+        <pre>$playing</pre>
+        <p>The volume is <span id="volume">$volume</span></p>
+        <div py:replace="volume_form(volume_data)"></div>
+    </div>
+    <div class="clearingdiv" />
+</body>
+</html>
Index: gutenbach-web/old/jquery-1.3.2.js
===================================================================
--- gutenbach-web/old/jquery-1.3.2.js	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/jquery-1.3.2.js	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,4376 @@
+/*!
+ * jQuery JavaScript Library v1.3.2
+ * http://jquery.com/
+ *
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ *
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
+ */
+(function(){
+
+var 
+	// Will speed up references to window, and allows munging its name.
+	window = this,
+	// Will speed up references to undefined, and allows munging its name.
+	undefined,
+	// Map over jQuery in case of overwrite
+	_jQuery = window.jQuery,
+	// Map over the $ in case of overwrite
+	_$ = window.$,
+
+	jQuery = window.jQuery = window.$ = function( selector, context ) {
+		// The jQuery object is actually just the init constructor 'enhanced'
+		return new jQuery.fn.init( selector, context );
+	},
+
+	// A simple way to check for HTML strings or ID strings
+	// (both of which we optimize for)
+	quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
+	// Is it a simple selector
+	isSimple = /^.[^:#\[\.,]*$/;
+
+jQuery.fn = jQuery.prototype = {
+	init: function( selector, context ) {
+		// Make sure that a selection was provided
+		selector = selector || document;
+
+		// Handle $(DOMElement)
+		if ( selector.nodeType ) {
+			this[0] = selector;
+			this.length = 1;
+			this.context = selector;
+			return this;
+		}
+		// Handle HTML strings
+		if ( typeof selector === "string" ) {
+			// Are we dealing with HTML string or an ID?
+			var match = quickExpr.exec( selector );
+
+			// Verify a match, and that no context was specified for #id
+			if ( match && (match[1] || !context) ) {
+
+				// HANDLE: $(html) -> $(array)
+				if ( match[1] )
+					selector = jQuery.clean( [ match[1] ], context );
+
+				// HANDLE: $("#id")
+				else {
+					var elem = document.getElementById( match[3] );
+
+					// Handle the case where IE and Opera return items
+					// by name instead of ID
+					if ( elem && elem.id != match[3] )
+						return jQuery().find( selector );
+
+					// Otherwise, we inject the element directly into the jQuery object
+					var ret = jQuery( elem || [] );
+					ret.context = document;
+					ret.selector = selector;
+					return ret;
+				}
+
+			// HANDLE: $(expr, [context])
+			// (which is just equivalent to: $(content).find(expr)
+			} else
+				return jQuery( context ).find( selector );
+
+		// HANDLE: $(function)
+		// Shortcut for document ready
+		} else if ( jQuery.isFunction( selector ) )
+			return jQuery( document ).ready( selector );
+
+		// Make sure that old selector state is passed along
+		if ( selector.selector && selector.context ) {
+			this.selector = selector.selector;
+			this.context = selector.context;
+		}
+
+		return this.setArray(jQuery.isArray( selector ) ?
+			selector :
+			jQuery.makeArray(selector));
+	},
+
+	// Start with an empty selector
+	selector: "",
+
+	// The current version of jQuery being used
+	jquery: "1.3.2",
+
+	// The number of elements contained in the matched element set
+	size: function() {
+		return this.length;
+	},
+
+	// Get the Nth element in the matched element set OR
+	// Get the whole matched element set as a clean array
+	get: function( num ) {
+		return num === undefined ?
+
+			// Return a 'clean' array
+			Array.prototype.slice.call( this ) :
+
+			// Return just the object
+			this[ num ];
+	},
+
+	// Take an array of elements and push it onto the stack
+	// (returning the new matched element set)
+	pushStack: function( elems, name, selector ) {
+		// Build a new jQuery matched element set
+		var ret = jQuery( elems );
+
+		// Add the old object onto the stack (as a reference)
+		ret.prevObject = this;
+
+		ret.context = this.context;
+
+		if ( name === "find" )
+			ret.selector = this.selector + (this.selector ? " " : "") + selector;
+		else if ( name )
+			ret.selector = this.selector + "." + name + "(" + selector + ")";
+
+		// Return the newly-formed element set
+		return ret;
+	},
+
+	// Force the current matched set of elements to become
+	// the specified array of elements (destroying the stack in the process)
+	// You should use pushStack() in order to do this, but maintain the stack
+	setArray: function( elems ) {
+		// Resetting the length to 0, then using the native Array push
+		// is a super-fast way to populate an object with array-like properties
+		this.length = 0;
+		Array.prototype.push.apply( this, elems );
+
+		return this;
+	},
+
+	// Execute a callback for every element in the matched set.
+	// (You can seed the arguments with an array of args, but this is
+	// only used internally.)
+	each: function( callback, args ) {
+		return jQuery.each( this, callback, args );
+	},
+
+	// Determine the position of an element within
+	// the matched set of elements
+	index: function( elem ) {
+		// Locate the position of the desired element
+		return jQuery.inArray(
+			// If it receives a jQuery object, the first element is used
+			elem && elem.jquery ? elem[0] : elem
+		, this );
+	},
+
+	attr: function( name, value, type ) {
+		var options = name;
+
+		// Look for the case where we're accessing a style value
+		if ( typeof name === "string" )
+			if ( value === undefined )
+				return this[0] && jQuery[ type || "attr" ]( this[0], name );
+
+			else {
+				options = {};
+				options[ name ] = value;
+			}
+
+		// Check to see if we're setting style values
+		return this.each(function(i){
+			// Set all the styles
+			for ( name in options )
+				jQuery.attr(
+					type ?
+						this.style :
+						this,
+					name, jQuery.prop( this, options[ name ], type, i, name )
+				);
+		});
+	},
+
+	css: function( key, value ) {
+		// ignore negative width and height values
+		if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
+			value = undefined;
+		return this.attr( key, value, "curCSS" );
+	},
+
+	text: function( text ) {
+		if ( typeof text !== "object" && text != null )
+			return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+
+		var ret = "";
+
+		jQuery.each( text || this, function(){
+			jQuery.each( this.childNodes, function(){
+				if ( this.nodeType != 8 )
+					ret += this.nodeType != 1 ?
+						this.nodeValue :
+						jQuery.fn.text( [ this ] );
+			});
+		});
+
+		return ret;
+	},
+
+	wrapAll: function( html ) {
+		if ( this[0] ) {
+			// The elements to wrap the target around
+			var wrap = jQuery( html, this[0].ownerDocument ).clone();
+
+			if ( this[0].parentNode )
+				wrap.insertBefore( this[0] );
+
+			wrap.map(function(){
+				var elem = this;
+
+				while ( elem.firstChild )
+					elem = elem.firstChild;
+
+				return elem;
+			}).append(this);
+		}
+
+		return this;
+	},
+
+	wrapInner: function( html ) {
+		return this.each(function(){
+			jQuery( this ).contents().wrapAll( html );
+		});
+	},
+
+	wrap: function( html ) {
+		return this.each(function(){
+			jQuery( this ).wrapAll( html );
+		});
+	},
+
+	append: function() {
+		return this.domManip(arguments, true, function(elem){
+			if (this.nodeType == 1)
+				this.appendChild( elem );
+		});
+	},
+
+	prepend: function() {
+		return this.domManip(arguments, true, function(elem){
+			if (this.nodeType == 1)
+				this.insertBefore( elem, this.firstChild );
+		});
+	},
+
+	before: function() {
+		return this.domManip(arguments, false, function(elem){
+			this.parentNode.insertBefore( elem, this );
+		});
+	},
+
+	after: function() {
+		return this.domManip(arguments, false, function(elem){
+			this.parentNode.insertBefore( elem, this.nextSibling );
+		});
+	},
+
+	end: function() {
+		return this.prevObject || jQuery( [] );
+	},
+
+	// For internal use only.
+	// Behaves like an Array's method, not like a jQuery method.
+	push: [].push,
+	sort: [].sort,
+	splice: [].splice,
+
+	find: function( selector ) {
+		if ( this.length === 1 ) {
+			var ret = this.pushStack( [], "find", selector );
+			ret.length = 0;
+			jQuery.find( selector, this[0], ret );
+			return ret;
+		} else {
+			return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){
+				return jQuery.find( selector, elem );
+			})), "find", selector );
+		}
+	},
+
+	clone: function( events ) {
+		// Do the clone
+		var ret = this.map(function(){
+			if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
+				// IE copies events bound via attachEvent when
+				// using cloneNode. Calling detachEvent on the
+				// clone will also remove the events from the orignal
+				// In order to get around this, we use innerHTML.
+				// Unfortunately, this means some modifications to
+				// attributes in IE that are actually only stored
+				// as properties will not be copied (such as the
+				// the name attribute on an input).
+				var html = this.outerHTML;
+				if ( !html ) {
+					var div = this.ownerDocument.createElement("div");
+					div.appendChild( this.cloneNode(true) );
+					html = div.innerHTML;
+				}
+
+				return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
+			} else
+				return this.cloneNode(true);
+		});
+
+		// Copy the events from the original to the clone
+		if ( events === true ) {
+			var orig = this.find("*").andSelf(), i = 0;
+
+			ret.find("*").andSelf().each(function(){
+				if ( this.nodeName !== orig[i].nodeName )
+					return;
+
+				var events = jQuery.data( orig[i], "events" );
+
+				for ( var type in events ) {
+					for ( var handler in events[ type ] ) {
+						jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+					}
+				}
+
+				i++;
+			});
+		}
+
+		// Return the cloned set
+		return ret;
+	},
+
+	filter: function( selector ) {
+		return this.pushStack(
+			jQuery.isFunction( selector ) &&
+			jQuery.grep(this, function(elem, i){
+				return selector.call( elem, i );
+			}) ||
+
+			jQuery.multiFilter( selector, jQuery.grep(this, function(elem){
+				return elem.nodeType === 1;
+			}) ), "filter", selector );
+	},
+
+	closest: function( selector ) {
+		var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
+			closer = 0;
+
+		return this.map(function(){
+			var cur = this;
+			while ( cur && cur.ownerDocument ) {
+				if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
+					jQuery.data(cur, "closest", closer);
+					return cur;
+				}
+				cur = cur.parentNode;
+				closer++;
+			}
+		});
+	},
+
+	not: function( selector ) {
+		if ( typeof selector === "string" )
+			// test special case where just one selector is passed in
+			if ( isSimple.test( selector ) )
+				return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector );
+			else
+				selector = jQuery.multiFilter( selector, this );
+
+		var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
+		return this.filter(function() {
+			return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
+		});
+	},
+
+	add: function( selector ) {
+		return this.pushStack( jQuery.unique( jQuery.merge(
+			this.get(),
+			typeof selector === "string" ?
+				jQuery( selector ) :
+				jQuery.makeArray( selector )
+		)));
+	},
+
+	is: function( selector ) {
+		return !!selector && jQuery.multiFilter( selector, this ).length > 0;
+	},
+
+	hasClass: function( selector ) {
+		return !!selector && this.is( "." + selector );
+	},
+
+	val: function( value ) {
+		if ( value === undefined ) {			
+			var elem = this[0];
+
+			if ( elem ) {
+				if( jQuery.nodeName( elem, 'option' ) )
+					return (elem.attributes.value || {}).specified ? elem.value : elem.text;
+				
+				// We need to handle select boxes special
+				if ( jQuery.nodeName( elem, "select" ) ) {
+					var index = elem.selectedIndex,
+						values = [],
+						options = elem.options,
+						one = elem.type == "select-one";
+
+					// Nothing was selected
+					if ( index < 0 )
+						return null;
+
+					// Loop through all the selected options
+					for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+						var option = options[ i ];
+
+						if ( option.selected ) {
+							// Get the specifc value for the option
+							value = jQuery(option).val();
+
+							// We don't need an array for one selects
+							if ( one )
+								return value;
+
+							// Multi-Selects return an array
+							values.push( value );
+						}
+					}
+
+					return values;				
+				}
+
+				// Everything else, we just grab the value
+				return (elem.value || "").replace(/\r/g, "");
+
+			}
+
+			return undefined;
+		}
+
+		if ( typeof value === "number" )
+			value += '';
+
+		return this.each(function(){
+			if ( this.nodeType != 1 )
+				return;
+
+			if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) )
+				this.checked = (jQuery.inArray(this.value, value) >= 0 ||
+					jQuery.inArray(this.name, value) >= 0);
+
+			else if ( jQuery.nodeName( this, "select" ) ) {
+				var values = jQuery.makeArray(value);
+
+				jQuery( "option", this ).each(function(){
+					this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
+						jQuery.inArray( this.text, values ) >= 0);
+				});
+
+				if ( !values.length )
+					this.selectedIndex = -1;
+
+			} else
+				this.value = value;
+		});
+	},
+
+	html: function( value ) {
+		return value === undefined ?
+			(this[0] ?
+				this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
+				null) :
+			this.empty().append( value );
+	},
+
+	replaceWith: function( value ) {
+		return this.after( value ).remove();
+	},
+
+	eq: function( i ) {
+		return this.slice( i, +i + 1 );
+	},
+
+	slice: function() {
+		return this.pushStack( Array.prototype.slice.apply( this, arguments ),
+			"slice", Array.prototype.slice.call(arguments).join(",") );
+	},
+
+	map: function( callback ) {
+		return this.pushStack( jQuery.map(this, function(elem, i){
+			return callback.call( elem, i, elem );
+		}));
+	},
+
+	andSelf: function() {
+		return this.add( this.prevObject );
+	},
+
+	domManip: function( args, table, callback ) {
+		if ( this[0] ) {
+			var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(),
+				scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ),
+				first = fragment.firstChild;
+
+			if ( first )
+				for ( var i = 0, l = this.length; i < l; i++ )
+					callback.call( root(this[i], first), this.length > 1 || i > 0 ?
+							fragment.cloneNode(true) : fragment );
+		
+			if ( scripts )
+				jQuery.each( scripts, evalScript );
+		}
+
+		return this;
+		
+		function root( elem, cur ) {
+			return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ?
+				(elem.getElementsByTagName("tbody")[0] ||
+				elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+				elem;
+		}
+	}
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+function evalScript( i, elem ) {
+	if ( elem.src )
+		jQuery.ajax({
+			url: elem.src,
+			async: false,
+			dataType: "script"
+		});
+
+	else
+		jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
+
+	if ( elem.parentNode )
+		elem.parentNode.removeChild( elem );
+}
+
+function now(){
+	return +new Date;
+}
+
+jQuery.extend = jQuery.fn.extend = function() {
+	// copy reference to target object
+	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
+
+	// Handle a deep copy situation
+	if ( typeof target === "boolean" ) {
+		deep = target;
+		target = arguments[1] || {};
+		// skip the boolean and the target
+		i = 2;
+	}
+
+	// Handle case when target is a string or something (possible in deep copy)
+	if ( typeof target !== "object" && !jQuery.isFunction(target) )
+		target = {};
+
+	// extend jQuery itself if only one argument is passed
+	if ( length == i ) {
+		target = this;
+		--i;
+	}
+
+	for ( ; i < length; i++ )
+		// Only deal with non-null/undefined values
+		if ( (options = arguments[ i ]) != null )
+			// Extend the base object
+			for ( var name in options ) {
+				var src = target[ name ], copy = options[ name ];
+
+				// Prevent never-ending loop
+				if ( target === copy )
+					continue;
+
+				// Recurse if we're merging object values
+				if ( deep && copy && typeof copy === "object" && !copy.nodeType )
+					target[ name ] = jQuery.extend( deep, 
+						// Never move original objects, clone them
+						src || ( copy.length != null ? [ ] : { } )
+					, copy );
+
+				// Don't bring in undefined values
+				else if ( copy !== undefined )
+					target[ name ] = copy;
+
+			}
+
+	// Return the modified object
+	return target;
+};
+
+// exclude the following css properties to add px
+var	exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
+	// cache defaultView
+	defaultView = document.defaultView || {},
+	toString = Object.prototype.toString;
+
+jQuery.extend({
+	noConflict: function( deep ) {
+		window.$ = _$;
+
+		if ( deep )
+			window.jQuery = _jQuery;
+
+		return jQuery;
+	},
+
+	// See test/unit/core.js for details concerning isFunction.
+	// Since version 1.3, DOM methods and functions like alert
+	// aren't supported. They return false on IE (#2968).
+	isFunction: function( obj ) {
+		return toString.call(obj) === "[object Function]";
+	},
+
+	isArray: function( obj ) {
+		return toString.call(obj) === "[object Array]";
+	},
+
+	// check if an element is in a (or is an) XML document
+	isXMLDoc: function( elem ) {
+		return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+			!!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
+	},
+
+	// Evalulates a script in a global context
+	globalEval: function( data ) {
+		if ( data && /\S/.test(data) ) {
+			// Inspired by code by Andrea Giammarchi
+			// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
+			var head = document.getElementsByTagName("head")[0] || document.documentElement,
+				script = document.createElement("script");
+
+			script.type = "text/javascript";
+			if ( jQuery.support.scriptEval )
+				script.appendChild( document.createTextNode( data ) );
+			else
+				script.text = data;
+
+			// Use insertBefore instead of appendChild  to circumvent an IE6 bug.
+			// This arises when a base node is used (#2709).
+			head.insertBefore( script, head.firstChild );
+			head.removeChild( script );
+		}
+	},
+
+	nodeName: function( elem, name ) {
+		return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
+	},
+
+	// args is for internal usage only
+	each: function( object, callback, args ) {
+		var name, i = 0, length = object.length;
+
+		if ( args ) {
+			if ( length === undefined ) {
+				for ( name in object )
+					if ( callback.apply( object[ name ], args ) === false )
+						break;
+			} else
+				for ( ; i < length; )
+					if ( callback.apply( object[ i++ ], args ) === false )
+						break;
+
+		// A special, fast, case for the most common use of each
+		} else {
+			if ( length === undefined ) {
+				for ( name in object )
+					if ( callback.call( object[ name ], name, object[ name ] ) === false )
+						break;
+			} else
+				for ( var value = object[0];
+					i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
+		}
+
+		return object;
+	},
+
+	prop: function( elem, value, type, i, name ) {
+		// Handle executable functions
+		if ( jQuery.isFunction( value ) )
+			value = value.call( elem, i );
+
+		// Handle passing in a number to a CSS property
+		return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ?
+			value + "px" :
+			value;
+	},
+
+	className: {
+		// internal only, use addClass("class")
+		add: function( elem, classNames ) {
+			jQuery.each((classNames || "").split(/\s+/), function(i, className){
+				if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
+					elem.className += (elem.className ? " " : "") + className;
+			});
+		},
+
+		// internal only, use removeClass("class")
+		remove: function( elem, classNames ) {
+			if (elem.nodeType == 1)
+				elem.className = classNames !== undefined ?
+					jQuery.grep(elem.className.split(/\s+/), function(className){
+						return !jQuery.className.has( classNames, className );
+					}).join(" ") :
+					"";
+		},
+
+		// internal only, use hasClass("class")
+		has: function( elem, className ) {
+			return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
+		}
+	},
+
+	// A method for quickly swapping in/out CSS properties to get correct calculations
+	swap: function( elem, options, callback ) {
+		var old = {};
+		// Remember the old values, and insert the new ones
+		for ( var name in options ) {
+			old[ name ] = elem.style[ name ];
+			elem.style[ name ] = options[ name ];
+		}
+
+		callback.call( elem );
+
+		// Revert the old values
+		for ( var name in options )
+			elem.style[ name ] = old[ name ];
+	},
+
+	css: function( elem, name, force, extra ) {
+		if ( name == "width" || name == "height" ) {
+			var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
+
+			function getWH() {
+				val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
+
+				if ( extra === "border" )
+					return;
+
+				jQuery.each( which, function() {
+					if ( !extra )
+						val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+					if ( extra === "margin" )
+						val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
+					else
+						val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+				});
+			}
+
+			if ( elem.offsetWidth !== 0 )
+				getWH();
+			else
+				jQuery.swap( elem, props, getWH );
+
+			return Math.max(0, Math.round(val));
+		}
+
+		return jQuery.curCSS( elem, name, force );
+	},
+
+	curCSS: function( elem, name, force ) {
+		var ret, style = elem.style;
+
+		// We need to handle opacity special in IE
+		if ( name == "opacity" && !jQuery.support.opacity ) {
+			ret = jQuery.attr( style, "opacity" );
+
+			return ret == "" ?
+				"1" :
+				ret;
+		}
+
+		// Make sure we're using the right name for getting the float value
+		if ( name.match( /float/i ) )
+			name = styleFloat;
+
+		if ( !force && style && style[ name ] )
+			ret = style[ name ];
+
+		else if ( defaultView.getComputedStyle ) {
+
+			// Only "float" is needed here
+			if ( name.match( /float/i ) )
+				name = "float";
+
+			name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
+
+			var computedStyle = defaultView.getComputedStyle( elem, null );
+
+			if ( computedStyle )
+				ret = computedStyle.getPropertyValue( name );
+
+			// We should always get a number back from opacity
+			if ( name == "opacity" && ret == "" )
+				ret = "1";
+
+		} else if ( elem.currentStyle ) {
+			var camelCase = name.replace(/\-(\w)/g, function(all, letter){
+				return letter.toUpperCase();
+			});
+
+			ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
+
+			// From the awesome hack by Dean Edwards
+			// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+			// If we're not dealing with a regular pixel number
+			// but a number that has a weird ending, we need to convert it to pixels
+			if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
+				// Remember the original values
+				var left = style.left, rsLeft = elem.runtimeStyle.left;
+
+				// Put in the new values to get a computed value out
+				elem.runtimeStyle.left = elem.currentStyle.left;
+				style.left = ret || 0;
+				ret = style.pixelLeft + "px";
+
+				// Revert the changed values
+				style.left = left;
+				elem.runtimeStyle.left = rsLeft;
+			}
+		}
+
+		return ret;
+	},
+
+	clean: function( elems, context, fragment ) {
+		context = context || document;
+
+		// !context.createElement fails in IE with an error but returns typeof 'object'
+		if ( typeof context.createElement === "undefined" )
+			context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+
+		// If a single string is passed in and it's a single tag
+		// just do a createElement and skip the rest
+		if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
+			var match = /^<(\w+)\s*\/?>$/.exec(elems[0]);
+			if ( match )
+				return [ context.createElement( match[1] ) ];
+		}
+
+		var ret = [], scripts = [], div = context.createElement("div");
+
+		jQuery.each(elems, function(i, elem){
+			if ( typeof elem === "number" )
+				elem += '';
+
+			if ( !elem )
+				return;
+
+			// Convert html string into DOM nodes
+			if ( typeof elem === "string" ) {
+				// Fix "XHTML"-style tags in all browsers
+				elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
+					return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
+						all :
+						front + "></" + tag + ">";
+				});
+
+				// Trim whitespace, otherwise indexOf won't work as expected
+				var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();
+
+				var wrap =
+					// option or optgroup
+					!tags.indexOf("<opt") &&
+					[ 1, "<select multiple='multiple'>", "</select>" ] ||
+
+					!tags.indexOf("<leg") &&
+					[ 1, "<fieldset>", "</fieldset>" ] ||
+
+					tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
+					[ 1, "<table>", "</table>" ] ||
+
+					!tags.indexOf("<tr") &&
+					[ 2, "<table><tbody>", "</tbody></table>" ] ||
+
+				 	// <thead> matched above
+					(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
+					[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
+
+					!tags.indexOf("<col") &&
+					[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
+
+					// IE can't serialize <link> and <script> tags normally
+					!jQuery.support.htmlSerialize &&
+					[ 1, "div<div>", "</div>" ] ||
+
+					[ 0, "", "" ];
+
+				// Go to html and back, then peel off extra wrappers
+				div.innerHTML = wrap[1] + elem + wrap[2];
+
+				// Move to the right depth
+				while ( wrap[0]-- )
+					div = div.lastChild;
+
+				// Remove IE's autoinserted <tbody> from table fragments
+				if ( !jQuery.support.tbody ) {
+
+					// String was a <table>, *may* have spurious <tbody>
+					var hasBody = /<tbody/i.test(elem),
+						tbody = !tags.indexOf("<table") && !hasBody ?
+							div.firstChild && div.firstChild.childNodes :
+
+						// String was a bare <thead> or <tfoot>
+						wrap[1] == "<table>" && !hasBody ?
+							div.childNodes :
+							[];
+
+					for ( var j = tbody.length - 1; j >= 0 ; --j )
+						if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
+							tbody[ j ].parentNode.removeChild( tbody[ j ] );
+
+					}
+
+				// IE completely kills leading whitespace when innerHTML is used
+				if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) )
+					div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
+				
+				elem = jQuery.makeArray( div.childNodes );
+			}
+
+			if ( elem.nodeType )
+				ret.push( elem );
+			else
+				ret = jQuery.merge( ret, elem );
+
+		});
+
+		if ( fragment ) {
+			for ( var i = 0; ret[i]; i++ ) {
+				if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+					scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+				} else {
+					if ( ret[i].nodeType === 1 )
+						ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
+					fragment.appendChild( ret[i] );
+				}
+			}
+			
+			return scripts;
+		}
+
+		return ret;
+	},
+
+	attr: function( elem, name, value ) {
+		// don't set attributes on text and comment nodes
+		if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
+			return undefined;
+
+		var notxml = !jQuery.isXMLDoc( elem ),
+			// Whether we are setting (or getting)
+			set = value !== undefined;
+
+		// Try to normalize/fix the name
+		name = notxml && jQuery.props[ name ] || name;
+
+		// Only do all the following if this is a node (faster for style)
+		// IE elem.getAttribute passes even for style
+		if ( elem.tagName ) {
+
+			// These attributes require special treatment
+			var special = /href|src|style/.test( name );
+
+			// Safari mis-reports the default selected property of a hidden option
+			// Accessing the parent's selectedIndex property fixes it
+			if ( name == "selected" && elem.parentNode )
+				elem.parentNode.selectedIndex;
+
+			// If applicable, access the attribute via the DOM 0 way
+			if ( name in elem && notxml && !special ) {
+				if ( set ){
+					// We can't allow the type property to be changed (since it causes problems in IE)
+					if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
+						throw "type property can't be changed";
+
+					elem[ name ] = value;
+				}
+
+				// browsers index elements by id/name on forms, give priority to attributes.
+				if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
+					return elem.getAttributeNode( name ).nodeValue;
+
+				// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+				if ( name == "tabIndex" ) {
+					var attributeNode = elem.getAttributeNode( "tabIndex" );
+					return attributeNode && attributeNode.specified
+						? attributeNode.value
+						: elem.nodeName.match(/(button|input|object|select|textarea)/i)
+							? 0
+							: elem.nodeName.match(/^(a|area)$/i) && elem.href
+								? 0
+								: undefined;
+				}
+
+				return elem[ name ];
+			}
+
+			if ( !jQuery.support.style && notxml &&  name == "style" )
+				return jQuery.attr( elem.style, "cssText", value );
+
+			if ( set )
+				// convert the value to a string (all browsers do this but IE) see #1070
+				elem.setAttribute( name, "" + value );
+
+			var attr = !jQuery.support.hrefNormalized && notxml && special
+					// Some attributes require a special call on IE
+					? elem.getAttribute( name, 2 )
+					: elem.getAttribute( name );
+
+			// Non-existent attributes return null, we normalize to undefined
+			return attr === null ? undefined : attr;
+		}
+
+		// elem is actually elem.style ... set the style
+
+		// IE uses filters for opacity
+		if ( !jQuery.support.opacity && name == "opacity" ) {
+			if ( set ) {
+				// IE has trouble with opacity if it does not have layout
+				// Force it by setting the zoom level
+				elem.zoom = 1;
+
+				// Set the alpha filter to set the opacity
+				elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
+					(parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
+			}
+
+			return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
+				(parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
+				"";
+		}
+
+		name = name.replace(/-([a-z])/ig, function(all, letter){
+			return letter.toUpperCase();
+		});
+
+		if ( set )
+			elem[ name ] = value;
+
+		return elem[ name ];
+	},
+
+	trim: function( text ) {
+		return (text || "").replace( /^\s+|\s+$/g, "" );
+	},
+
+	makeArray: function( array ) {
+		var ret = [];
+
+		if( array != null ){
+			var i = array.length;
+			// The window, strings (and functions) also have 'length'
+			if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
+				ret[0] = array;
+			else
+				while( i )
+					ret[--i] = array[i];
+		}
+
+		return ret;
+	},
+
+	inArray: function( elem, array ) {
+		for ( var i = 0, length = array.length; i < length; i++ )
+		// Use === because on IE, window == document
+			if ( array[ i ] === elem )
+				return i;
+
+		return -1;
+	},
+
+	merge: function( first, second ) {
+		// We have to loop this way because IE & Opera overwrite the length
+		// expando of getElementsByTagName
+		var i = 0, elem, pos = first.length;
+		// Also, we need to make sure that the correct elements are being returned
+		// (IE returns comment nodes in a '*' query)
+		if ( !jQuery.support.getAll ) {
+			while ( (elem = second[ i++ ]) != null )
+				if ( elem.nodeType != 8 )
+					first[ pos++ ] = elem;
+
+		} else
+			while ( (elem = second[ i++ ]) != null )
+				first[ pos++ ] = elem;
+
+		return first;
+	},
+
+	unique: function( array ) {
+		var ret = [], done = {};
+
+		try {
+
+			for ( var i = 0, length = array.length; i < length; i++ ) {
+				var id = jQuery.data( array[ i ] );
+
+				if ( !done[ id ] ) {
+					done[ id ] = true;
+					ret.push( array[ i ] );
+				}
+			}
+
+		} catch( e ) {
+			ret = array;
+		}
+
+		return ret;
+	},
+
+	grep: function( elems, callback, inv ) {
+		var ret = [];
+
+		// Go through the array, only saving the items
+		// that pass the validator function
+		for ( var i = 0, length = elems.length; i < length; i++ )
+			if ( !inv != !callback( elems[ i ], i ) )
+				ret.push( elems[ i ] );
+
+		return ret;
+	},
+
+	map: function( elems, callback ) {
+		var ret = [];
+
+		// Go through the array, translating each of the items to their
+		// new value (or values).
+		for ( var i = 0, length = elems.length; i < length; i++ ) {
+			var value = callback( elems[ i ], i );
+
+			if ( value != null )
+				ret[ ret.length ] = value;
+		}
+
+		return ret.concat.apply( [], ret );
+	}
+});
+
+// Use of jQuery.browser is deprecated.
+// It's included for backwards compatibility and plugins,
+// although they should work to migrate away.
+
+var userAgent = navigator.userAgent.toLowerCase();
+
+// Figure out what browser is being used
+jQuery.browser = {
+	version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
+	safari: /webkit/.test( userAgent ),
+	opera: /opera/.test( userAgent ),
+	msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
+	mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
+};
+
+jQuery.each({
+	parent: function(elem){return elem.parentNode;},
+	parents: function(elem){return jQuery.dir(elem,"parentNode");},
+	next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
+	prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
+	nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
+	prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
+	siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
+	children: function(elem){return jQuery.sibling(elem.firstChild);},
+	contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
+}, function(name, fn){
+	jQuery.fn[ name ] = function( selector ) {
+		var ret = jQuery.map( this, fn );
+
+		if ( selector && typeof selector == "string" )
+			ret = jQuery.multiFilter( selector, ret );
+
+		return this.pushStack( jQuery.unique( ret ), name, selector );
+	};
+});
+
+jQuery.each({
+	appendTo: "append",
+	prependTo: "prepend",
+	insertBefore: "before",
+	insertAfter: "after",
+	replaceAll: "replaceWith"
+}, function(name, original){
+	jQuery.fn[ name ] = function( selector ) {
+		var ret = [], insert = jQuery( selector );
+
+		for ( var i = 0, l = insert.length; i < l; i++ ) {
+			var elems = (i > 0 ? this.clone(true) : this).get();
+			jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+			ret = ret.concat( elems );
+		}
+
+		return this.pushStack( ret, name, selector );
+	};
+});
+
+jQuery.each({
+	removeAttr: function( name ) {
+		jQuery.attr( this, name, "" );
+		if (this.nodeType == 1)
+			this.removeAttribute( name );
+	},
+
+	addClass: function( classNames ) {
+		jQuery.className.add( this, classNames );
+	},
+
+	removeClass: function( classNames ) {
+		jQuery.className.remove( this, classNames );
+	},
+
+	toggleClass: function( classNames, state ) {
+		if( typeof state !== "boolean" )
+			state = !jQuery.className.has( this, classNames );
+		jQuery.className[ state ? "add" : "remove" ]( this, classNames );
+	},
+
+	remove: function( selector ) {
+		if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
+			// Prevent memory leaks
+			jQuery( "*", this ).add([this]).each(function(){
+				jQuery.event.remove(this);
+				jQuery.removeData(this);
+			});
+			if (this.parentNode)
+				this.parentNode.removeChild( this );
+		}
+	},
+
+	empty: function() {
+		// Remove element nodes and prevent memory leaks
+		jQuery(this).children().remove();
+
+		// Remove any remaining nodes
+		while ( this.firstChild )
+			this.removeChild( this.firstChild );
+	}
+}, function(name, fn){
+	jQuery.fn[ name ] = function(){
+		return this.each( fn, arguments );
+	};
+});
+
+// Helper function used by the dimensions and offset modules
+function num(elem, prop) {
+	return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
+}
+var expando = "jQuery" + now(), uuid = 0, windowData = {};
+
+jQuery.extend({
+	cache: {},
+
+	data: function( elem, name, data ) {
+		elem = elem == window ?
+			windowData :
+			elem;
+
+		var id = elem[ expando ];
+
+		// Compute a unique ID for the element
+		if ( !id )
+			id = elem[ expando ] = ++uuid;
+
+		// Only generate the data cache if we're
+		// trying to access or manipulate it
+		if ( name && !jQuery.cache[ id ] )
+			jQuery.cache[ id ] = {};
+
+		// Prevent overriding the named cache with undefined values
+		if ( data !== undefined )
+			jQuery.cache[ id ][ name ] = data;
+
+		// Return the named cache data, or the ID for the element
+		return name ?
+			jQuery.cache[ id ][ name ] :
+			id;
+	},
+
+	removeData: function( elem, name ) {
+		elem = elem == window ?
+			windowData :
+			elem;
+
+		var id = elem[ expando ];
+
+		// If we want to remove a specific section of the element's data
+		if ( name ) {
+			if ( jQuery.cache[ id ] ) {
+				// Remove the section of cache data
+				delete jQuery.cache[ id ][ name ];
+
+				// If we've removed all the data, remove the element's cache
+				name = "";
+
+				for ( name in jQuery.cache[ id ] )
+					break;
+
+				if ( !name )
+					jQuery.removeData( elem );
+			}
+
+		// Otherwise, we want to remove all of the element's data
+		} else {
+			// Clean up the element expando
+			try {
+				delete elem[ expando ];
+			} catch(e){
+				// IE has trouble directly removing the expando
+				// but it's ok with using removeAttribute
+				if ( elem.removeAttribute )
+					elem.removeAttribute( expando );
+			}
+
+			// Completely remove the data cache
+			delete jQuery.cache[ id ];
+		}
+	},
+	queue: function( elem, type, data ) {
+		if ( elem ){
+	
+			type = (type || "fx") + "queue";
+	
+			var q = jQuery.data( elem, type );
+	
+			if ( !q || jQuery.isArray(data) )
+				q = jQuery.data( elem, type, jQuery.makeArray(data) );
+			else if( data )
+				q.push( data );
+	
+		}
+		return q;
+	},
+
+	dequeue: function( elem, type ){
+		var queue = jQuery.queue( elem, type ),
+			fn = queue.shift();
+		
+		if( !type || type === "fx" )
+			fn = queue[0];
+			
+		if( fn !== undefined )
+			fn.call(elem);
+	}
+});
+
+jQuery.fn.extend({
+	data: function( key, value ){
+		var parts = key.split(".");
+		parts[1] = parts[1] ? "." + parts[1] : "";
+
+		if ( value === undefined ) {
+			var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+			if ( data === undefined && this.length )
+				data = jQuery.data( this[0], key );
+
+			return data === undefined && parts[1] ?
+				this.data( parts[0] ) :
+				data;
+		} else
+			return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
+				jQuery.data( this, key, value );
+			});
+	},
+
+	removeData: function( key ){
+		return this.each(function(){
+			jQuery.removeData( this, key );
+		});
+	},
+	queue: function(type, data){
+		if ( typeof type !== "string" ) {
+			data = type;
+			type = "fx";
+		}
+
+		if ( data === undefined )
+			return jQuery.queue( this[0], type );
+
+		return this.each(function(){
+			var queue = jQuery.queue( this, type, data );
+			
+			 if( type == "fx" && queue.length == 1 )
+				queue[0].call(this);
+		});
+	},
+	dequeue: function(type){
+		return this.each(function(){
+			jQuery.dequeue( this, type );
+		});
+	}
+});/*!
+ * Sizzle CSS Selector Engine - v0.9.3
+ *  Copyright 2009, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
+	done = 0,
+	toString = Object.prototype.toString;
+
+var Sizzle = function(selector, context, results, seed) {
+	results = results || [];
+	context = context || document;
+
+	if ( context.nodeType !== 1 && context.nodeType !== 9 )
+		return [];
+	
+	if ( !selector || typeof selector !== "string" ) {
+		return results;
+	}
+
+	var parts = [], m, set, checkSet, check, mode, extra, prune = true;
+	
+	// Reset the position of the chunker regexp (start from head)
+	chunker.lastIndex = 0;
+	
+	while ( (m = chunker.exec(selector)) !== null ) {
+		parts.push( m[1] );
+		
+		if ( m[2] ) {
+			extra = RegExp.rightContext;
+			break;
+		}
+	}
+
+	if ( parts.length > 1 && origPOS.exec( selector ) ) {
+		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+			set = posProcess( parts[0] + parts[1], context );
+		} else {
+			set = Expr.relative[ parts[0] ] ?
+				[ context ] :
+				Sizzle( parts.shift(), context );
+
+			while ( parts.length ) {
+				selector = parts.shift();
+
+				if ( Expr.relative[ selector ] )
+					selector += parts.shift();
+
+				set = posProcess( selector, set );
+			}
+		}
+	} else {
+		var ret = seed ?
+			{ expr: parts.pop(), set: makeArray(seed) } :
+			Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
+		set = Sizzle.filter( ret.expr, ret.set );
+
+		if ( parts.length > 0 ) {
+			checkSet = makeArray(set);
+		} else {
+			prune = false;
+		}
+
+		while ( parts.length ) {
+			var cur = parts.pop(), pop = cur;
+
+			if ( !Expr.relative[ cur ] ) {
+				cur = "";
+			} else {
+				pop = parts.pop();
+			}
+
+			if ( pop == null ) {
+				pop = context;
+			}
+
+			Expr.relative[ cur ]( checkSet, pop, isXML(context) );
+		}
+	}
+
+	if ( !checkSet ) {
+		checkSet = set;
+	}
+
+	if ( !checkSet ) {
+		throw "Syntax error, unrecognized expression: " + (cur || selector);
+	}
+
+	if ( toString.call(checkSet) === "[object Array]" ) {
+		if ( !prune ) {
+			results.push.apply( results, checkSet );
+		} else if ( context.nodeType === 1 ) {
+			for ( var i = 0; checkSet[i] != null; i++ ) {
+				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
+					results.push( set[i] );
+				}
+			}
+		} else {
+			for ( var i = 0; checkSet[i] != null; i++ ) {
+				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+					results.push( set[i] );
+				}
+			}
+		}
+	} else {
+		makeArray( checkSet, results );
+	}
+
+	if ( extra ) {
+		Sizzle( extra, context, results, seed );
+
+		if ( sortOrder ) {
+			hasDuplicate = false;
+			results.sort(sortOrder);
+
+			if ( hasDuplicate ) {
+				for ( var i = 1; i < results.length; i++ ) {
+					if ( results[i] === results[i-1] ) {
+						results.splice(i--, 1);
+					}
+				}
+			}
+		}
+	}
+
+	return results;
+};
+
+Sizzle.matches = function(expr, set){
+	return Sizzle(expr, null, null, set);
+};
+
+Sizzle.find = function(expr, context, isXML){
+	var set, match;
+
+	if ( !expr ) {
+		return [];
+	}
+
+	for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+		var type = Expr.order[i], match;
+		
+		if ( (match = Expr.match[ type ].exec( expr )) ) {
+			var left = RegExp.leftContext;
+
+			if ( left.substr( left.length - 1 ) !== "\\" ) {
+				match[1] = (match[1] || "").replace(/\\/g, "");
+				set = Expr.find[ type ]( match, context, isXML );
+				if ( set != null ) {
+					expr = expr.replace( Expr.match[ type ], "" );
+					break;
+				}
+			}
+		}
+	}
+
+	if ( !set ) {
+		set = context.getElementsByTagName("*");
+	}
+
+	return {set: set, expr: expr};
+};
+
+Sizzle.filter = function(expr, set, inplace, not){
+	var old = expr, result = [], curLoop = set, match, anyFound,
+		isXMLFilter = set && set[0] && isXML(set[0]);
+
+	while ( expr && set.length ) {
+		for ( var type in Expr.filter ) {
+			if ( (match = Expr.match[ type ].exec( expr )) != null ) {
+				var filter = Expr.filter[ type ], found, item;
+				anyFound = false;
+
+				if ( curLoop == result ) {
+					result = [];
+				}
+
+				if ( Expr.preFilter[ type ] ) {
+					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+					if ( !match ) {
+						anyFound = found = true;
+					} else if ( match === true ) {
+						continue;
+					}
+				}
+
+				if ( match ) {
+					for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+						if ( item ) {
+							found = filter( item, match, i, curLoop );
+							var pass = not ^ !!found;
+
+							if ( inplace && found != null ) {
+								if ( pass ) {
+									anyFound = true;
+								} else {
+									curLoop[i] = false;
+								}
+							} else if ( pass ) {
+								result.push( item );
+								anyFound = true;
+							}
+						}
+					}
+				}
+
+				if ( found !== undefined ) {
+					if ( !inplace ) {
+						curLoop = result;
+					}
+
+					expr = expr.replace( Expr.match[ type ], "" );
+
+					if ( !anyFound ) {
+						return [];
+					}
+
+					break;
+				}
+			}
+		}
+
+		// Improper expression
+		if ( expr == old ) {
+			if ( anyFound == null ) {
+				throw "Syntax error, unrecognized expression: " + expr;
+			} else {
+				break;
+			}
+		}
+
+		old = expr;
+	}
+
+	return curLoop;
+};
+
+var Expr = Sizzle.selectors = {
+	order: [ "ID", "NAME", "TAG" ],
+	match: {
+		ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
+		CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
+		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
+		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+		TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
+		CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
+		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
+		PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
+	},
+	attrMap: {
+		"class": "className",
+		"for": "htmlFor"
+	},
+	attrHandle: {
+		href: function(elem){
+			return elem.getAttribute("href");
+		}
+	},
+	relative: {
+		"+": function(checkSet, part, isXML){
+			var isPartStr = typeof part === "string",
+				isTag = isPartStr && !/\W/.test(part),
+				isPartStrNotTag = isPartStr && !isTag;
+
+			if ( isTag && !isXML ) {
+				part = part.toUpperCase();
+			}
+
+			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+				if ( (elem = checkSet[i]) ) {
+					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+					checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
+						elem || false :
+						elem === part;
+				}
+			}
+
+			if ( isPartStrNotTag ) {
+				Sizzle.filter( part, checkSet, true );
+			}
+		},
+		">": function(checkSet, part, isXML){
+			var isPartStr = typeof part === "string";
+
+			if ( isPartStr && !/\W/.test(part) ) {
+				part = isXML ? part : part.toUpperCase();
+
+				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+					var elem = checkSet[i];
+					if ( elem ) {
+						var parent = elem.parentNode;
+						checkSet[i] = parent.nodeName === part ? parent : false;
+					}
+				}
+			} else {
+				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+					var elem = checkSet[i];
+					if ( elem ) {
+						checkSet[i] = isPartStr ?
+							elem.parentNode :
+							elem.parentNode === part;
+					}
+				}
+
+				if ( isPartStr ) {
+					Sizzle.filter( part, checkSet, true );
+				}
+			}
+		},
+		"": function(checkSet, part, isXML){
+			var doneName = done++, checkFn = dirCheck;
+
+			if ( !part.match(/\W/) ) {
+				var nodeCheck = part = isXML ? part : part.toUpperCase();
+				checkFn = dirNodeCheck;
+			}
+
+			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
+		},
+		"~": function(checkSet, part, isXML){
+			var doneName = done++, checkFn = dirCheck;
+
+			if ( typeof part === "string" && !part.match(/\W/) ) {
+				var nodeCheck = part = isXML ? part : part.toUpperCase();
+				checkFn = dirNodeCheck;
+			}
+
+			checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
+		}
+	},
+	find: {
+		ID: function(match, context, isXML){
+			if ( typeof context.getElementById !== "undefined" && !isXML ) {
+				var m = context.getElementById(match[1]);
+				return m ? [m] : [];
+			}
+		},
+		NAME: function(match, context, isXML){
+			if ( typeof context.getElementsByName !== "undefined" ) {
+				var ret = [], results = context.getElementsByName(match[1]);
+
+				for ( var i = 0, l = results.length; i < l; i++ ) {
+					if ( results[i].getAttribute("name") === match[1] ) {
+						ret.push( results[i] );
+					}
+				}
+
+				return ret.length === 0 ? null : ret;
+			}
+		},
+		TAG: function(match, context){
+			return context.getElementsByTagName(match[1]);
+		}
+	},
+	preFilter: {
+		CLASS: function(match, curLoop, inplace, result, not, isXML){
+			match = " " + match[1].replace(/\\/g, "") + " ";
+
+			if ( isXML ) {
+				return match;
+			}
+
+			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+				if ( elem ) {
+					if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
+						if ( !inplace )
+							result.push( elem );
+					} else if ( inplace ) {
+						curLoop[i] = false;
+					}
+				}
+			}
+
+			return false;
+		},
+		ID: function(match){
+			return match[1].replace(/\\/g, "");
+		},
+		TAG: function(match, curLoop){
+			for ( var i = 0; curLoop[i] === false; i++ ){}
+			return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
+		},
+		CHILD: function(match){
+			if ( match[1] == "nth" ) {
+				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+				var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+					match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
+					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+				// calculate the numbers (first)n+(last) including if they are negative
+				match[2] = (test[1] + (test[2] || 1)) - 0;
+				match[3] = test[3] - 0;
+			}
+
+			// TODO: Move to normal caching system
+			match[0] = done++;
+
+			return match;
+		},
+		ATTR: function(match, curLoop, inplace, result, not, isXML){
+			var name = match[1].replace(/\\/g, "");
+			
+			if ( !isXML && Expr.attrMap[name] ) {
+				match[1] = Expr.attrMap[name];
+			}
+
+			if ( match[2] === "~=" ) {
+				match[4] = " " + match[4] + " ";
+			}
+
+			return match;
+		},
+		PSEUDO: function(match, curLoop, inplace, result, not){
+			if ( match[1] === "not" ) {
+				// If we're dealing with a complex expression, or a simple one
+				if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
+					match[3] = Sizzle(match[3], null, null, curLoop);
+				} else {
+					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+					if ( !inplace ) {
+						result.push.apply( result, ret );
+					}
+					return false;
+				}
+			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+				return true;
+			}
+			
+			return match;
+		},
+		POS: function(match){
+			match.unshift( true );
+			return match;
+		}
+	},
+	filters: {
+		enabled: function(elem){
+			return elem.disabled === false && elem.type !== "hidden";
+		},
+		disabled: function(elem){
+			return elem.disabled === true;
+		},
+		checked: function(elem){
+			return elem.checked === true;
+		},
+		selected: function(elem){
+			// Accessing this property makes selected-by-default
+			// options in Safari work properly
+			elem.parentNode.selectedIndex;
+			return elem.selected === true;
+		},
+		parent: function(elem){
+			return !!elem.firstChild;
+		},
+		empty: function(elem){
+			return !elem.firstChild;
+		},
+		has: function(elem, i, match){
+			return !!Sizzle( match[3], elem ).length;
+		},
+		header: function(elem){
+			return /h\d/i.test( elem.nodeName );
+		},
+		text: function(elem){
+			return "text" === elem.type;
+		},
+		radio: function(elem){
+			return "radio" === elem.type;
+		},
+		checkbox: function(elem){
+			return "checkbox" === elem.type;
+		},
+		file: function(elem){
+			return "file" === elem.type;
+		},
+		password: function(elem){
+			return "password" === elem.type;
+		},
+		submit: function(elem){
+			return "submit" === elem.type;
+		},
+		image: function(elem){
+			return "image" === elem.type;
+		},
+		reset: function(elem){
+			return "reset" === elem.type;
+		},
+		button: function(elem){
+			return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
+		},
+		input: function(elem){
+			return /input|select|textarea|button/i.test(elem.nodeName);
+		}
+	},
+	setFilters: {
+		first: function(elem, i){
+			return i === 0;
+		},
+		last: function(elem, i, match, array){
+			return i === array.length - 1;
+		},
+		even: function(elem, i){
+			return i % 2 === 0;
+		},
+		odd: function(elem, i){
+			return i % 2 === 1;
+		},
+		lt: function(elem, i, match){
+			return i < match[3] - 0;
+		},
+		gt: function(elem, i, match){
+			return i > match[3] - 0;
+		},
+		nth: function(elem, i, match){
+			return match[3] - 0 == i;
+		},
+		eq: function(elem, i, match){
+			return match[3] - 0 == i;
+		}
+	},
+	filter: {
+		PSEUDO: function(elem, match, i, array){
+			var name = match[1], filter = Expr.filters[ name ];
+
+			if ( filter ) {
+				return filter( elem, i, match, array );
+			} else if ( name === "contains" ) {
+				return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
+			} else if ( name === "not" ) {
+				var not = match[3];
+
+				for ( var i = 0, l = not.length; i < l; i++ ) {
+					if ( not[i] === elem ) {
+						return false;
+					}
+				}
+
+				return true;
+			}
+		},
+		CHILD: function(elem, match){
+			var type = match[1], node = elem;
+			switch (type) {
+				case 'only':
+				case 'first':
+					while (node = node.previousSibling)  {
+						if ( node.nodeType === 1 ) return false;
+					}
+					if ( type == 'first') return true;
+					node = elem;
+				case 'last':
+					while (node = node.nextSibling)  {
+						if ( node.nodeType === 1 ) return false;
+					}
+					return true;
+				case 'nth':
+					var first = match[2], last = match[3];
+
+					if ( first == 1 && last == 0 ) {
+						return true;
+					}
+					
+					var doneName = match[0],
+						parent = elem.parentNode;
+	
+					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+						var count = 0;
+						for ( node = parent.firstChild; node; node = node.nextSibling ) {
+							if ( node.nodeType === 1 ) {
+								node.nodeIndex = ++count;
+							}
+						} 
+						parent.sizcache = doneName;
+					}
+					
+					var diff = elem.nodeIndex - last;
+					if ( first == 0 ) {
+						return diff == 0;
+					} else {
+						return ( diff % first == 0 && diff / first >= 0 );
+					}
+			}
+		},
+		ID: function(elem, match){
+			return elem.nodeType === 1 && elem.getAttribute("id") === match;
+		},
+		TAG: function(elem, match){
+			return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
+		},
+		CLASS: function(elem, match){
+			return (" " + (elem.className || elem.getAttribute("class")) + " ")
+				.indexOf( match ) > -1;
+		},
+		ATTR: function(elem, match){
+			var name = match[1],
+				result = Expr.attrHandle[ name ] ?
+					Expr.attrHandle[ name ]( elem ) :
+					elem[ name ] != null ?
+						elem[ name ] :
+						elem.getAttribute( name ),
+				value = result + "",
+				type = match[2],
+				check = match[4];
+
+			return result == null ?
+				type === "!=" :
+				type === "=" ?
+				value === check :
+				type === "*=" ?
+				value.indexOf(check) >= 0 :
+				type === "~=" ?
+				(" " + value + " ").indexOf(check) >= 0 :
+				!check ?
+				value && result !== false :
+				type === "!=" ?
+				value != check :
+				type === "^=" ?
+				value.indexOf(check) === 0 :
+				type === "$=" ?
+				value.substr(value.length - check.length) === check :
+				type === "|=" ?
+				value === check || value.substr(0, check.length + 1) === check + "-" :
+				false;
+		},
+		POS: function(elem, match, i, array){
+			var name = match[2], filter = Expr.setFilters[ name ];
+
+			if ( filter ) {
+				return filter( elem, i, match, array );
+			}
+		}
+	}
+};
+
+var origPOS = Expr.match.POS;
+
+for ( var type in Expr.match ) {
+	Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
+}
+
+var makeArray = function(array, results) {
+	array = Array.prototype.slice.call( array );
+
+	if ( results ) {
+		results.push.apply( results, array );
+		return results;
+	}
+	
+	return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+try {
+	Array.prototype.slice.call( document.documentElement.childNodes );
+
+// Provide a fallback method if it does not work
+} catch(e){
+	makeArray = function(array, results) {
+		var ret = results || [];
+
+		if ( toString.call(array) === "[object Array]" ) {
+			Array.prototype.push.apply( ret, array );
+		} else {
+			if ( typeof array.length === "number" ) {
+				for ( var i = 0, l = array.length; i < l; i++ ) {
+					ret.push( array[i] );
+				}
+			} else {
+				for ( var i = 0; array[i]; i++ ) {
+					ret.push( array[i] );
+				}
+			}
+		}
+
+		return ret;
+	};
+}
+
+var sortOrder;
+
+if ( document.documentElement.compareDocumentPosition ) {
+	sortOrder = function( a, b ) {
+		var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+		if ( ret === 0 ) {
+			hasDuplicate = true;
+		}
+		return ret;
+	};
+} else if ( "sourceIndex" in document.documentElement ) {
+	sortOrder = function( a, b ) {
+		var ret = a.sourceIndex - b.sourceIndex;
+		if ( ret === 0 ) {
+			hasDuplicate = true;
+		}
+		return ret;
+	};
+} else if ( document.createRange ) {
+	sortOrder = function( a, b ) {
+		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+		aRange.selectNode(a);
+		aRange.collapse(true);
+		bRange.selectNode(b);
+		bRange.collapse(true);
+		var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+		if ( ret === 0 ) {
+			hasDuplicate = true;
+		}
+		return ret;
+	};
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+	// We're going to inject a fake input element with a specified name
+	var form = document.createElement("form"),
+		id = "script" + (new Date).getTime();
+	form.innerHTML = "<input name='" + id + "'/>";
+
+	// Inject it into the root element, check its status, and remove it quickly
+	var root = document.documentElement;
+	root.insertBefore( form, root.firstChild );
+
+	// The workaround has to do additional checks after a getElementById
+	// Which slows things down for other browsers (hence the branching)
+	if ( !!document.getElementById( id ) ) {
+		Expr.find.ID = function(match, context, isXML){
+			if ( typeof context.getElementById !== "undefined" && !isXML ) {
+				var m = context.getElementById(match[1]);
+				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+			}
+		};
+
+		Expr.filter.ID = function(elem, match){
+			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+			return elem.nodeType === 1 && node && node.nodeValue === match;
+		};
+	}
+
+	root.removeChild( form );
+})();
+
+(function(){
+	// Check to see if the browser returns only elements
+	// when doing getElementsByTagName("*")
+
+	// Create a fake element
+	var div = document.createElement("div");
+	div.appendChild( document.createComment("") );
+
+	// Make sure no comments are found
+	if ( div.getElementsByTagName("*").length > 0 ) {
+		Expr.find.TAG = function(match, context){
+			var results = context.getElementsByTagName(match[1]);
+
+			// Filter out possible comments
+			if ( match[1] === "*" ) {
+				var tmp = [];
+
+				for ( var i = 0; results[i]; i++ ) {
+					if ( results[i].nodeType === 1 ) {
+						tmp.push( results[i] );
+					}
+				}
+
+				results = tmp;
+			}
+
+			return results;
+		};
+	}
+
+	// Check to see if an attribute returns normalized href attributes
+	div.innerHTML = "<a href='#'></a>";
+	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+			div.firstChild.getAttribute("href") !== "#" ) {
+		Expr.attrHandle.href = function(elem){
+			return elem.getAttribute("href", 2);
+		};
+	}
+})();
+
+if ( document.querySelectorAll ) (function(){
+	var oldSizzle = Sizzle, div = document.createElement("div");
+	div.innerHTML = "<p class='TEST'></p>";
+
+	// Safari can't handle uppercase or unicode characters when
+	// in quirks mode.
+	if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+		return;
+	}
+	
+	Sizzle = function(query, context, extra, seed){
+		context = context || document;
+
+		// Only use querySelectorAll on non-XML documents
+		// (ID selectors don't work in non-HTML documents)
+		if ( !seed && context.nodeType === 9 && !isXML(context) ) {
+			try {
+				return makeArray( context.querySelectorAll(query), extra );
+			} catch(e){}
+		}
+		
+		return oldSizzle(query, context, extra, seed);
+	};
+
+	Sizzle.find = oldSizzle.find;
+	Sizzle.filter = oldSizzle.filter;
+	Sizzle.selectors = oldSizzle.selectors;
+	Sizzle.matches = oldSizzle.matches;
+})();
+
+if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
+	var div = document.createElement("div");
+	div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+	// Opera can't find a second classname (in 9.6)
+	if ( div.getElementsByClassName("e").length === 0 )
+		return;
+
+	// Safari caches class attributes, doesn't catch changes (in 3.2)
+	div.lastChild.className = "e";
+
+	if ( div.getElementsByClassName("e").length === 1 )
+		return;
+
+	Expr.order.splice(1, 0, "CLASS");
+	Expr.find.CLASS = function(match, context, isXML) {
+		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+			return context.getElementsByClassName(match[1]);
+		}
+	};
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+	var sibDir = dir == "previousSibling" && !isXML;
+	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+		var elem = checkSet[i];
+		if ( elem ) {
+			if ( sibDir && elem.nodeType === 1 ){
+				elem.sizcache = doneName;
+				elem.sizset = i;
+			}
+			elem = elem[dir];
+			var match = false;
+
+			while ( elem ) {
+				if ( elem.sizcache === doneName ) {
+					match = checkSet[elem.sizset];
+					break;
+				}
+
+				if ( elem.nodeType === 1 && !isXML ){
+					elem.sizcache = doneName;
+					elem.sizset = i;
+				}
+
+				if ( elem.nodeName === cur ) {
+					match = elem;
+					break;
+				}
+
+				elem = elem[dir];
+			}
+
+			checkSet[i] = match;
+		}
+	}
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+	var sibDir = dir == "previousSibling" && !isXML;
+	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+		var elem = checkSet[i];
+		if ( elem ) {
+			if ( sibDir && elem.nodeType === 1 ) {
+				elem.sizcache = doneName;
+				elem.sizset = i;
+			}
+			elem = elem[dir];
+			var match = false;
+
+			while ( elem ) {
+				if ( elem.sizcache === doneName ) {
+					match = checkSet[elem.sizset];
+					break;
+				}
+
+				if ( elem.nodeType === 1 ) {
+					if ( !isXML ) {
+						elem.sizcache = doneName;
+						elem.sizset = i;
+					}
+					if ( typeof cur !== "string" ) {
+						if ( elem === cur ) {
+							match = true;
+							break;
+						}
+
+					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+						match = elem;
+						break;
+					}
+				}
+
+				elem = elem[dir];
+			}
+
+			checkSet[i] = match;
+		}
+	}
+}
+
+var contains = document.compareDocumentPosition ?  function(a, b){
+	return a.compareDocumentPosition(b) & 16;
+} : function(a, b){
+	return a !== b && (a.contains ? a.contains(b) : true);
+};
+
+var isXML = function(elem){
+	return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+		!!elem.ownerDocument && isXML( elem.ownerDocument );
+};
+
+var posProcess = function(selector, context){
+	var tmpSet = [], later = "", match,
+		root = context.nodeType ? [context] : context;
+
+	// Position selectors must be done after the filter
+	// And so must :not(positional) so we move all PSEUDOs to the end
+	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+		later += match[0];
+		selector = selector.replace( Expr.match.PSEUDO, "" );
+	}
+
+	selector = Expr.relative[selector] ? selector + "*" : selector;
+
+	for ( var i = 0, l = root.length; i < l; i++ ) {
+		Sizzle( selector, root[i], tmpSet );
+	}
+
+	return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.filter = Sizzle.filter;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+
+Sizzle.selectors.filters.hidden = function(elem){
+	return elem.offsetWidth === 0 || elem.offsetHeight === 0;
+};
+
+Sizzle.selectors.filters.visible = function(elem){
+	return elem.offsetWidth > 0 || elem.offsetHeight > 0;
+};
+
+Sizzle.selectors.filters.animated = function(elem){
+	return jQuery.grep(jQuery.timers, function(fn){
+		return elem === fn.elem;
+	}).length;
+};
+
+jQuery.multiFilter = function( expr, elems, not ) {
+	if ( not ) {
+		expr = ":not(" + expr + ")";
+	}
+
+	return Sizzle.matches(expr, elems);
+};
+
+jQuery.dir = function( elem, dir ){
+	var matched = [], cur = elem[dir];
+	while ( cur && cur != document ) {
+		if ( cur.nodeType == 1 )
+			matched.push( cur );
+		cur = cur[dir];
+	}
+	return matched;
+};
+
+jQuery.nth = function(cur, result, dir, elem){
+	result = result || 1;
+	var num = 0;
+
+	for ( ; cur; cur = cur[dir] )
+		if ( cur.nodeType == 1 && ++num == result )
+			break;
+
+	return cur;
+};
+
+jQuery.sibling = function(n, elem){
+	var r = [];
+
+	for ( ; n; n = n.nextSibling ) {
+		if ( n.nodeType == 1 && n != elem )
+			r.push( n );
+	}
+
+	return r;
+};
+
+return;
+
+window.Sizzle = Sizzle;
+
+})();
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+	// Bind an event to an element
+	// Original by Dean Edwards
+	add: function(elem, types, handler, data) {
+		if ( elem.nodeType == 3 || elem.nodeType == 8 )
+			return;
+
+		// For whatever reason, IE has trouble passing the window object
+		// around, causing it to be cloned in the process
+		if ( elem.setInterval && elem != window )
+			elem = window;
+
+		// Make sure that the function being executed has a unique ID
+		if ( !handler.guid )
+			handler.guid = this.guid++;
+
+		// if data is passed, bind to handler
+		if ( data !== undefined ) {
+			// Create temporary function pointer to original handler
+			var fn = handler;
+
+			// Create unique handler function, wrapped around original handler
+			handler = this.proxy( fn );
+
+			// Store data in unique handler
+			handler.data = data;
+		}
+
+		// Init the element's event structure
+		var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
+			handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
+				// Handle the second event of a trigger and when
+				// an event is called after a page has unloaded
+				return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
+					jQuery.event.handle.apply(arguments.callee.elem, arguments) :
+					undefined;
+			});
+		// Add elem as a property of the handle function
+		// This is to prevent a memory leak with non-native
+		// event in IE.
+		handle.elem = elem;
+
+		// Handle multiple events separated by a space
+		// jQuery(...).bind("mouseover mouseout", fn);
+		jQuery.each(types.split(/\s+/), function(index, type) {
+			// Namespaced event handlers
+			var namespaces = type.split(".");
+			type = namespaces.shift();
+			handler.type = namespaces.slice().sort().join(".");
+
+			// Get the current list of functions bound to this event
+			var handlers = events[type];
+			
+			if ( jQuery.event.specialAll[type] )
+				jQuery.event.specialAll[type].setup.call(elem, data, namespaces);
+
+			// Init the event handler queue
+			if (!handlers) {
+				handlers = events[type] = {};
+
+				// Check for a special event handler
+				// Only use addEventListener/attachEvent if the special
+				// events handler returns false
+				if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
+					// Bind the global event handler to the element
+					if (elem.addEventListener)
+						elem.addEventListener(type, handle, false);
+					else if (elem.attachEvent)
+						elem.attachEvent("on" + type, handle);
+				}
+			}
+
+			// Add the function to the element's handler list
+			handlers[handler.guid] = handler;
+
+			// Keep track of which events have been used, for global triggering
+			jQuery.event.global[type] = true;
+		});
+
+		// Nullify elem to prevent memory leaks in IE
+		elem = null;
+	},
+
+	guid: 1,
+	global: {},
+
+	// Detach an event or set of events from an element
+	remove: function(elem, types, handler) {
+		// don't do events on text and comment nodes
+		if ( elem.nodeType == 3 || elem.nodeType == 8 )
+			return;
+
+		var events = jQuery.data(elem, "events"), ret, index;
+
+		if ( events ) {
+			// Unbind all events for the element
+			if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") )
+				for ( var type in events )
+					this.remove( elem, type + (types || "") );
+			else {
+				// types is actually an event object here
+				if ( types.type ) {
+					handler = types.handler;
+					types = types.type;
+				}
+
+				// Handle multiple events seperated by a space
+				// jQuery(...).unbind("mouseover mouseout", fn);
+				jQuery.each(types.split(/\s+/), function(index, type){
+					// Namespaced event handlers
+					var namespaces = type.split(".");
+					type = namespaces.shift();
+					var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
+
+					if ( events[type] ) {
+						// remove the given handler for the given type
+						if ( handler )
+							delete events[type][handler.guid];
+
+						// remove all handlers for the given type
+						else
+							for ( var handle in events[type] )
+								// Handle the removal of namespaced events
+								if ( namespace.test(events[type][handle].type) )
+									delete events[type][handle];
+									
+						if ( jQuery.event.specialAll[type] )
+							jQuery.event.specialAll[type].teardown.call(elem, namespaces);
+
+						// remove generic event handler if no more handlers exist
+						for ( ret in events[type] ) break;
+						if ( !ret ) {
+							if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) {
+								if (elem.removeEventListener)
+									elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
+								else if (elem.detachEvent)
+									elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
+							}
+							ret = null;
+							delete events[type];
+						}
+					}
+				});
+			}
+
+			// Remove the expando if it's no longer used
+			for ( ret in events ) break;
+			if ( !ret ) {
+				var handle = jQuery.data( elem, "handle" );
+				if ( handle ) handle.elem = null;
+				jQuery.removeData( elem, "events" );
+				jQuery.removeData( elem, "handle" );
+			}
+		}
+	},
+
+	// bubbling is internal
+	trigger: function( event, data, elem, bubbling ) {
+		// Event object or event type
+		var type = event.type || event;
+
+		if( !bubbling ){
+			event = typeof event === "object" ?
+				// jQuery.Event object
+				event[expando] ? event :
+				// Object literal
+				jQuery.extend( jQuery.Event(type), event ) :
+				// Just the event type (string)
+				jQuery.Event(type);
+
+			if ( type.indexOf("!") >= 0 ) {
+				event.type = type = type.slice(0, -1);
+				event.exclusive = true;
+			}
+
+			// Handle a global trigger
+			if ( !elem ) {
+				// Don't bubble custom events when global (to avoid too much overhead)
+				event.stopPropagation();
+				// Only trigger if we've ever bound an event for it
+				if ( this.global[type] )
+					jQuery.each( jQuery.cache, function(){
+						if ( this.events && this.events[type] )
+							jQuery.event.trigger( event, data, this.handle.elem );
+					});
+			}
+
+			// Handle triggering a single element
+
+			// don't do events on text and comment nodes
+			if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 )
+				return undefined;
+			
+			// Clean up in case it is reused
+			event.result = undefined;
+			event.target = elem;
+			
+			// Clone the incoming data, if any
+			data = jQuery.makeArray(data);
+			data.unshift( event );
+		}
+
+		event.currentTarget = elem;
+
+		// Trigger the event, it is assumed that "handle" is a function
+		var handle = jQuery.data(elem, "handle");
+		if ( handle )
+			handle.apply( elem, data );
+
+		// Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
+		if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
+			event.result = false;
+
+		// Trigger the native events (except for clicks on links)
+		if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
+			this.triggered = true;
+			try {
+				elem[ type ]();
+			// prevent IE from throwing an error for some hidden elements
+			} catch (e) {}
+		}
+
+		this.triggered = false;
+
+		if ( !event.isPropagationStopped() ) {
+			var parent = elem.parentNode || elem.ownerDocument;
+			if ( parent )
+				jQuery.event.trigger(event, data, parent, true);
+		}
+	},
+
+	handle: function(event) {
+		// returned undefined or false
+		var all, handlers;
+
+		event = arguments[0] = jQuery.event.fix( event || window.event );
+		event.currentTarget = this;
+		
+		// Namespaced event handlers
+		var namespaces = event.type.split(".");
+		event.type = namespaces.shift();
+
+		// Cache this now, all = true means, any handler
+		all = !namespaces.length && !event.exclusive;
+		
+		var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
+
+		handlers = ( jQuery.data(this, "events") || {} )[event.type];
+
+		for ( var j in handlers ) {
+			var handler = handlers[j];
+
+			// Filter the functions by class
+			if ( all || namespace.test(handler.type) ) {
+				// Pass in a reference to the handler function itself
+				// So that we can later remove it
+				event.handler = handler;
+				event.data = handler.data;
+
+				var ret = handler.apply(this, arguments);
+
+				if( ret !== undefined ){
+					event.result = ret;
+					if ( ret === false ) {
+						event.preventDefault();
+						event.stopPropagation();
+					}
+				}
+
+				if( event.isImmediatePropagationStopped() )
+					break;
+
+			}
+		}
+	},
+
+	props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+	fix: function(event) {
+		if ( event[expando] )
+			return event;
+
+		// store a copy of the original event object
+		// and "clone" to set read-only properties
+		var originalEvent = event;
+		event = jQuery.Event( originalEvent );
+
+		for ( var i = this.props.length, prop; i; ){
+			prop = this.props[ --i ];
+			event[ prop ] = originalEvent[ prop ];
+		}
+
+		// Fix target property, if necessary
+		if ( !event.target )
+			event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
+
+		// check if target is a textnode (safari)
+		if ( event.target.nodeType == 3 )
+			event.target = event.target.parentNode;
+
+		// Add relatedTarget, if necessary
+		if ( !event.relatedTarget && event.fromElement )
+			event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
+
+		// Calculate pageX/Y if missing and clientX/Y available
+		if ( event.pageX == null && event.clientX != null ) {
+			var doc = document.documentElement, body = document.body;
+			event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
+			event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
+		}
+
+		// Add which for key events
+		if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
+			event.which = event.charCode || event.keyCode;
+
+		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+		if ( !event.metaKey && event.ctrlKey )
+			event.metaKey = event.ctrlKey;
+
+		// Add which for click: 1 == left; 2 == middle; 3 == right
+		// Note: button is not normalized, so don't use it
+		if ( !event.which && event.button )
+			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+
+		return event;
+	},
+
+	proxy: function( fn, proxy ){
+		proxy = proxy || function(){ return fn.apply(this, arguments); };
+		// Set the guid of unique handler to the same of original handler, so it can be removed
+		proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
+		// So proxy can be declared as an argument
+		return proxy;
+	},
+
+	special: {
+		ready: {
+			// Make sure the ready event is setup
+			setup: bindReady,
+			teardown: function() {}
+		}
+	},
+	
+	specialAll: {
+		live: {
+			setup: function( selector, namespaces ){
+				jQuery.event.add( this, namespaces[0], liveHandler );
+			},
+			teardown:  function( namespaces ){
+				if ( namespaces.length ) {
+					var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
+					
+					jQuery.each( (jQuery.data(this, "events").live || {}), function(){
+						if ( name.test(this.type) )
+							remove++;
+					});
+					
+					if ( remove < 1 )
+						jQuery.event.remove( this, namespaces[0], liveHandler );
+				}
+			}
+		}
+	}
+};
+
+jQuery.Event = function( src ){
+	// Allow instantiation without the 'new' keyword
+	if( !this.preventDefault )
+		return new jQuery.Event(src);
+	
+	// Event object
+	if( src && src.type ){
+		this.originalEvent = src;
+		this.type = src.type;
+	// Event type
+	}else
+		this.type = src;
+
+	// timeStamp is buggy for some events on Firefox(#3843)
+	// So we won't rely on the native value
+	this.timeStamp = now();
+	
+	// Mark it as fixed
+	this[expando] = true;
+};
+
+function returnFalse(){
+	return false;
+}
+function returnTrue(){
+	return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+	preventDefault: function() {
+		this.isDefaultPrevented = returnTrue;
+
+		var e = this.originalEvent;
+		if( !e )
+			return;
+		// if preventDefault exists run it on the original event
+		if (e.preventDefault)
+			e.preventDefault();
+		// otherwise set the returnValue property of the original event to false (IE)
+		e.returnValue = false;
+	},
+	stopPropagation: function() {
+		this.isPropagationStopped = returnTrue;
+
+		var e = this.originalEvent;
+		if( !e )
+			return;
+		// if stopPropagation exists run it on the original event
+		if (e.stopPropagation)
+			e.stopPropagation();
+		// otherwise set the cancelBubble property of the original event to true (IE)
+		e.cancelBubble = true;
+	},
+	stopImmediatePropagation:function(){
+		this.isImmediatePropagationStopped = returnTrue;
+		this.stopPropagation();
+	},
+	isDefaultPrevented: returnFalse,
+	isPropagationStopped: returnFalse,
+	isImmediatePropagationStopped: returnFalse
+};
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function(event) {
+	// Check if mouse(over|out) are still within the same parent element
+	var parent = event.relatedTarget;
+	// Traverse up the tree
+	while ( parent && parent != this )
+		try { parent = parent.parentNode; }
+		catch(e) { parent = this; }
+	
+	if( parent != this ){
+		// set the correct event type
+		event.type = event.data;
+		// handle event if we actually just moused on to a non sub-element
+		jQuery.event.handle.apply( this, arguments );
+	}
+};
+	
+jQuery.each({ 
+	mouseover: 'mouseenter', 
+	mouseout: 'mouseleave'
+}, function( orig, fix ){
+	jQuery.event.special[ fix ] = {
+		setup: function(){
+			jQuery.event.add( this, orig, withinElement, fix );
+		},
+		teardown: function(){
+			jQuery.event.remove( this, orig, withinElement );
+		}
+	};			   
+});
+
+jQuery.fn.extend({
+	bind: function( type, data, fn ) {
+		return type == "unload" ? this.one(type, data, fn) : this.each(function(){
+			jQuery.event.add( this, type, fn || data, fn && data );
+		});
+	},
+
+	one: function( type, data, fn ) {
+		var one = jQuery.event.proxy( fn || data, function(event) {
+			jQuery(this).unbind(event, one);
+			return (fn || data).apply( this, arguments );
+		});
+		return this.each(function(){
+			jQuery.event.add( this, type, one, fn && data);
+		});
+	},
+
+	unbind: function( type, fn ) {
+		return this.each(function(){
+			jQuery.event.remove( this, type, fn );
+		});
+	},
+
+	trigger: function( type, data ) {
+		return this.each(function(){
+			jQuery.event.trigger( type, data, this );
+		});
+	},
+
+	triggerHandler: function( type, data ) {
+		if( this[0] ){
+			var event = jQuery.Event(type);
+			event.preventDefault();
+			event.stopPropagation();
+			jQuery.event.trigger( event, data, this[0] );
+			return event.result;
+		}		
+	},
+
+	toggle: function( fn ) {
+		// Save reference to arguments for access in closure
+		var args = arguments, i = 1;
+
+		// link all the functions, so any of them can unbind this click handler
+		while( i < args.length )
+			jQuery.event.proxy( fn, args[i++] );
+
+		return this.click( jQuery.event.proxy( fn, function(event) {
+			// Figure out which function to execute
+			this.lastToggle = ( this.lastToggle || 0 ) % i;
+
+			// Make sure that clicks stop
+			event.preventDefault();
+
+			// and execute the function
+			return args[ this.lastToggle++ ].apply( this, arguments ) || false;
+		}));
+	},
+
+	hover: function(fnOver, fnOut) {
+		return this.mouseenter(fnOver).mouseleave(fnOut);
+	},
+
+	ready: function(fn) {
+		// Attach the listeners
+		bindReady();
+
+		// If the DOM is already ready
+		if ( jQuery.isReady )
+			// Execute the function immediately
+			fn.call( document, jQuery );
+
+		// Otherwise, remember the function for later
+		else
+			// Add the function to the wait list
+			jQuery.readyList.push( fn );
+
+		return this;
+	},
+	
+	live: function( type, fn ){
+		var proxy = jQuery.event.proxy( fn );
+		proxy.guid += this.selector + type;
+
+		jQuery(document).bind( liveConvert(type, this.selector), this.selector, proxy );
+
+		return this;
+	},
+	
+	die: function( type, fn ){
+		jQuery(document).unbind( liveConvert(type, this.selector), fn ? { guid: fn.guid + this.selector + type } : null );
+		return this;
+	}
+});
+
+function liveHandler( event ){
+	var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"),
+		stop = true,
+		elems = [];
+
+	jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){
+		if ( check.test(fn.type) ) {
+			var elem = jQuery(event.target).closest(fn.data)[0];
+			if ( elem )
+				elems.push({ elem: elem, fn: fn });
+		}
+	});
+
+	elems.sort(function(a,b) {
+		return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
+	});
+	
+	jQuery.each(elems, function(){
+		if ( this.fn.call(this.elem, event, this.fn.data) === false )
+			return (stop = false);
+	});
+
+	return stop;
+}
+
+function liveConvert(type, selector){
+	return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
+}
+
+jQuery.extend({
+	isReady: false,
+	readyList: [],
+	// Handle when the DOM is ready
+	ready: function() {
+		// Make sure that the DOM is not already loaded
+		if ( !jQuery.isReady ) {
+			// Remember that the DOM is ready
+			jQuery.isReady = true;
+
+			// If there are functions bound, to execute
+			if ( jQuery.readyList ) {
+				// Execute all of them
+				jQuery.each( jQuery.readyList, function(){
+					this.call( document, jQuery );
+				});
+
+				// Reset the list of functions
+				jQuery.readyList = null;
+			}
+
+			// Trigger any bound ready events
+			jQuery(document).triggerHandler("ready");
+		}
+	}
+});
+
+var readyBound = false;
+
+function bindReady(){
+	if ( readyBound ) return;
+	readyBound = true;
+
+	// Mozilla, Opera and webkit nightlies currently support this event
+	if ( document.addEventListener ) {
+		// Use the handy event callback
+		document.addEventListener( "DOMContentLoaded", function(){
+			document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
+			jQuery.ready();
+		}, false );
+
+	// If IE event model is used
+	} else if ( document.attachEvent ) {
+		// ensure firing before onload,
+		// maybe late but safe also for iframes
+		document.attachEvent("onreadystatechange", function(){
+			if ( document.readyState === "complete" ) {
+				document.detachEvent( "onreadystatechange", arguments.callee );
+				jQuery.ready();
+			}
+		});
+
+		// If IE and not an iframe
+		// continually check to see if the document is ready
+		if ( document.documentElement.doScroll && window == window.top ) (function(){
+			if ( jQuery.isReady ) return;
+
+			try {
+				// If IE is used, use the trick by Diego Perini
+				// http://javascript.nwbox.com/IEContentLoaded/
+				document.documentElement.doScroll("left");
+			} catch( error ) {
+				setTimeout( arguments.callee, 0 );
+				return;
+			}
+
+			// and execute any waiting functions
+			jQuery.ready();
+		})();
+	}
+
+	// A fallback to window.onload, that will always work
+	jQuery.event.add( window, "load", jQuery.ready );
+}
+
+jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
+	"mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
+	"change,select,submit,keydown,keypress,keyup,error").split(","), function(i, name){
+
+	// Handle event binding
+	jQuery.fn[name] = function(fn){
+		return fn ? this.bind(name, fn) : this.trigger(name);
+	};
+});
+
+// Prevent memory leaks in IE
+// And prevent errors on refresh with events like mouseover in other browsers
+// Window isn't included so as not to unbind existing unload events
+jQuery( window ).bind( 'unload', function(){ 
+	for ( var id in jQuery.cache )
+		// Skip the window
+		if ( id != 1 && jQuery.cache[ id ].handle )
+			jQuery.event.remove( jQuery.cache[ id ].handle.elem );
+}); 
+(function(){
+
+	jQuery.support = {};
+
+	var root = document.documentElement,
+		script = document.createElement("script"),
+		div = document.createElement("div"),
+		id = "script" + (new Date).getTime();
+
+	div.style.display = "none";
+	div.innerHTML = '   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';
+
+	var all = div.getElementsByTagName("*"),
+		a = div.getElementsByTagName("a")[0];
+
+	// Can't get basic test support
+	if ( !all || !all.length || !a ) {
+		return;
+	}
+
+	jQuery.support = {
+		// IE strips leading whitespace when .innerHTML is used
+		leadingWhitespace: div.firstChild.nodeType == 3,
+		
+		// Make sure that tbody elements aren't automatically inserted
+		// IE will insert them into empty tables
+		tbody: !div.getElementsByTagName("tbody").length,
+		
+		// Make sure that you can get all elements in an <object> element
+		// IE 7 always returns no results
+		objectAll: !!div.getElementsByTagName("object")[0]
+			.getElementsByTagName("*").length,
+		
+		// Make sure that link elements get serialized correctly by innerHTML
+		// This requires a wrapper element in IE
+		htmlSerialize: !!div.getElementsByTagName("link").length,
+		
+		// Get the style information from getAttribute
+		// (IE uses .cssText insted)
+		style: /red/.test( a.getAttribute("style") ),
+		
+		// Make sure that URLs aren't manipulated
+		// (IE normalizes it by default)
+		hrefNormalized: a.getAttribute("href") === "/a",
+		
+		// Make sure that element opacity exists
+		// (IE uses filter instead)
+		opacity: a.style.opacity === "0.5",
+		
+		// Verify style float existence
+		// (IE uses styleFloat instead of cssFloat)
+		cssFloat: !!a.style.cssFloat,
+
+		// Will be defined later
+		scriptEval: false,
+		noCloneEvent: true,
+		boxModel: null
+	};
+	
+	script.type = "text/javascript";
+	try {
+		script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
+	} catch(e){}
+
+	root.insertBefore( script, root.firstChild );
+	
+	// Make sure that the execution of code works by injecting a script
+	// tag with appendChild/createTextNode
+	// (IE doesn't support this, fails, and uses .text instead)
+	if ( window[ id ] ) {
+		jQuery.support.scriptEval = true;
+		delete window[ id ];
+	}
+
+	root.removeChild( script );
+
+	if ( div.attachEvent && div.fireEvent ) {
+		div.attachEvent("onclick", function(){
+			// Cloning a node shouldn't copy over any
+			// bound event handlers (IE does this)
+			jQuery.support.noCloneEvent = false;
+			div.detachEvent("onclick", arguments.callee);
+		});
+		div.cloneNode(true).fireEvent("onclick");
+	}
+
+	// Figure out if the W3C box model works as expected
+	// document.body must exist before we can do this
+	jQuery(function(){
+		var div = document.createElement("div");
+		div.style.width = div.style.paddingLeft = "1px";
+
+		document.body.appendChild( div );
+		jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
+		document.body.removeChild( div ).style.display = 'none';
+	});
+})();
+
+var styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat";
+
+jQuery.props = {
+	"for": "htmlFor",
+	"class": "className",
+	"float": styleFloat,
+	cssFloat: styleFloat,
+	styleFloat: styleFloat,
+	readonly: "readOnly",
+	maxlength: "maxLength",
+	cellspacing: "cellSpacing",
+	rowspan: "rowSpan",
+	tabindex: "tabIndex"
+};
+jQuery.fn.extend({
+	// Keep a copy of the old load
+	_load: jQuery.fn.load,
+
+	load: function( url, params, callback ) {
+		if ( typeof url !== "string" )
+			return this._load( url );
+
+		var off = url.indexOf(" ");
+		if ( off >= 0 ) {
+			var selector = url.slice(off, url.length);
+			url = url.slice(0, off);
+		}
+
+		// Default to a GET request
+		var type = "GET";
+
+		// If the second parameter was provided
+		if ( params )
+			// If it's a function
+			if ( jQuery.isFunction( params ) ) {
+				// We assume that it's the callback
+				callback = params;
+				params = null;
+
+			// Otherwise, build a param string
+			} else if( typeof params === "object" ) {
+				params = jQuery.param( params );
+				type = "POST";
+			}
+
+		var self = this;
+
+		// Request the remote document
+		jQuery.ajax({
+			url: url,
+			type: type,
+			dataType: "html",
+			data: params,
+			complete: function(res, status){
+				// If successful, inject the HTML into all the matched elements
+				if ( status == "success" || status == "notmodified" )
+					// See if a selector was specified
+					self.html( selector ?
+						// Create a dummy div to hold the results
+						jQuery("<div/>")
+							// inject the contents of the document in, removing the scripts
+							// to avoid any 'Permission Denied' errors in IE
+							.append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
+
+							// Locate the specified elements
+							.find(selector) :
+
+						// If not, just inject the full result
+						res.responseText );
+
+				if( callback )
+					self.each( callback, [res.responseText, status, res] );
+			}
+		});
+		return this;
+	},
+
+	serialize: function() {
+		return jQuery.param(this.serializeArray());
+	},
+	serializeArray: function() {
+		return this.map(function(){
+			return this.elements ? jQuery.makeArray(this.elements) : this;
+		})
+		.filter(function(){
+			return this.name && !this.disabled &&
+				(this.checked || /select|textarea/i.test(this.nodeName) ||
+					/text|hidden|password|search/i.test(this.type));
+		})
+		.map(function(i, elem){
+			var val = jQuery(this).val();
+			return val == null ? null :
+				jQuery.isArray(val) ?
+					jQuery.map( val, function(val, i){
+						return {name: elem.name, value: val};
+					}) :
+					{name: elem.name, value: val};
+		}).get();
+	}
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
+	jQuery.fn[o] = function(f){
+		return this.bind(o, f);
+	};
+});
+
+var jsc = now();
+
+jQuery.extend({
+  
+	get: function( url, data, callback, type ) {
+		// shift arguments if data argument was ommited
+		if ( jQuery.isFunction( data ) ) {
+			callback = data;
+			data = null;
+		}
+
+		return jQuery.ajax({
+			type: "GET",
+			url: url,
+			data: data,
+			success: callback,
+			dataType: type
+		});
+	},
+
+	getScript: function( url, callback ) {
+		return jQuery.get(url, null, callback, "script");
+	},
+
+	getJSON: function( url, data, callback ) {
+		return jQuery.get(url, data, callback, "json");
+	},
+
+	post: function( url, data, callback, type ) {
+		if ( jQuery.isFunction( data ) ) {
+			callback = data;
+			data = {};
+		}
+
+		return jQuery.ajax({
+			type: "POST",
+			url: url,
+			data: data,
+			success: callback,
+			dataType: type
+		});
+	},
+
+	ajaxSetup: function( settings ) {
+		jQuery.extend( jQuery.ajaxSettings, settings );
+	},
+
+	ajaxSettings: {
+		url: location.href,
+		global: true,
+		type: "GET",
+		contentType: "application/x-www-form-urlencoded",
+		processData: true,
+		async: true,
+		/*
+		timeout: 0,
+		data: null,
+		username: null,
+		password: null,
+		*/
+		// Create the request object; Microsoft failed to properly
+		// implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
+		// This function can be overriden by calling jQuery.ajaxSetup
+		xhr:function(){
+			return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
+		},
+		accepts: {
+			xml: "application/xml, text/xml",
+			html: "text/html",
+			script: "text/javascript, application/javascript",
+			json: "application/json, text/javascript",
+			text: "text/plain",
+			_default: "*/*"
+		}
+	},
+
+	// Last-Modified header cache for next request
+	lastModified: {},
+
+	ajax: function( s ) {
+		// Extend the settings, but re-extend 's' so that it can be
+		// checked again later (in the test suite, specifically)
+		s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
+
+		var jsonp, jsre = /=\?(&|$)/g, status, data,
+			type = s.type.toUpperCase();
+
+		// convert data if not already a string
+		if ( s.data && s.processData && typeof s.data !== "string" )
+			s.data = jQuery.param(s.data);
+
+		// Handle JSONP Parameter Callbacks
+		if ( s.dataType == "jsonp" ) {
+			if ( type == "GET" ) {
+				if ( !s.url.match(jsre) )
+					s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+			} else if ( !s.data || !s.data.match(jsre) )
+				s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+			s.dataType = "json";
+		}
+
+		// Build temporary JSONP function
+		if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
+			jsonp = "jsonp" + jsc++;
+
+			// Replace the =? sequence both in the query string and the data
+			if ( s.data )
+				s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+			s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+			// We need to make sure
+			// that a JSONP style response is executed properly
+			s.dataType = "script";
+
+			// Handle JSONP-style loading
+			window[ jsonp ] = function(tmp){
+				data = tmp;
+				success();
+				complete();
+				// Garbage collect
+				window[ jsonp ] = undefined;
+				try{ delete window[ jsonp ]; } catch(e){}
+				if ( head )
+					head.removeChild( script );
+			};
+		}
+
+		if ( s.dataType == "script" && s.cache == null )
+			s.cache = false;
+
+		if ( s.cache === false && type == "GET" ) {
+			var ts = now();
+			// try replacing _= if it is there
+			var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
+			// if nothing was replaced, add timestamp to the end
+			s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
+		}
+
+		// If data is available, append data to url for get requests
+		if ( s.data && type == "GET" ) {
+			s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
+
+			// IE likes to send both get and post data, prevent this
+			s.data = null;
+		}
+
+		// Watch for a new set of requests
+		if ( s.global && ! jQuery.active++ )
+			jQuery.event.trigger( "ajaxStart" );
+
+		// Matches an absolute URL, and saves the domain
+		var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );
+
+		// If we're requesting a remote document
+		// and trying to load JSON or Script with a GET
+		if ( s.dataType == "script" && type == "GET" && parts
+			&& ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){
+
+			var head = document.getElementsByTagName("head")[0];
+			var script = document.createElement("script");
+			script.src = s.url;
+			if (s.scriptCharset)
+				script.charset = s.scriptCharset;
+
+			// Handle Script loading
+			if ( !jsonp ) {
+				var done = false;
+
+				// Attach handlers for all browsers
+				script.onload = script.onreadystatechange = function(){
+					if ( !done && (!this.readyState ||
+							this.readyState == "loaded" || this.readyState == "complete") ) {
+						done = true;
+						success();
+						complete();
+
+						// Handle memory leak in IE
+						script.onload = script.onreadystatechange = null;
+						head.removeChild( script );
+					}
+				};
+			}
+
+			head.appendChild(script);
+
+			// We handle everything using the script element injection
+			return undefined;
+		}
+
+		var requestDone = false;
+
+		// Create the request object
+		var xhr = s.xhr();
+
+		// Open the socket
+		// Passing null username, generates a login popup on Opera (#2865)
+		if( s.username )
+			xhr.open(type, s.url, s.async, s.username, s.password);
+		else
+			xhr.open(type, s.url, s.async);
+
+		// Need an extra try/catch for cross domain requests in Firefox 3
+		try {
+			// Set the correct header, if data is being sent
+			if ( s.data )
+				xhr.setRequestHeader("Content-Type", s.contentType);
+
+			// Set the If-Modified-Since header, if ifModified mode.
+			if ( s.ifModified )
+				xhr.setRequestHeader("If-Modified-Since",
+					jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
+
+			// Set header so the called script knows that it's an XMLHttpRequest
+			xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+
+			// Set the Accepts header for the server, depending on the dataType
+			xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
+				s.accepts[ s.dataType ] + ", */*" :
+				s.accepts._default );
+		} catch(e){}
+
+		// Allow custom headers/mimetypes and early abort
+		if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
+			// Handle the global AJAX counter
+			if ( s.global && ! --jQuery.active )
+				jQuery.event.trigger( "ajaxStop" );
+			// close opended socket
+			xhr.abort();
+			return false;
+		}
+
+		if ( s.global )
+			jQuery.event.trigger("ajaxSend", [xhr, s]);
+
+		// Wait for a response to come back
+		var onreadystatechange = function(isTimeout){
+			// The request was aborted, clear the interval and decrement jQuery.active
+			if (xhr.readyState == 0) {
+				if (ival) {
+					// clear poll interval
+					clearInterval(ival);
+					ival = null;
+					// Handle the global AJAX counter
+					if ( s.global && ! --jQuery.active )
+						jQuery.event.trigger( "ajaxStop" );
+				}
+			// The transfer is complete and the data is available, or the request timed out
+			} else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
+				requestDone = true;
+
+				// clear poll interval
+				if (ival) {
+					clearInterval(ival);
+					ival = null;
+				}
+
+				status = isTimeout == "timeout" ? "timeout" :
+					!jQuery.httpSuccess( xhr ) ? "error" :
+					s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" :
+					"success";
+
+				if ( status == "success" ) {
+					// Watch for, and catch, XML document parse errors
+					try {
+						// process the data (runs the xml through httpData regardless of callback)
+						data = jQuery.httpData( xhr, s.dataType, s );
+					} catch(e) {
+						status = "parsererror";
+					}
+				}
+
+				// Make sure that the request was successful or notmodified
+				if ( status == "success" ) {
+					// Cache Last-Modified header, if ifModified mode.
+					var modRes;
+					try {
+						modRes = xhr.getResponseHeader("Last-Modified");
+					} catch(e) {} // swallow exception thrown by FF if header is not available
+
+					if ( s.ifModified && modRes )
+						jQuery.lastModified[s.url] = modRes;
+
+					// JSONP handles its own success callback
+					if ( !jsonp )
+						success();
+				} else
+					jQuery.handleError(s, xhr, status);
+
+				// Fire the complete handlers
+				complete();
+
+				if ( isTimeout )
+					xhr.abort();
+
+				// Stop memory leaks
+				if ( s.async )
+					xhr = null;
+			}
+		};
+
+		if ( s.async ) {
+			// don't attach the handler to the request, just poll it instead
+			var ival = setInterval(onreadystatechange, 13);
+
+			// Timeout checker
+			if ( s.timeout > 0 )
+				setTimeout(function(){
+					// Check to see if the request is still happening
+					if ( xhr && !requestDone )
+						onreadystatechange( "timeout" );
+				}, s.timeout);
+		}
+
+		// Send the data
+		try {
+			xhr.send(s.data);
+		} catch(e) {
+			jQuery.handleError(s, xhr, null, e);
+		}
+
+		// firefox 1.5 doesn't fire statechange for sync requests
+		if ( !s.async )
+			onreadystatechange();
+
+		function success(){
+			// If a local callback was specified, fire it and pass it the data
+			if ( s.success )
+				s.success( data, status );
+
+			// Fire the global callback
+			if ( s.global )
+				jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
+		}
+
+		function complete(){
+			// Process result
+			if ( s.complete )
+				s.complete(xhr, status);
+
+			// The request was completed
+			if ( s.global )
+				jQuery.event.trigger( "ajaxComplete", [xhr, s] );
+
+			// Handle the global AJAX counter
+			if ( s.global && ! --jQuery.active )
+				jQuery.event.trigger( "ajaxStop" );
+		}
+
+		// return XMLHttpRequest to allow aborting the request etc.
+		return xhr;
+	},
+
+	handleError: function( s, xhr, status, e ) {
+		// If a local callback was specified, fire it
+		if ( s.error ) s.error( xhr, status, e );
+
+		// Fire the global callback
+		if ( s.global )
+			jQuery.event.trigger( "ajaxError", [xhr, s, e] );
+	},
+
+	// Counter for holding the number of active queries
+	active: 0,
+
+	// Determines if an XMLHttpRequest was successful or not
+	httpSuccess: function( xhr ) {
+		try {
+			// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
+			return !xhr.status && location.protocol == "file:" ||
+				( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223;
+		} catch(e){}
+		return false;
+	},
+
+	// Determines if an XMLHttpRequest returns NotModified
+	httpNotModified: function( xhr, url ) {
+		try {
+			var xhrRes = xhr.getResponseHeader("Last-Modified");
+
+			// Firefox always returns 200. check Last-Modified date
+			return xhr.status == 304 || xhrRes == jQuery.lastModified[url];
+		} catch(e){}
+		return false;
+	},
+
+	httpData: function( xhr, type, s ) {
+		var ct = xhr.getResponseHeader("content-type"),
+			xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
+			data = xml ? xhr.responseXML : xhr.responseText;
+
+		if ( xml && data.documentElement.tagName == "parsererror" )
+			throw "parsererror";
+			
+		// Allow a pre-filtering function to sanitize the response
+		// s != null is checked to keep backwards compatibility
+		if( s && s.dataFilter )
+			data = s.dataFilter( data, type );
+
+		// The filter can actually parse the response
+		if( typeof data === "string" ){
+
+			// If the type is "script", eval it in global context
+			if ( type == "script" )
+				jQuery.globalEval( data );
+
+			// Get the JavaScript object, if JSON is used.
+			if ( type == "json" )
+				data = window["eval"]("(" + data + ")");
+		}
+		
+		return data;
+	},
+
+	// Serialize an array of form elements or a set of
+	// key/values into a query string
+	param: function( a ) {
+		var s = [ ];
+
+		function add( key, value ){
+			s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
+		};
+
+		// If an array was passed in, assume that it is an array
+		// of form elements
+		if ( jQuery.isArray(a) || a.jquery )
+			// Serialize the form elements
+			jQuery.each( a, function(){
+				add( this.name, this.value );
+			});
+
+		// Otherwise, assume that it's an object of key/value pairs
+		else
+			// Serialize the key/values
+			for ( var j in a )
+				// If the value is an array then the key names need to be repeated
+				if ( jQuery.isArray(a[j]) )
+					jQuery.each( a[j], function(){
+						add( j, this );
+					});
+				else
+					add( j, jQuery.isFunction(a[j]) ? a[j]() : a[j] );
+
+		// Return the resulting serialization
+		return s.join("&").replace(/%20/g, "+");
+	}
+
+});
+var elemdisplay = {},
+	timerId,
+	fxAttrs = [
+		// height animations
+		[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+		// width animations
+		[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+		// opacity animations
+		[ "opacity" ]
+	];
+
+function genFx( type, num ){
+	var obj = {};
+	jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function(){
+		obj[ this ] = type;
+	});
+	return obj;
+}
+
+jQuery.fn.extend({
+	show: function(speed,callback){
+		if ( speed ) {
+			return this.animate( genFx("show", 3), speed, callback);
+		} else {
+			for ( var i = 0, l = this.length; i < l; i++ ){
+				var old = jQuery.data(this[i], "olddisplay");
+				
+				this[i].style.display = old || "";
+				
+				if ( jQuery.css(this[i], "display") === "none" ) {
+					var tagName = this[i].tagName, display;
+					
+					if ( elemdisplay[ tagName ] ) {
+						display = elemdisplay[ tagName ];
+					} else {
+						var elem = jQuery("<" + tagName + " />").appendTo("body");
+						
+						display = elem.css("display");
+						if ( display === "none" )
+							display = "block";
+						
+						elem.remove();
+						
+						elemdisplay[ tagName ] = display;
+					}
+					
+					jQuery.data(this[i], "olddisplay", display);
+				}
+			}
+
+			// Set the display of the elements in a second loop
+			// to avoid the constant reflow
+			for ( var i = 0, l = this.length; i < l; i++ ){
+				this[i].style.display = jQuery.data(this[i], "olddisplay") || "";
+			}
+			
+			return this;
+		}
+	},
+
+	hide: function(speed,callback){
+		if ( speed ) {
+			return this.animate( genFx("hide", 3), speed, callback);
+		} else {
+			for ( var i = 0, l = this.length; i < l; i++ ){
+				var old = jQuery.data(this[i], "olddisplay");
+				if ( !old && old !== "none" )
+					jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
+			}
+
+			// Set the display of the elements in a second loop
+			// to avoid the constant reflow
+			for ( var i = 0, l = this.length; i < l; i++ ){
+				this[i].style.display = "none";
+			}
+
+			return this;
+		}
+	},
+
+	// Save the old toggle function
+	_toggle: jQuery.fn.toggle,
+
+	toggle: function( fn, fn2 ){
+		var bool = typeof fn === "boolean";
+
+		return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
+			this._toggle.apply( this, arguments ) :
+			fn == null || bool ?
+				this.each(function(){
+					var state = bool ? fn : jQuery(this).is(":hidden");
+					jQuery(this)[ state ? "show" : "hide" ]();
+				}) :
+				this.animate(genFx("toggle", 3), fn, fn2);
+	},
+
+	fadeTo: function(speed,to,callback){
+		return this.animate({opacity: to}, speed, callback);
+	},
+
+	animate: function( prop, speed, easing, callback ) {
+		var optall = jQuery.speed(speed, easing, callback);
+
+		return this[ optall.queue === false ? "each" : "queue" ](function(){
+		
+			var opt = jQuery.extend({}, optall), p,
+				hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),
+				self = this;
+	
+			for ( p in prop ) {
+				if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
+					return opt.complete.call(this);
+
+				if ( ( p == "height" || p == "width" ) && this.style ) {
+					// Store display property
+					opt.display = jQuery.css(this, "display");
+
+					// Make sure that nothing sneaks out
+					opt.overflow = this.style.overflow;
+				}
+			}
+
+			if ( opt.overflow != null )
+				this.style.overflow = "hidden";
+
+			opt.curAnim = jQuery.extend({}, prop);
+
+			jQuery.each( prop, function(name, val){
+				var e = new jQuery.fx( self, opt, name );
+
+				if ( /toggle|show|hide/.test(val) )
+					e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
+				else {
+					var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
+						start = e.cur(true) || 0;
+
+					if ( parts ) {
+						var end = parseFloat(parts[2]),
+							unit = parts[3] || "px";
+
+						// We need to compute starting value
+						if ( unit != "px" ) {
+							self.style[ name ] = (end || 1) + unit;
+							start = ((end || 1) / e.cur(true)) * start;
+							self.style[ name ] = start + unit;
+						}
+
+						// If a +=/-= token was provided, we're doing a relative animation
+						if ( parts[1] )
+							end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
+
+						e.custom( start, end, unit );
+					} else
+						e.custom( start, val, "" );
+				}
+			});
+
+			// For JS strict compliance
+			return true;
+		});
+	},
+
+	stop: function(clearQueue, gotoEnd){
+		var timers = jQuery.timers;
+
+		if (clearQueue)
+			this.queue([]);
+
+		this.each(function(){
+			// go in reverse order so anything added to the queue during the loop is ignored
+			for ( var i = timers.length - 1; i >= 0; i-- )
+				if ( timers[i].elem == this ) {
+					if (gotoEnd)
+						// force the next step to be the last
+						timers[i](true);
+					timers.splice(i, 1);
+				}
+		});
+
+		// start the next in the queue if the last step wasn't forced
+		if (!gotoEnd)
+			this.dequeue();
+
+		return this;
+	}
+
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+	slideDown: genFx("show", 1),
+	slideUp: genFx("hide", 1),
+	slideToggle: genFx("toggle", 1),
+	fadeIn: { opacity: "show" },
+	fadeOut: { opacity: "hide" }
+}, function( name, props ){
+	jQuery.fn[ name ] = function( speed, callback ){
+		return this.animate( props, speed, callback );
+	};
+});
+
+jQuery.extend({
+
+	speed: function(speed, easing, fn) {
+		var opt = typeof speed === "object" ? speed : {
+			complete: fn || !fn && easing ||
+				jQuery.isFunction( speed ) && speed,
+			duration: speed,
+			easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+		};
+
+		opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+			jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
+
+		// Queueing
+		opt.old = opt.complete;
+		opt.complete = function(){
+			if ( opt.queue !== false )
+				jQuery(this).dequeue();
+			if ( jQuery.isFunction( opt.old ) )
+				opt.old.call( this );
+		};
+
+		return opt;
+	},
+
+	easing: {
+		linear: function( p, n, firstNum, diff ) {
+			return firstNum + diff * p;
+		},
+		swing: function( p, n, firstNum, diff ) {
+			return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+		}
+	},
+
+	timers: [],
+
+	fx: function( elem, options, prop ){
+		this.options = options;
+		this.elem = elem;
+		this.prop = prop;
+
+		if ( !options.orig )
+			options.orig = {};
+	}
+
+});
+
+jQuery.fx.prototype = {
+
+	// Simple function for setting a style value
+	update: function(){
+		if ( this.options.step )
+			this.options.step.call( this.elem, this.now, this );
+
+		(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+
+		// Set display property to block for height/width animations
+		if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style )
+			this.elem.style.display = "block";
+	},
+
+	// Get the current size
+	cur: function(force){
+		if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) )
+			return this.elem[ this.prop ];
+
+		var r = parseFloat(jQuery.css(this.elem, this.prop, force));
+		return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
+	},
+
+	// Start an animation from one number to another
+	custom: function(from, to, unit){
+		this.startTime = now();
+		this.start = from;
+		this.end = to;
+		this.unit = unit || this.unit || "px";
+		this.now = this.start;
+		this.pos = this.state = 0;
+
+		var self = this;
+		function t(gotoEnd){
+			return self.step(gotoEnd);
+		}
+
+		t.elem = this.elem;
+
+		if ( t() && jQuery.timers.push(t) && !timerId ) {
+			timerId = setInterval(function(){
+				var timers = jQuery.timers;
+
+				for ( var i = 0; i < timers.length; i++ )
+					if ( !timers[i]() )
+						timers.splice(i--, 1);
+
+				if ( !timers.length ) {
+					clearInterval( timerId );
+					timerId = undefined;
+				}
+			}, 13);
+		}
+	},
+
+	// Simple 'show' function
+	show: function(){
+		// Remember where we started, so that we can go back to it later
+		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
+		this.options.show = true;
+
+		// Begin the animation
+		// Make sure that we start at a small width/height to avoid any
+		// flash of content
+		this.custom(this.prop == "width" || this.prop == "height" ? 1 : 0, this.cur());
+
+		// Start by showing the element
+		jQuery(this.elem).show();
+	},
+
+	// Simple 'hide' function
+	hide: function(){
+		// Remember where we started, so that we can go back to it later
+		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
+		this.options.hide = true;
+
+		// Begin the animation
+		this.custom(this.cur(), 0);
+	},
+
+	// Each step of an animation
+	step: function(gotoEnd){
+		var t = now();
+
+		if ( gotoEnd || t >= this.options.duration + this.startTime ) {
+			this.now = this.end;
+			this.pos = this.state = 1;
+			this.update();
+
+			this.options.curAnim[ this.prop ] = true;
+
+			var done = true;
+			for ( var i in this.options.curAnim )
+				if ( this.options.curAnim[i] !== true )
+					done = false;
+
+			if ( done ) {
+				if ( this.options.display != null ) {
+					// Reset the overflow
+					this.elem.style.overflow = this.options.overflow;
+
+					// Reset the display
+					this.elem.style.display = this.options.display;
+					if ( jQuery.css(this.elem, "display") == "none" )
+						this.elem.style.display = "block";
+				}
+
+				// Hide the element if the "hide" operation was done
+				if ( this.options.hide )
+					jQuery(this.elem).hide();
+
+				// Reset the properties, if the item has been hidden or shown
+				if ( this.options.hide || this.options.show )
+					for ( var p in this.options.curAnim )
+						jQuery.attr(this.elem.style, p, this.options.orig[p]);
+					
+				// Execute the complete function
+				this.options.complete.call( this.elem );
+			}
+
+			return false;
+		} else {
+			var n = t - this.startTime;
+			this.state = n / this.options.duration;
+
+			// Perform the easing function, defaults to swing
+			this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
+			this.now = this.start + ((this.end - this.start) * this.pos);
+
+			// Perform the next step of the animation
+			this.update();
+		}
+
+		return true;
+	}
+
+};
+
+jQuery.extend( jQuery.fx, {
+	speeds:{
+		slow: 600,
+ 		fast: 200,
+ 		// Default speed
+ 		_default: 400
+	},
+	step: {
+
+		opacity: function(fx){
+			jQuery.attr(fx.elem.style, "opacity", fx.now);
+		},
+
+		_default: function(fx){
+			if ( fx.elem.style && fx.elem.style[ fx.prop ] != null )
+				fx.elem.style[ fx.prop ] = fx.now + fx.unit;
+			else
+				fx.elem[ fx.prop ] = fx.now;
+		}
+	}
+});
+if ( document.documentElement["getBoundingClientRect"] )
+	jQuery.fn.offset = function() {
+		if ( !this[0] ) return { top: 0, left: 0 };
+		if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
+		var box  = this[0].getBoundingClientRect(), doc = this[0].ownerDocument, body = doc.body, docElem = doc.documentElement,
+			clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
+			top  = box.top  + (self.pageYOffset || jQuery.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop,
+			left = box.left + (self.pageXOffset || jQuery.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
+		return { top: top, left: left };
+	};
+else 
+	jQuery.fn.offset = function() {
+		if ( !this[0] ) return { top: 0, left: 0 };
+		if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
+		jQuery.offset.initialized || jQuery.offset.initialize();
+
+		var elem = this[0], offsetParent = elem.offsetParent, prevOffsetParent = elem,
+			doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
+			body = doc.body, defaultView = doc.defaultView,
+			prevComputedStyle = defaultView.getComputedStyle(elem, null),
+			top = elem.offsetTop, left = elem.offsetLeft;
+
+		while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+			computedStyle = defaultView.getComputedStyle(elem, null);
+			top -= elem.scrollTop, left -= elem.scrollLeft;
+			if ( elem === offsetParent ) {
+				top += elem.offsetTop, left += elem.offsetLeft;
+				if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) )
+					top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
+					left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
+				prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
+			}
+			if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" )
+				top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
+				left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
+			prevComputedStyle = computedStyle;
+		}
+
+		if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" )
+			top  += body.offsetTop,
+			left += body.offsetLeft;
+
+		if ( prevComputedStyle.position === "fixed" )
+			top  += Math.max(docElem.scrollTop, body.scrollTop),
+			left += Math.max(docElem.scrollLeft, body.scrollLeft);
+
+		return { top: top, left: left };
+	};
+
+jQuery.offset = {
+	initialize: function() {
+		if ( this.initialized ) return;
+		var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
+			html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';
+
+		rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
+		for ( prop in rules ) container.style[prop] = rules[prop];
+
+		container.innerHTML = html;
+		body.insertBefore(container, body.firstChild);
+		innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;
+
+		this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+		this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+		innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
+		this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+		body.style.marginTop = '1px';
+		this.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
+		body.style.marginTop = bodyMarginTop;
+
+		body.removeChild(container);
+		this.initialized = true;
+	},
+
+	bodyOffset: function(body) {
+		jQuery.offset.initialized || jQuery.offset.initialize();
+		var top = body.offsetTop, left = body.offsetLeft;
+		if ( jQuery.offset.doesNotIncludeMarginInBodyOffset )
+			top  += parseInt( jQuery.curCSS(body, 'marginTop',  true), 10 ) || 0,
+			left += parseInt( jQuery.curCSS(body, 'marginLeft', true), 10 ) || 0;
+		return { top: top, left: left };
+	}
+};
+
+
+jQuery.fn.extend({
+	position: function() {
+		var left = 0, top = 0, results;
+
+		if ( this[0] ) {
+			// Get *real* offsetParent
+			var offsetParent = this.offsetParent(),
+
+			// Get correct offsets
+			offset       = this.offset(),
+			parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+			// Subtract element margins
+			// note: when an element has margin: auto the offsetLeft and marginLeft 
+			// are the same in Safari causing offset.left to incorrectly be 0
+			offset.top  -= num( this, 'marginTop'  );
+			offset.left -= num( this, 'marginLeft' );
+
+			// Add offsetParent borders
+			parentOffset.top  += num( offsetParent, 'borderTopWidth'  );
+			parentOffset.left += num( offsetParent, 'borderLeftWidth' );
+
+			// Subtract the two offsets
+			results = {
+				top:  offset.top  - parentOffset.top,
+				left: offset.left - parentOffset.left
+			};
+		}
+
+		return results;
+	},
+
+	offsetParent: function() {
+		var offsetParent = this[0].offsetParent || document.body;
+		while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') )
+			offsetParent = offsetParent.offsetParent;
+		return jQuery(offsetParent);
+	}
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ['Left', 'Top'], function(i, name) {
+	var method = 'scroll' + name;
+	
+	jQuery.fn[ method ] = function(val) {
+		if (!this[0]) return null;
+
+		return val !== undefined ?
+
+			// Set the scroll offset
+			this.each(function() {
+				this == window || this == document ?
+					window.scrollTo(
+						!i ? val : jQuery(window).scrollLeft(),
+						 i ? val : jQuery(window).scrollTop()
+					) :
+					this[ method ] = val;
+			}) :
+
+			// Return the scroll offset
+			this[0] == window || this[0] == document ?
+				self[ i ? 'pageYOffset' : 'pageXOffset' ] ||
+					jQuery.boxModel && document.documentElement[ method ] ||
+					document.body[ method ] :
+				this[0][ method ];
+	};
+});
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function(i, name){
+
+	var tl = i ? "Left"  : "Top",  // top or left
+		br = i ? "Right" : "Bottom", // bottom or right
+		lower = name.toLowerCase();
+
+	// innerHeight and innerWidth
+	jQuery.fn["inner" + name] = function(){
+		return this[0] ?
+			jQuery.css( this[0], lower, false, "padding" ) :
+			null;
+	};
+
+	// outerHeight and outerWidth
+	jQuery.fn["outer" + name] = function(margin) {
+		return this[0] ?
+			jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) :
+			null;
+	};
+	
+	var type = name.toLowerCase();
+
+	jQuery.fn[ type ] = function( size ) {
+		// Get window width or height
+		return this[0] == window ?
+			// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+			document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] ||
+			document.body[ "client" + name ] :
+
+			// Get document width or height
+			this[0] == document ?
+				// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+				Math.max(
+					document.documentElement["client" + name],
+					document.body["scroll" + name], document.documentElement["scroll" + name],
+					document.body["offset" + name], document.documentElement["offset" + name]
+				) :
+
+				// Get or set width or height on the element
+				size === undefined ?
+					// Get width or height on the element
+					(this.length ? jQuery.css( this[0], type ) : null) :
+
+					// Set the width or height on the element (default to pixels if value is unitless)
+					this.css( type, typeof size === "string" ? size : size + "px" );
+	};
+
+});
+})();
Index: gutenbach-web/old/jquery-ui-personalized-1.6rc6.js
===================================================================
--- gutenbach-web/old/jquery-ui-personalized-1.6rc6.js	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/jquery-ui-personalized-1.6rc6.js	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,1028 @@
+/*
+ * jQuery UI 1.6rc6
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI
+ */
+;(function($) {
+
+var _remove = $.fn.remove,
+	isFF2 = $.browser.mozilla && (parseFloat($.browser.version) < 1.9);
+
+//Helper functions and ui object
+$.ui = {
+	version: "1.6rc6",
+
+	// $.ui.plugin is deprecated.  Use the proxy pattern instead.
+	plugin: {
+		add: function(module, option, set) {
+			var proto = $.ui[module].prototype;
+			for(var i in set) {
+				proto.plugins[i] = proto.plugins[i] || [];
+				proto.plugins[i].push([option, set[i]]);
+			}
+		},
+		call: function(instance, name, args) {
+			var set = instance.plugins[name];
+			if(!set) { return; }
+
+			for (var i = 0; i < set.length; i++) {
+				if (instance.options[set[i][0]]) {
+					set[i][1].apply(instance.element, args);
+				}
+			}
+		}
+	},
+
+	contains: function(a, b) {
+		return document.compareDocumentPosition
+			? a.compareDocumentPosition(b) & 16
+			: a !== b && a.contains(b);
+	},
+
+	cssCache: {},
+	css: function(name) {
+		if ($.ui.cssCache[name]) { return $.ui.cssCache[name]; }
+		var tmp = $('<div class="ui-gen"></div>').addClass(name).css({position:'absolute', top:'-5000px', left:'-5000px', display:'block'}).appendTo('body');
+
+		//if (!$.browser.safari)
+			//tmp.appendTo('body');
+
+		//Opera and Safari set width and height to 0px instead of auto
+		//Safari returns rgba(0,0,0,0) when bgcolor is not set
+		$.ui.cssCache[name] = !!(
+			(!(/auto|default/).test(tmp.css('cursor')) || (/^[1-9]/).test(tmp.css('height')) || (/^[1-9]/).test(tmp.css('width')) ||
+			!(/none/).test(tmp.css('backgroundImage')) || !(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor')))
+		);
+		try { $('body').get(0).removeChild(tmp.get(0));	} catch(e){}
+		return $.ui.cssCache[name];
+	},
+
+	hasScroll: function(el, a) {
+
+		//If overflow is hidden, the element might have extra content, but the user wants to hide it
+		if ($(el).css('overflow') == 'hidden') { return false; }
+
+		var scroll = (a && a == 'left') ? 'scrollLeft' : 'scrollTop',
+			has = false;
+
+		if (el[scroll] > 0) { return true; }
+
+		// TODO: determine which cases actually cause this to happen
+		// if the element doesn't have the scroll set, see if it's possible to
+		// set the scroll
+		el[scroll] = 1;
+		has = (el[scroll] > 0);
+		el[scroll] = 0;
+		return has;
+	},
+
+	isOverAxis: function(x, reference, size) {
+		//Determines when x coordinate is over "b" element axis
+		return (x > reference) && (x < (reference + size));
+	},
+
+	isOver: function(y, x, top, left, height, width) {
+		//Determines when x, y coordinates is over "b" element
+		return $.ui.isOverAxis(y, top, height) && $.ui.isOverAxis(x, left, width);
+	},
+
+	keyCode: {
+		BACKSPACE: 8,
+		CAPS_LOCK: 20,
+		COMMA: 188,
+		CONTROL: 17,
+		DELETE: 46,
+		DOWN: 40,
+		END: 35,
+		ENTER: 13,
+		ESCAPE: 27,
+		HOME: 36,
+		INSERT: 45,
+		LEFT: 37,
+		NUMPAD_ADD: 107,
+		NUMPAD_DECIMAL: 110,
+		NUMPAD_DIVIDE: 111,
+		NUMPAD_ENTER: 108,
+		NUMPAD_MULTIPLY: 106,
+		NUMPAD_SUBTRACT: 109,
+		PAGE_DOWN: 34,
+		PAGE_UP: 33,
+		PERIOD: 190,
+		RIGHT: 39,
+		SHIFT: 16,
+		SPACE: 32,
+		TAB: 9,
+		UP: 38
+	}
+};
+
+// WAI-ARIA normalization
+if (isFF2) {
+	var attr = $.attr,
+		removeAttr = $.fn.removeAttr,
+		ariaNS = "http://www.w3.org/2005/07/aaa",
+		ariaState = /^aria-/,
+		ariaRole = /^wairole:/;
+
+	$.attr = function(elem, name, value) {
+		var set = value !== undefined;
+
+		return (name == 'role'
+			? (set
+				? attr.call(this, elem, name, "wairole:" + value)
+				: (attr.apply(this, arguments) || "").replace(ariaRole, ""))
+			: (ariaState.test(name)
+				? (set
+					? elem.setAttributeNS(ariaNS,
+						name.replace(ariaState, "aaa:"), value)
+					: attr.call(this, elem, name.replace(ariaState, "aaa:")))
+				: attr.apply(this, arguments)));
+	};
+
+	$.fn.removeAttr = function(name) {
+		return (ariaState.test(name)
+			? this.each(function() {
+				this.removeAttributeNS(ariaNS, name.replace(ariaState, ""));
+			}) : removeAttr.call(this, name));
+	};
+}
+
+//jQuery plugins
+$.fn.extend({
+	remove: function() {
+		// Safari has a native remove event which actually removes DOM elements,
+		// so we have to use triggerHandler instead of trigger (#3037).
+		$("*", this).add(this).each(function() {
+			$(this).triggerHandler("remove");
+		});
+		return _remove.apply(this, arguments );
+	},
+
+	enableSelection: function() {
+		return this
+			.attr('unselectable', 'off')
+			.css('MozUserSelect', '')
+			.unbind('selectstart.ui');
+	},
+
+	disableSelection: function() {
+		return this
+			.attr('unselectable', 'on')
+			.css('MozUserSelect', 'none')
+			.bind('selectstart.ui', function() { return false; });
+	},
+
+	scrollParent: function() {
+		var scrollParent;
+		if(($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
+			scrollParent = this.parents().filter(function() {
+				return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+			}).eq(0);
+		} else {
+			scrollParent = this.parents().filter(function() {
+				return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+			}).eq(0);
+		}
+
+		return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
+	}
+});
+
+
+//Additional selectors
+$.extend($.expr[':'], {
+	data: function(elem, i, match) {
+		return !!$.data(elem, match[3]);
+	},
+
+	focusable: function(element) {
+		var nodeName = element.nodeName.toLowerCase(),
+			tabIndex = $.attr(element, 'tabindex');
+		return (/input|select|textarea|button|object/.test(nodeName)
+			? !element.disabled
+			: 'a' == nodeName || 'area' == nodeName
+				? element.href || !isNaN(tabIndex)
+				: !isNaN(tabIndex))
+			// the element and all of its ancestors must be visible
+			// the browser may report that the area is hidden
+			&& !$(element)['area' == nodeName ? 'parents' : 'closest'](':hidden').length;
+	},
+
+	tabbable: function(element) {
+		var tabIndex = $.attr(element, 'tabindex');
+		return (isNaN(tabIndex) || tabIndex >= 0) && $(element).is(':focusable');
+	}
+});
+
+
+// $.widget is a factory to create jQuery plugins
+// taking some boilerplate code out of the plugin code
+function getter(namespace, plugin, method, args) {
+	function getMethods(type) {
+		var methods = $[namespace][plugin][type] || [];
+		return (typeof methods == 'string' ? methods.split(/,?\s+/) : methods);
+	}
+
+	var methods = getMethods('getter');
+	if (args.length == 1 && typeof args[0] == 'string') {
+		methods = methods.concat(getMethods('getterSetter'));
+	}
+	return ($.inArray(method, methods) != -1);
+}
+
+$.widget = function(name, prototype) {
+	var namespace = name.split(".")[0];
+	name = name.split(".")[1];
+
+	// create plugin method
+	$.fn[name] = function(options) {
+		var isMethodCall = (typeof options == 'string'),
+			args = Array.prototype.slice.call(arguments, 1);
+
+		// prevent calls to internal methods
+		if (isMethodCall && options.substring(0, 1) == '_') {
+			return this;
+		}
+
+		// handle getter methods
+		if (isMethodCall && getter(namespace, name, options, args)) {
+			var instance = $.data(this[0], name);
+			return (instance ? instance[options].apply(instance, args)
+				: undefined);
+		}
+
+		// handle initialization and non-getter methods
+		return this.each(function() {
+			var instance = $.data(this, name);
+
+			// constructor
+			(!instance && !isMethodCall &&
+				$.data(this, name, new $[namespace][name](this, options))._init());
+
+			// method call
+			(instance && isMethodCall && $.isFunction(instance[options]) &&
+				instance[options].apply(instance, args));
+		});
+	};
+
+	// create widget constructor
+	$[namespace] = $[namespace] || {};
+	$[namespace][name] = function(element, options) {
+		var self = this;
+
+		this.namespace = namespace;
+		this.widgetName = name;
+		this.widgetEventPrefix = $[namespace][name].eventPrefix || name;
+		this.widgetBaseClass = namespace + '-' + name;
+
+		this.options = $.extend({},
+			$.widget.defaults,
+			$[namespace][name].defaults,
+			$.metadata && $.metadata.get(element)[name],
+			options);
+
+		this.element = $(element)
+			.bind('setData.' + name, function(event, key, value) {
+				if (event.target == element) {
+					return self._setData(key, value);
+				}
+			})
+			.bind('getData.' + name, function(event, key) {
+				if (event.target == element) {
+					return self._getData(key);
+				}
+			})
+			.bind('remove', function() {
+				return self.destroy();
+			});
+	};
+
+	// add widget prototype
+	$[namespace][name].prototype = $.extend({}, $.widget.prototype, prototype);
+
+	// TODO: merge getter and getterSetter properties from widget prototype
+	// and plugin prototype
+	$[namespace][name].getterSetter = 'option';
+};
+
+$.widget.prototype = {
+	_init: function() {},
+	destroy: function() {
+		this.element.removeData(this.widgetName)
+			.removeClass(this.widgetBaseClass + '-disabled' + ' ' + this.namespace + '-state-disabled')
+			.removeAttr('aria-disabled');
+	},
+
+	option: function(key, value) {
+		var options = key,
+			self = this;
+
+		if (typeof key == "string") {
+			if (value === undefined) {
+				return this._getData(key);
+			}
+			options = {};
+			options[key] = value;
+		}
+
+		$.each(options, function(key, value) {
+			self._setData(key, value);
+		});
+	},
+	_getData: function(key) {
+		return this.options[key];
+	},
+	_setData: function(key, value) {
+		this.options[key] = value;
+
+		if (key == 'disabled') {
+			this.element
+				[value ? 'addClass' : 'removeClass'](
+					this.widgetBaseClass + '-disabled' + ' ' +
+					this.namespace + '-state-disabled')
+				.attr("aria-disabled", value);
+		}
+	},
+
+	enable: function() {
+		this._setData('disabled', false);
+	},
+	disable: function() {
+		this._setData('disabled', true);
+	},
+
+	_trigger: function(type, event, data) {
+		var callback = this.options[type],
+			eventName = (type == this.widgetEventPrefix
+				? type : this.widgetEventPrefix + type);
+
+		event = $.Event(event);
+		event.type = eventName;
+
+		// copy original event properties over to the new event
+		// this would happen if we could call $.event.fix instead of $.Event
+		// but we don't have a way to force an event to be fixed multiple times
+		if (event.originalEvent) {
+			for (var i = $.event.props.length, prop; i;) {
+				prop = $.event.props[--i];
+				event[prop] = event.originalEvent[prop];
+			}
+		}
+
+		this.element.trigger(event, data);
+
+		return !($.isFunction(callback) && callback.call(this.element[0], event, data) === false
+			|| event.isDefaultPrevented());
+	}
+};
+
+$.widget.defaults = {
+	disabled: false
+};
+
+
+/** Mouse Interaction Plugin **/
+
+$.ui.mouse = {
+	_mouseInit: function() {
+		var self = this;
+
+		this.element
+			.bind('mousedown.'+this.widgetName, function(event) {
+				return self._mouseDown(event);
+			})
+			.bind('click.'+this.widgetName, function(event) {
+				if(self._preventClickEvent) {
+					self._preventClickEvent = false;
+					return false;
+				}
+			});
+
+		// Prevent text selection in IE
+		if ($.browser.msie) {
+			this._mouseUnselectable = this.element.attr('unselectable');
+			this.element.attr('unselectable', 'on');
+		}
+
+		this.started = false;
+	},
+
+	// TODO: make sure destroying one instance of mouse doesn't mess with
+	// other instances of mouse
+	_mouseDestroy: function() {
+		this.element.unbind('.'+this.widgetName);
+
+		// Restore text selection in IE
+		($.browser.msie
+			&& this.element.attr('unselectable', this._mouseUnselectable));
+	},
+
+	_mouseDown: function(event) {
+		// don't let more than one widget handle mouseStart
+		if (event.originalEvent.mouseHandled) { return; }
+
+		// we may have missed mouseup (out of window)
+		(this._mouseStarted && this._mouseUp(event));
+
+		this._mouseDownEvent = event;
+
+		var self = this,
+			btnIsLeft = (event.which == 1),
+			elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
+		if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
+			return true;
+		}
+
+		this.mouseDelayMet = !this.options.delay;
+		if (!this.mouseDelayMet) {
+			this._mouseDelayTimer = setTimeout(function() {
+				self.mouseDelayMet = true;
+			}, this.options.delay);
+		}
+
+		if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+			this._mouseStarted = (this._mouseStart(event) !== false);
+			if (!this._mouseStarted) {
+				event.preventDefault();
+				return true;
+			}
+		}
+
+		// these delegates are required to keep context
+		this._mouseMoveDelegate = function(event) {
+			return self._mouseMove(event);
+		};
+		this._mouseUpDelegate = function(event) {
+			return self._mouseUp(event);
+		};
+		$(document)
+			.bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+			.bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+		// preventDefault() is used to prevent the selection of text here -
+		// however, in Safari, this causes select boxes not to be selectable
+		// anymore, so this fix is needed
+		($.browser.safari || event.preventDefault());
+
+		event.originalEvent.mouseHandled = true;
+		return true;
+	},
+
+	_mouseMove: function(event) {
+		// IE mouseup check - mouseup happened when mouse was out of window
+		if ($.browser.msie && !event.button) {
+			return this._mouseUp(event);
+		}
+
+		if (this._mouseStarted) {
+			this._mouseDrag(event);
+			return event.preventDefault();
+		}
+
+		if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+			this._mouseStarted =
+				(this._mouseStart(this._mouseDownEvent, event) !== false);
+			(this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
+		}
+
+		return !this._mouseStarted;
+	},
+
+	_mouseUp: function(event) {
+		$(document)
+			.unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+			.unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+		if (this._mouseStarted) {
+			this._mouseStarted = false;
+			this._preventClickEvent = true;
+			this._mouseStop(event);
+		}
+
+		return false;
+	},
+
+	_mouseDistanceMet: function(event) {
+		return (Math.max(
+				Math.abs(this._mouseDownEvent.pageX - event.pageX),
+				Math.abs(this._mouseDownEvent.pageY - event.pageY)
+			) >= this.options.distance
+		);
+	},
+
+	_mouseDelayMet: function(event) {
+		return this.mouseDelayMet;
+	},
+
+	// These are placeholder methods, to be overriden by extending plugin
+	_mouseStart: function(event) {},
+	_mouseDrag: function(event) {},
+	_mouseStop: function(event) {},
+	_mouseCapture: function(event) { return true; }
+};
+
+$.ui.mouse.defaults = {
+	cancel: null,
+	distance: 1,
+	delay: 0
+};
+
+})(jQuery);
+/*
+ * jQuery UI Slider 1.6rc6
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ *	ui.core.js
+ */
+
+(function($) {
+
+$.widget("ui.slider", $.extend({}, $.ui.mouse, {
+
+	_init: function() {
+
+		var self = this, o = this.options;
+		this._keySliding = false;
+		this._handleIndex = null;
+		this._detectOrientation();
+		this._mouseInit();
+
+		this.element
+			.addClass("ui-slider"
+				+ " ui-slider-" + this.orientation
+				+ " ui-widget"
+				+ " ui-widget-content"
+				+ " ui-corner-all");
+
+		this.range = $([]);
+
+		if (o.range) {
+
+			if (o.range === true) {
+				this.range = $('<div></div>');
+				if (!o.values) o.values = [this._valueMin(), this._valueMin()];
+				if (o.values.length && o.values.length != 2) {
+					o.values = [o.values[0], o.values[0]];
+				}
+			} else {
+				this.range = $('<div></div>');
+			}
+
+			this.range
+				.appendTo(this.element)
+				.addClass("ui-slider-range"
+					+ " ui-widget-header");
+
+			(o.range == "min") && (this.orientation == "horizontal") && this.range.css({ left : 0 });
+			(o.range == "max") && (this.orientation == "horizontal") && this.range.css({ right : 0 });
+			(o.range == "min") && (this.orientation == "vertical") && this.range.css({ bottom : 0 });
+			(o.range == "max") && (this.orientation == "vertical") && this.range.css({ top : 0 });
+
+		}
+
+		if ($(".ui-slider-handle", this.element).length == 0)
+			$('<a href="#"></a>')
+				.appendTo(this.element)
+				.addClass("ui-slider-handle");
+
+		if (o.values && o.values.length) {
+			while ($(".ui-slider-handle", this.element).length < o.values.length)
+				$('<a href="#"></a>')
+					.appendTo(this.element)
+					.addClass("ui-slider-handle");
+		}
+
+		this.handles = $(".ui-slider-handle", this.element)
+			.addClass("ui-state-default"
+				+ " ui-corner-all");
+
+		this.handle = this.handles.eq(0);
+
+		this.handles.add(this.range).filter("a")
+			.click(function(event) { event.preventDefault(); })
+			.hover(function() { $(this).addClass('ui-state-hover'); }, function() { $(this).removeClass('ui-state-hover'); })
+			.focus(function() { self.handles.removeClass('ui-state-focus'); $(this).addClass('ui-state-focus'); })
+			.blur(function() { $(this).removeClass('ui-state-focus'); });
+
+		this.handles.each(function(i) {
+			$(this).data("index.ui-slider-handle", i);
+		});
+
+		this.handles.keydown(function(event) {
+
+			var index = $(this).data("index.ui-slider-handle");
+
+			if (self.options.disabled)
+				return;
+
+			switch (event.keyCode) {
+				case $.ui.keyCode.HOME:
+				case $.ui.keyCode.END:
+				case $.ui.keyCode.UP:
+				case $.ui.keyCode.RIGHT:
+				case $.ui.keyCode.DOWN:
+				case $.ui.keyCode.LEFT:
+					if (!self._keySliding) {
+						self._keySliding = true;
+						$(this).addClass("ui-state-active");
+						self._start(event);
+					}
+					break;
+			}
+
+			var curVal, newVal, step = self._step();
+			if (self.options.values && self.options.values.length) {
+				curVal = newVal = self.values(index);
+			} else {
+				curVal = newVal = self.value();
+			}
+
+			switch (event.keyCode) {
+				case $.ui.keyCode.HOME:
+					newVal = self._valueMin();
+					break;
+				case $.ui.keyCode.END:
+					newVal = self._valueMax();
+					break;
+				case $.ui.keyCode.UP:
+				case $.ui.keyCode.RIGHT:
+					if(curVal == self._valueMax()) return;
+					newVal = curVal + step;
+					break;
+				case $.ui.keyCode.DOWN:
+				case $.ui.keyCode.LEFT:
+					if(curVal == self._valueMin()) return;
+					newVal = curVal - step;
+					break;
+			}
+
+			self._slide(event, index, newVal);
+
+		}).keyup(function(event) {
+
+			if (self._keySliding) {
+				self._stop(event);
+				self._change(event);
+				self._keySliding = false;
+				$(this).removeClass("ui-state-active");
+			}
+
+		});
+
+		this._refreshValue();
+
+	},
+
+	destroy: function() {
+
+		this.handles.remove();
+
+		this.element
+			.removeClass("ui-slider"
+				+ " ui-slider-horizontal"
+				+ " ui-slider-vertical"
+				+ " ui-slider-disabled"
+				+ " ui-widget"
+				+ " ui-widget-content"
+				+ " ui-corner-all")
+			.removeData("slider")
+			.unbind(".slider");
+
+		this._mouseDestroy();
+
+	},
+
+	_mouseCapture: function(event) {
+
+		var o = this.options;
+
+		if (o.disabled)
+			return false;
+
+		this._start(event);
+
+		this.elementSize = {
+			width: this.element.outerWidth(),
+			height: this.element.outerHeight()
+		};
+		this.elementOffset = this.element.offset();
+
+		var position = { x: event.pageX, y: event.pageY };
+		var normValue = this._normValueFromMouse(position);
+
+		var distance = this._valueMax() + 1, closestHandle;
+		var self = this, index;
+		this.handles.each(function(i) {
+			var thisDistance = Math.abs(normValue - self.values(i));
+			if (distance > thisDistance) {
+				distance = thisDistance;
+				closestHandle = $(this);
+				index = i;
+			}
+		});
+		
+		// workaround for bug #3736 (if both handles of a range are at 0, the first is always used as the one with least distance,
+		// and moving it is obviously prevented by preventing negative ranges)
+		if(o.range && (this.values(0) + this.values(1)) == 0) {
+			closestHandle = $(this.handles[++index]);
+		}
+
+		self._handleIndex = index;
+
+		closestHandle
+			.addClass("ui-state-active")
+			.focus();
+		
+		var offset = closestHandle.offset();
+		var mouseOverHandle = !$(event.target).parents().andSelf().is('.ui-slider-handle');
+		this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
+			left: event.pageX - offset.left + (parseInt(closestHandle.css('marginLeft'),10) || 0),
+			top: event.pageY - offset.top + (parseInt(closestHandle.css('marginTop'),10) || 0)
+		};
+
+		normValue = this._normValueFromMouse(position);
+		this._slide(event, index, normValue);
+		return true;
+
+	},
+
+	_mouseStart: function(event) {
+		return true;
+	},
+
+	_mouseDrag: function(event) {
+
+		var position = { x: event.pageX, y: event.pageY };
+		var normValue = this._normValueFromMouse(position);
+		
+		this._slide(event, this._handleIndex, normValue);
+
+		return false;
+
+	},
+
+	_mouseStop: function(event) {
+
+		this.handles.removeClass("ui-state-active");
+		this._stop(event);
+		this._change(event);
+		this._handleIndex = null;
+		this._clickOffset = null;
+
+		return false;
+
+	},
+	
+	_detectOrientation: function() {
+		this.orientation = this.options.orientation == 'auto' ? (this.element[0].offsetWidth/this.element[0].offsetHeight > 1 ? 'horizontal' : 'vertical') : this.options.orientation;
+	},
+
+	_normValueFromMouse: function(position) {
+
+		var pixelTotal, pixelMouse;
+		if ('horizontal' == this.orientation) {
+			pixelTotal = this.elementSize.width;
+			pixelMouse = position.x - this.elementOffset.left - (this._clickOffset ? this._clickOffset.left : 0);
+		} else {
+			pixelTotal = this.elementSize.height;
+			pixelMouse = position.y - this.elementOffset.top - (this._clickOffset ? this._clickOffset.top : 0);
+		}
+
+		var percentMouse = (pixelMouse / pixelTotal);
+		if (percentMouse > 1) percentMouse = 1;
+		if (percentMouse < 0) percentMouse = 0;
+		if ('vertical' == this.orientation)
+			percentMouse = 1 - percentMouse;
+
+		var valueTotal = this._valueMax() - this._valueMin(),
+			valueMouse = percentMouse * valueTotal,
+			valueMouseModStep = valueMouse % this.options.step,
+			normValue = this._valueMin() + valueMouse - valueMouseModStep;
+
+		if (valueMouseModStep > (this.options.step / 2))
+			normValue += this.options.step;
+
+		return normValue;
+
+	},
+
+	_start: function(event) {
+		this._trigger("start", event, {
+			value: this.value()
+		});
+	},
+
+	_slide: function(event, index, newVal) {
+
+		var handle = this.handles[index];
+
+		if (this.options.values && this.options.values.length) {
+
+			var otherVal = this.values(index ? 0 : 1);
+
+			if ((index == 0 && newVal >= otherVal) || (index == 1 && newVal <= otherVal))
+				newVal = otherVal;
+
+			if (newVal != this.values(index)) {
+				var newValues = this.values();
+				newValues[index] = newVal;
+				// A slide can be canceled by returning false from the slide callback
+				var allowed = this._trigger("slide", event, {
+					handle: handle,
+					value: newVal,
+					values: newValues
+				});
+				var otherVal = this.values(index ? 0 : 1);
+				if (allowed !== false) {
+					this.values(index, newVal, !( event.type == 'mousedown' && this.options.animate ));
+				}
+			}
+
+		} else {
+
+			if (newVal != this.value()) {
+				// A slide can be canceled by returning false from the slide callback
+				var allowed = this._trigger("slide", event, {
+					handle: handle,
+					value: newVal
+				});
+				if (allowed !== false) {
+					this._setData('value', newVal, ( event.type == 'mousedown' && this.options.animate ));
+				}
+					
+			}
+
+		}
+
+	},
+
+	_stop: function(event) {
+		this._trigger("stop", event, {
+			value: this.value()
+		});
+	},
+
+	_change: function(event) {
+		this._trigger("change", event, {
+			value: this.value()
+		});
+	},
+
+	value: function(newValue) {
+
+		if (arguments.length) {
+			this._setData("value", newValue);
+			this._change();
+		}
+
+		return this._value();
+
+	},
+
+	values: function(index, newValue, noAnimation) {
+
+		if(!this.options.animate) noAnimation = true;
+
+		if (arguments.length > 1) {
+			this.options.values[index] = newValue;
+			this._refreshValue(!noAnimation);
+			this._change();
+		}
+
+		if (arguments.length) {
+			if (this.options.values && this.options.values.length) {
+				return this._values(index);
+			} else {
+				return this.value();
+			}
+		} else {
+			return this._values();
+		}
+
+	},
+
+	_setData: function(key, value) {
+
+		$.widget.prototype._setData.apply(this, arguments);
+
+		switch (key) {
+			case 'orientation':
+
+				this._detectOrientation();
+				
+				this.element
+					.removeClass("ui-slider-horizontal ui-slider-vertical")
+					.addClass("ui-slider-" + this.orientation);
+				this._refreshValue();
+				break;
+			case 'value':
+				this._refreshValue();
+				break;
+		}
+
+	},
+
+	_step: function() {
+		var step = this.options.step;
+		return step;
+	},
+
+	_value: function() {
+
+		var val = this.options.value;
+		if (val < this._valueMin()) val = this._valueMin();
+		if (val > this._valueMax()) val = this._valueMax();
+
+		return val;
+
+	},
+
+	_values: function(index) {
+
+		if (arguments.length) {
+			var val = this.options.values[index];
+			if (val < this._valueMin()) val = this._valueMin();
+			if (val > this._valueMax()) val = this._valueMax();
+
+			return val;
+		} else {
+			return this.options.values;
+		}
+
+	},
+
+	_valueMin: function() {
+		var valueMin = this.options.min;
+		return valueMin;
+	},
+
+	_valueMax: function() {
+		var valueMax = this.options.max;
+		return valueMax;
+	},
+
+	_refreshValue: function(animate) {
+
+		var oRange = this.options.range, o = this.options, self = this;
+
+		if (this.options.values && this.options.values.length) {
+			var vp0, vp1;
+			this.handles.each(function(i, j) {
+				var valPercent = (self.values(i) - self._valueMin()) / (self._valueMax() - self._valueMin()) * 100;
+				var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
+				$(this).stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
+				if (self.options.range === true) {
+					if (self.orientation == 'horizontal') {
+						(i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ left: valPercent + '%' }, o.animate);
+						(i == 1) && self.range[animate ? 'animate' : 'css']({ width: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
+					} else {
+						(i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ bottom: (valPercent) + '%' }, o.animate);
+						(i == 1) && self.range[animate ? 'animate' : 'css']({ height: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
+					}
+				}
+				lastValPercent = valPercent;
+			});
+		} else {
+			var valPercent = (this.value() - this._valueMin()) / (this._valueMax() - this._valueMin()) * 100;
+			var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
+			this.handle.stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
+
+			(oRange == "min") && (this.orientation == "horizontal") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ left: 0, width: valPercent + '%' }, o.animate);
+			(oRange == "max") && (this.orientation == "horizontal") && this.range[animate ? 'animate' : 'css']({ left: valPercent + '%', width: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
+			(oRange == "min") && (this.orientation == "vertical") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ top: (100 - valPercent) + '%', height: valPercent + '%' }, o.animate);
+			(oRange == "max") && (this.orientation == "vertical") && this.range[animate ? 'animate' : 'css']({ bottom: valPercent + '%', height: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
+		}
+
+	}
+
+}));
+
+$.extend($.ui.slider, {
+	getter: "value values",
+	version: "1.6rc6",
+	eventPrefix: "slide",
+	defaults: {
+		animate: false,
+		delay: 0,
+		distance: 0,
+		max: 100,
+		min: 0,
+		orientation: 'auto',
+		range: false,
+		step: 1,
+		value: 0,
+		values: null
+	}
+});
+
+})(jQuery);
Index: gutenbach-web/old/jquery-ui-personalized-1.6rc6.min.js
===================================================================
--- gutenbach-web/old/jquery-ui-personalized-1.6rc6.min.js	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/jquery-ui-personalized-1.6rc6.min.js	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,22 @@
+/*
+ * jQuery UI 1.6rc6
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI
+ */
+(function(c){var i=c.fn.remove,d=c.browser.mozilla&&(parseFloat(c.browser.version)<1.9);c.ui={version:"1.6rc6",plugin:{add:function(k,l,n){var m=c.ui[k].prototype;for(var j in n){m.plugins[j]=m.plugins[j]||[];m.plugins[j].push([l,n[j]])}},call:function(j,l,k){var n=j.plugins[l];if(!n){return}for(var m=0;m<n.length;m++){if(j.options[n[m][0]]){n[m][1].apply(j.element,k)}}}},contains:function(k,j){return document.compareDocumentPosition?k.compareDocumentPosition(j)&16:k!==j&&k.contains(j)},cssCache:{},css:function(j){if(c.ui.cssCache[j]){return c.ui.cssCache[j]}var k=c('<div class="ui-gen"></div>').addClass(j).css({position:"absolute",top:"-5000px",left:"-5000px",display:"block"}).appendTo("body");c.ui.cssCache[j]=!!((!(/auto|default/).test(k.css("cursor"))||(/^[1-9]/).test(k.css("height"))||(/^[1-9]/).test(k.css("width"))||!(/none/).test(k.css("backgroundImage"))||!(/transparent|rgba\(0, 0, 0, 0\)/).test(k.css("backgroundColor"))));try{c("body").get(0).removeChild(k.get(0))}catch(l){}return c.ui.cssCache[j]},hasScroll:function(m,k){if(c(m).css("overflow")=="hidden"){return false}var j=(k&&k=="left")?"scrollLeft":"scrollTop",l=false;if(m[j]>0){return true}m[j]=1;l=(m[j]>0);m[j]=0;return l},isOverAxis:function(k,j,l){return(k>j)&&(k<(j+l))},isOver:function(o,k,n,m,j,l){return c.ui.isOverAxis(o,n,j)&&c.ui.isOverAxis(k,m,l)},keyCode:{BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38}};if(d){var f=c.attr,e=c.fn.removeAttr,h="http://www.w3.org/2005/07/aaa",a=/^aria-/,b=/^wairole:/;c.attr=function(k,j,l){var m=l!==undefined;return(j=="role"?(m?f.call(this,k,j,"wairole:"+l):(f.apply(this,arguments)||"").replace(b,"")):(a.test(j)?(m?k.setAttributeNS(h,j.replace(a,"aaa:"),l):f.call(this,k,j.replace(a,"aaa:"))):f.apply(this,arguments)))};c.fn.removeAttr=function(j){return(a.test(j)?this.each(function(){this.removeAttributeNS(h,j.replace(a,""))}):e.call(this,j))}}c.fn.extend({remove:function(){c("*",this).add(this).each(function(){c(this).triggerHandler("remove")});return i.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable","off").css("MozUserSelect","").unbind("selectstart.ui")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return false})},scrollParent:function(){var j;if((c.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){j=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(c.curCSS(this,"position",1))&&(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}else{j=this.parents().filter(function(){return(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!j.length?c(document):j}});c.extend(c.expr[":"],{data:function(l,k,j){return !!c.data(l,j[3])},focusable:function(k){var l=k.nodeName.toLowerCase(),j=c.attr(k,"tabindex");return(/input|select|textarea|button|object/.test(l)?!k.disabled:"a"==l||"area"==l?k.href||!isNaN(j):!isNaN(j))&&!c(k)["area"==l?"parents":"closest"](":hidden").length},tabbable:function(k){var j=c.attr(k,"tabindex");return(isNaN(j)||j>=0)&&c(k).is(":focusable")}});function g(m,n,o,l){function k(q){var p=c[m][n][q]||[];return(typeof p=="string"?p.split(/,?\s+/):p)}var j=k("getter");if(l.length==1&&typeof l[0]=="string"){j=j.concat(k("getterSetter"))}return(c.inArray(o,j)!=-1)}c.widget=function(k,j){var l=k.split(".")[0];k=k.split(".")[1];c.fn[k]=function(p){var n=(typeof p=="string"),o=Array.prototype.slice.call(arguments,1);if(n&&p.substring(0,1)=="_"){return this}if(n&&g(l,k,p,o)){var m=c.data(this[0],k);return(m?m[p].apply(m,o):undefined)}return this.each(function(){var q=c.data(this,k);(!q&&!n&&c.data(this,k,new c[l][k](this,p))._init());(q&&n&&c.isFunction(q[p])&&q[p].apply(q,o))})};c[l]=c[l]||{};c[l][k]=function(o,n){var m=this;this.namespace=l;this.widgetName=k;this.widgetEventPrefix=c[l][k].eventPrefix||k;this.widgetBaseClass=l+"-"+k;this.options=c.extend({},c.widget.defaults,c[l][k].defaults,c.metadata&&c.metadata.get(o)[k],n);this.element=c(o).bind("setData."+k,function(q,p,r){if(q.target==o){return m._setData(p,r)}}).bind("getData."+k,function(q,p){if(q.target==o){return m._getData(p)}}).bind("remove",function(){return m.destroy()})};c[l][k].prototype=c.extend({},c.widget.prototype,j);c[l][k].getterSetter="option"};c.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName).removeClass(this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").removeAttr("aria-disabled")},option:function(l,m){var k=l,j=this;if(typeof l=="string"){if(m===undefined){return this._getData(l)}k={};k[l]=m}c.each(k,function(n,o){j._setData(n,o)})},_getData:function(j){return this.options[j]},_setData:function(j,k){this.options[j]=k;if(j=="disabled"){this.element[k?"addClass":"removeClass"](this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").attr("aria-disabled",k)}},enable:function(){this._setData("disabled",false)},disable:function(){this._setData("disabled",true)},_trigger:function(l,m,n){var p=this.options[l],j=(l==this.widgetEventPrefix?l:this.widgetEventPrefix+l);m=c.Event(m);m.type=j;if(m.originalEvent){for(var k=c.event.props.length,o;k;){o=c.event.props[--k];m[o]=m.originalEvent[o]}}this.element.trigger(m,n);return !(c.isFunction(p)&&p.call(this.element[0],m,n)===false||m.isDefaultPrevented())}};c.widget.defaults={disabled:false};c.ui.mouse={_mouseInit:function(){var j=this;this.element.bind("mousedown."+this.widgetName,function(k){return j._mouseDown(k)}).bind("click."+this.widgetName,function(k){if(j._preventClickEvent){j._preventClickEvent=false;return false}});if(c.browser.msie){this._mouseUnselectable=this.element.attr("unselectable");this.element.attr("unselectable","on")}this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName);(c.browser.msie&&this.element.attr("unselectable",this._mouseUnselectable))},_mouseDown:function(l){if(l.originalEvent.mouseHandled){return}(this._mouseStarted&&this._mouseUp(l));this._mouseDownEvent=l;var k=this,m=(l.which==1),j=(typeof this.options.cancel=="string"?c(l.target).parents().add(l.target).filter(this.options.cancel).length:false);if(!m||j||!this._mouseCapture(l)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){k.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(l)&&this._mouseDelayMet(l)){this._mouseStarted=(this._mouseStart(l)!==false);if(!this._mouseStarted){l.preventDefault();return true}}this._mouseMoveDelegate=function(n){return k._mouseMove(n)};this._mouseUpDelegate=function(n){return k._mouseUp(n)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);(c.browser.safari||l.preventDefault());l.originalEvent.mouseHandled=true;return true},_mouseMove:function(j){if(c.browser.msie&&!j.button){return this._mouseUp(j)}if(this._mouseStarted){this._mouseDrag(j);return j.preventDefault()}if(this._mouseDistanceMet(j)&&this._mouseDelayMet(j)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,j)!==false);(this._mouseStarted?this._mouseDrag(j):this._mouseUp(j))}return !this._mouseStarted},_mouseUp:function(j){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=true;this._mouseStop(j)}return false},_mouseDistanceMet:function(j){return(Math.max(Math.abs(this._mouseDownEvent.pageX-j.pageX),Math.abs(this._mouseDownEvent.pageY-j.pageY))>=this.options.distance)},_mouseDelayMet:function(j){return this.mouseDelayMet},_mouseStart:function(j){},_mouseDrag:function(j){},_mouseStop:function(j){},_mouseCapture:function(j){return true}};c.ui.mouse.defaults={cancel:null,distance:1,delay:0}})(jQuery);;/*
+ * jQuery UI Slider 1.6rc6
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ *	ui.core.js
+ */
+(function(a){a.widget("ui.slider",a.extend({},a.ui.mouse,{_init:function(){var b=this,c=this.options;this._keySliding=false;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget ui-widget-content ui-corner-all");this.range=a([]);if(c.range){if(c.range===true){this.range=a("<div></div>");if(!c.values){c.values=[this._valueMin(),this._valueMin()]}if(c.values.length&&c.values.length!=2){c.values=[c.values[0],c.values[0]]}}else{this.range=a("<div></div>")}this.range.appendTo(this.element).addClass("ui-slider-range ui-widget-header");(c.range=="min")&&(this.orientation=="horizontal")&&this.range.css({left:0});(c.range=="max")&&(this.orientation=="horizontal")&&this.range.css({right:0});(c.range=="min")&&(this.orientation=="vertical")&&this.range.css({bottom:0});(c.range=="max")&&(this.orientation=="vertical")&&this.range.css({top:0})}if(a(".ui-slider-handle",this.element).length==0){a('<a href="#"></a>').appendTo(this.element).addClass("ui-slider-handle")}if(c.values&&c.values.length){while(a(".ui-slider-handle",this.element).length<c.values.length){a('<a href="#"></a>').appendTo(this.element).addClass("ui-slider-handle")}}this.handles=a(".ui-slider-handle",this.element).addClass("ui-state-default ui-corner-all");this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(d){d.preventDefault()}).hover(function(){a(this).addClass("ui-state-hover")},function(){a(this).removeClass("ui-state-hover")}).focus(function(){b.handles.removeClass("ui-state-focus");a(this).addClass("ui-state-focus")}).blur(function(){a(this).removeClass("ui-state-focus")});this.handles.each(function(d){a(this).data("index.ui-slider-handle",d)});this.handles.keydown(function(h){var e=a(this).data("index.ui-slider-handle");if(b.options.disabled){return}switch(h.keyCode){case a.ui.keyCode.HOME:case a.ui.keyCode.END:case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:if(!b._keySliding){b._keySliding=true;a(this).addClass("ui-state-active");b._start(h)}break}var f,d,g=b._step();if(b.options.values&&b.options.values.length){f=d=b.values(e)}else{f=d=b.value()}switch(h.keyCode){case a.ui.keyCode.HOME:d=b._valueMin();break;case a.ui.keyCode.END:d=b._valueMax();break;case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:if(f==b._valueMax()){return}d=f+g;break;case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:if(f==b._valueMin()){return}d=f-g;break}b._slide(h,e,d)}).keyup(function(d){if(b._keySliding){b._stop(d);b._change(d);b._keySliding=false;a(this).removeClass("ui-state-active")}});this._refreshValue()},destroy:function(){this.handles.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy()},_mouseCapture:function(d){var e=this.options;if(e.disabled){return false}this._start(d);this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();var h={x:d.pageX,y:d.pageY};var j=this._normValueFromMouse(h);var c=this._valueMax()+1,f;var k=this,i;this.handles.each(function(l){var m=Math.abs(j-k.values(l));if(c>m){c=m;f=a(this);i=l}});if(e.range&&(this.values(0)+this.values(1))==0){f=a(this.handles[++i])}k._handleIndex=i;f.addClass("ui-state-active").focus();var g=f.offset();var b=!a(d.target).parents().andSelf().is(".ui-slider-handle");this._clickOffset=b?{left:0,top:0}:{left:d.pageX-g.left+(parseInt(f.css("marginLeft"),10)||0),top:d.pageY-g.top+(parseInt(f.css("marginTop"),10)||0)};j=this._normValueFromMouse(h);this._slide(d,i,j);return true},_mouseStart:function(b){return true},_mouseDrag:function(d){var b={x:d.pageX,y:d.pageY};var c=this._normValueFromMouse(b);this._slide(d,this._handleIndex,c);return false},_mouseStop:function(b){this.handles.removeClass("ui-state-active");this._stop(b);this._change(b);this._handleIndex=null;this._clickOffset=null;return false},_detectOrientation:function(){this.orientation=this.options.orientation=="auto"?(this.element[0].offsetWidth/this.element[0].offsetHeight>1?"horizontal":"vertical"):this.options.orientation},_normValueFromMouse:function(d){var c,h;if("horizontal"==this.orientation){c=this.elementSize.width;h=d.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{c=this.elementSize.height;h=d.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}var f=(h/c);if(f>1){f=1}if(f<0){f=0}if("vertical"==this.orientation){f=1-f}var e=this._valueMax()-this._valueMin(),i=f*e,b=i%this.options.step,g=this._valueMin()+i-b;if(b>(this.options.step/2)){g+=this.options.step}return g},_start:function(b){this._trigger("start",b,{value:this.value()})},_slide:function(f,e,d){var g=this.handles[e];if(this.options.values&&this.options.values.length){var b=this.values(e?0:1);if((e==0&&d>=b)||(e==1&&d<=b)){d=b}if(d!=this.values(e)){var c=this.values();c[e]=d;var h=this._trigger("slide",f,{handle:g,value:d,values:c});var b=this.values(e?0:1);if(h!==false){this.values(e,d,!(f.type=="mousedown"&&this.options.animate))}}}else{if(d!=this.value()){var h=this._trigger("slide",f,{handle:g,value:d});if(h!==false){this._setData("value",d,(f.type=="mousedown"&&this.options.animate))}}}},_stop:function(b){this._trigger("stop",b,{value:this.value()})},_change:function(b){this._trigger("change",b,{value:this.value()})},value:function(b){if(arguments.length){this._setData("value",b);this._change()}return this._value()},values:function(c,d,b){if(!this.options.animate){b=true}if(arguments.length>1){this.options.values[c]=d;this._refreshValue(!b);this._change()}if(arguments.length){if(this.options.values&&this.options.values.length){return this._values(c)}else{return this.value()}}else{return this._values()}},_setData:function(b,c){a.widget.prototype._setData.apply(this,arguments);switch(b){case"orientation":this._detectOrientation();this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation);this._refreshValue();break;case"value":this._refreshValue();break}},_step:function(){var b=this.options.step;return b},_value:function(){var b=this.options.value;if(b<this._valueMin()){b=this._valueMin()}if(b>this._valueMax()){b=this._valueMax()}return b},_values:function(b){if(arguments.length){var c=this.options.values[b];if(c<this._valueMin()){c=this._valueMin()}if(c>this._valueMax()){c=this._valueMax()}return c}else{return this.options.values}},_valueMin:function(){var b=this.options.min;return b},_valueMax:function(){var b=this.options.max;return b},_refreshValue:function(e){var h=this.options.range,g=this.options,d=this;if(this.options.values&&this.options.values.length){var c,i;this.handles.each(function(n,l){var m=(d.values(n)-d._valueMin())/(d._valueMax()-d._valueMin())*100;var k={};k[d.orientation=="horizontal"?"left":"bottom"]=m+"%";a(this).stop(1,1)[e?"animate":"css"](k,g.animate);if(d.options.range===true){if(d.orientation=="horizontal"){(n==0)&&d.range.stop(1,1)[e?"animate":"css"]({left:m+"%"},g.animate);(n==1)&&d.range[e?"animate":"css"]({width:(m-lastValPercent)+"%"},{queue:false,duration:g.animate})}else{(n==0)&&d.range.stop(1,1)[e?"animate":"css"]({bottom:(m)+"%"},g.animate);(n==1)&&d.range[e?"animate":"css"]({height:(m-lastValPercent)+"%"},{queue:false,duration:g.animate})}}lastValPercent=m})}else{var f=(this.value()-this._valueMin())/(this._valueMax()-this._valueMin())*100;var b={};b[d.orientation=="horizontal"?"left":"bottom"]=f+"%";this.handle.stop(1,1)[e?"animate":"css"](b,g.animate);(h=="min")&&(this.orientation=="horizontal")&&this.range.stop(1,1)[e?"animate":"css"]({left:0,width:f+"%"},g.animate);(h=="max")&&(this.orientation=="horizontal")&&this.range[e?"animate":"css"]({left:f+"%",width:(100-f)+"%"},{queue:false,duration:g.animate});(h=="min")&&(this.orientation=="vertical")&&this.range.stop(1,1)[e?"animate":"css"]({top:(100-f)+"%",height:f+"%"},g.animate);(h=="max")&&(this.orientation=="vertical")&&this.range[e?"animate":"css"]({bottom:f+"%",height:(100-f)+"%"},{queue:false,duration:g.animate})}}}));a.extend(a.ui.slider,{getter:"value values",version:"1.6rc6",eventPrefix:"slide",defaults:{animate:false,delay:0,distance:0,max:100,min:0,orientation:"auto",range:false,step:1,value:0,values:null}})})(jQuery);;
Index: gutenbach-web/old/master.html
===================================================================
--- gutenbach-web/old/master.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/master.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      py:strip="">
+    <xi:include href="header.html" />
+    <xi:include href="footer.html" />
+<head py:match="head" py:attrs="select('@*')">
+    <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+    <title py:replace="''">Your title goes here</title>
+    <meta py:replace="select('*')"/>
+    <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/jquery/theme/ui.all.css')}" />
+    <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/style.css')}" />
+</head>
+
+<body py:match="body" py:attrs="select('@*')">
+  <script type="text/javascript">
+      document.body.className += ' has_js';
+  </script>
+  <div class="wrapper">
+  ${header()}
+  <ul id="mainmenu">
+    <li class="first"><a href="${tg.url('/')}" class="${('', 'active')[defined('page') and page==page=='index']}">Status</a></li>
+        <li><a href="${tg.url('/about')}" class="${('', 'active')[defined('page') and page==page=='about']}">About</a></li>
+        <li><a href="${tg.url('/todo')}" class="${('', 'active')[defined('page') and page==page=='todo']}">Todo</a></li>
+        <li><a href="mailto:sipb@mit.edu">Contact</a></li>
+  </ul>
+  <div id="content">
+    <py:with vars="flash=tg.flash_obj.render('flash', use_js=False)">
+        <div py:if="flash" py:content="XML(flash)" />
+    </py:with>
+    <div py:replace="select('*|text()')"/>
+    <!-- End of main_content -->
+  </div>
+    <div class="push"></div>
+</div>
+  ${footer()}
+</body>
+</html>
Index: gutenbach-web/old/middleware.py
===================================================================
--- gutenbach-web/old/middleware.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/middleware.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,49 @@
+"""TurboGears middleware initialization"""
+from sipbmp3web.config.app_cfg import base_config
+from sipbmp3web.config.environment import load_environment
+import subprocess, os
+from pylons import config
+
+#Use base_config to setup the necessary WSGI App factory. 
+#make_base_app will wrap the TG2 app with all the middleware it needs. 
+make_base_app = base_config.setup_tg_wsgi_app(load_environment)
+
+class FastCGIFixMiddleware(object):
+    """Remove dispatch.fcgi from the SCRIPT_NAME
+    
+    mod_rewrite doesn't do a perfect job of hiding it's actions to the
+    underlying script, which causes TurboGears to get confused and tack
+    on dispatch.fcgi when it really shouldn't. This fixes that problem as a
+    Middleware that fiddles with the appropriate environment variable
+    before any processing takes place.
+    """
+    def __init__(self, app, global_conf=None):
+        self.app = app
+    def __call__(self, environ, start_response):
+        environ['SCRIPT_NAME'] = environ['SCRIPT_NAME'].replace('/dispatch.fcgi', '')
+        return self.app(environ, start_response)
+
+class KinitMiddleware(object):
+    """Performs Kerberos authentication with a keytab"""
+    def __init__(self, app, global_conf=None):
+        self.app = app
+        try:
+            self.keytab = config["keytab"]
+            self.krbname = config["krbname"]
+        except KeyError:
+            self.keytab = None
+    def __call__(self, environ, start_response):
+        if self.keytab:
+            try:
+                subprocess.call(["kinit", self.krbname, "-k", "-t", self.keytab])
+            except OSError:
+                subprocess.call(["/usr/kerberos/bin/kinit", self.krbname, "-k", "-t", self.keytab])
+        return self.app(environ, start_response)
+
+def make_app(global_conf, full_stack=True, **app_conf):
+    app = make_base_app(global_conf, full_stack=True, **app_conf)
+    app = FastCGIFixMiddleware(app, global_conf)
+    app = KinitMiddleware(app, global_conf)
+    return app
+
+
Index: gutenbach-web/old/model.template
===================================================================
--- gutenbach-web/old/model.template	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/model.template	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,14 @@
+from sqlalchemy import *
+from sqlalchemy.orm import mapper, relation
+from sqlalchemy import Table, ForeignKey, Column
+from sqlalchemy.types import Integer, Unicode
+#from sqlalchemy.orm import relation, backref
+
+from sipbmp3web.model import DeclarativeBase, metadata, DBSession
+
+class SampleModel(DeclarativeBase):
+    __tablename__ = 'sample_model'
+
+    id = Column(Integer, primary_key=True)
+    data = Column(Unicode(255), nullable=False)
+
Index: gutenbach-web/old/paster_plugins.txt
===================================================================
--- gutenbach-web/old/paster_plugins.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/paster_plugins.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,4 @@
+PasteScript
+Pylons
+TurboGears2
+tg.devtools
Index: gutenbach-web/old/production.ini.sample
===================================================================
--- gutenbach-web/old/production.ini.sample	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/production.ini.sample	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,9 @@
+[app:main]
+smtp_server = localhost
+email_to = edwardzyang@thewritingpot.com
+error_email_from = paste@ezyang.scripts.mit.edu
+
+use = egg:sipbmp3-web
+sipbmp3.server = zygorthian-space-raiders.mit.edu
+keytab = /mit/ezyang/web_scripts/ezyang.extra.keytab
+sqlalchemy.url = sqlite:///%(here)s/devdata.db
Index: gutenbach-web/old/requires.txt
===================================================================
--- gutenbach-web/old/requires.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/requires.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,4 @@
+TurboGears2
+ToscaWidgets >= 0.9.1
+zope.sqlalchemy
+repoze.what-quickstart
Index: gutenbach-web/old/root.py
===================================================================
--- gutenbach-web/old/root.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/root.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,54 @@
+"""Main Controller"""
+from sipbmp3web.lib.base import BaseController
+from tg import expose, flash, require, url, request, redirect, validate
+from pylons.i18n import ugettext as _
+from pylons import config
+#from tg import redirect, validate
+from sipbmp3web.model import DBSession, metadata
+from sipbmp3web.controllers.error import ErrorController
+from sipbmp3web import model
+from repoze.what import predicates
+from sipbmp3web.controllers.secure import SecureController
+from remctl import remctl
+import tw.forms as twf
+from sipbmp3web.widgets.slider import UISlider
+
+volume_form = twf.TableForm('volume_form', action='volume', children=[
+    UISlider('volume', min=1, max=31, validator=twf.validators.NotEmpty())
+])
+
+class RootController(BaseController):
+    error = ErrorController()
+
+    @expose('sipbmp3web.templates.index')
+    def index(self, **kw):
+        server = config['sipbmp3.server']
+        out = dict(page="index")
+        volume = int(remctl(server, command=["volume", "get"]).stdout.rstrip())
+        playing = remctl(server, command=["status", "get"]).stdout
+        # Todo: add better parsing
+        if not playing: playing = "Nothing playing"
+        if not "volume" in kw: kw["volume"] = volume
+        return dict(
+                    page="index",
+                    playing=playing,
+                    volume=volume,
+                    volume_form=volume_form,
+                    volume_data=kw,
+                )
+
+    @validate(form=volume_form, error_handler=index)
+    @expose()
+    def volume(self, **kw):
+        server = config['sipbmp3.server']
+        remctl(server, command=["volume", "set", kw["volume"]])
+        redirect('index')
+
+    @expose('sipbmp3web.templates.about')
+    def about(self):
+        return dict(page="about")
+
+    @expose('sipbmp3web.templates.todo')
+    def todo(self):
+        return dict(page="todo")
+
Index: gutenbach-web/old/sample.css
===================================================================
--- gutenbach-web/old/sample.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/sample.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,307 @@
+html {
+	background: #555555 url('../images/pagebg.png') top left repeat-x;
+	text-align: center;
+	margin: 0;
+	padding: 0;
+}
+
+body {
+	text-align: left;
+	width: 960px;
+	margin: 0 auto;
+	padding: 0;
+	font-size: 12px;
+	font-family:"Lucida Grande","Lucida Sans Unicode",geneva,verdana,sans-serif;
+}
+
+a {
+	color: #286571;
+}
+
+#header {
+	height: 132px;
+	margin: 10px 10px 0 10px;
+	background: url('../images/headerbg.png') top left no-repeat;
+}
+
+#header h1 {
+	padding: 0;
+	margin: 0;
+	padding-top: 30px;
+	padding-left: 180px;
+	color: #fff;
+	position: relative;
+	font-size: 36px;
+}
+
+#header h1 .subtitle {
+	font-size: 60%;
+	position: absolute;
+	left: 240px;
+	top: 70px;
+}
+
+ul#mainmenu {
+	margin: 0;
+	padding: 0 10px;
+	background: url('../images/menubg.png') top left no-repeat;
+	height: 38px;
+}
+
+ul#mainmenu li {
+	list-style-type: none;
+	margin: 0;
+	padding: 0;
+	position: relative;
+	display: inline;
+	float: left;
+}
+
+ul#mainmenu li a {
+	color: #fff;
+	float: left;
+	height: 31px;
+	display: block;
+	line-height: 30px;
+	vertical-align: middle;
+	padding: 0 10px;
+	font-size: 12px;
+	text-decoration: none;
+	background: url('../images/menu-item-border.png') left top no-repeat;
+}
+
+ul#mainmenu li a:hover, ul#mainmenu li a.active {
+	background: url('../images/menu-item-actibg.png') left top no-repeat;
+}
+
+ul#mainmenu li.first a {
+	background: none;
+}
+
+ul#mainmenu li.first a:hover, ul#mainmenu li.first a.active {
+	background: url('../images/menu-item-actibg-first.png') left top no-repeat;
+}
+
+ul#mainmenu li.loginlogout
+{
+    float: right;
+    right: 10px;
+}
+
+ul#mainmenu li.loginlogout a:hover
+{
+    background: url('../images/menu-item-border.png') left top no-repeat;
+}
+
+#content {
+	background: #fff url('../images/contentbg.png') left bottom no-repeat;
+	margin : 0 10px 10px 10px;
+	padding: 0 10px;
+}
+
+#content .currentpage {
+	margin-top: 0;
+}
+
+/* Login form*/
+
+#loginform
+{
+    text-align: center;
+}
+
+form.loginfields
+{
+    text-align: left;
+    width: 270px;
+    background: url('../images/loginbg.png') top left no-repeat;
+    padding: 0;
+    margin: 0 auto;
+    border: 0;
+}
+
+form.loginfields h2
+{
+    font-size: 16px;
+    float: left;
+    padding: 0;
+    margin: 5px;
+    margin-top: 0;
+    background: url('../images/loginheader-left.png') top left no-repeat;
+}
+
+* html form.loginfields h2
+{
+    margin-left: 3px;
+    width: 80px;
+    text-align: center;
+}
+
+form.loginfields h2 span
+{
+    background: url('../images/loginheader-right.png') right top no-repeat;
+    height: 30px;
+    line-height: 30px;
+    display: block;
+    font-size: 100%;
+    font-weight: normal;
+    color: #fff;
+    padding: 0 10px;
+}
+
+*+html form.loginfields h2 span
+{
+    padding: 0 20px;
+}
+
+form.loginfields label
+{
+    clear: left;
+    float: left;
+    margin-top: 5px;
+    margin-left: 10px;
+    width: 65px;
+    line-height: 31px;
+    vertical-align: middle;
+}
+
+form.loginfields input.text
+{
+    float: left;
+    margin-left: 10px;
+    margin-top: 5px;
+    width: 165px;
+    height: 21px;
+    padding: 5px;
+    border: 0;
+    background: url('../images/inputbg.png') top left no-repeat;
+    color: #fff;
+}
+
+form.loginfields input#submit
+{
+    background: url('../images/loginbottombg.png') bottom left no-repeat;
+    width: 270px;
+    height: 61px;
+    border: 0;
+    margin-top: 10px;
+    color: #fff;
+    padding: 10px 140px 20px 10px;
+}
+
+* html form.loginfields input#submit
+{
+    clear: both;
+    margin-left: -10px;
+}
+
+/* Sidebar */
+.sidebar {
+  border: 1px solid #cce;
+  background-color: #eee;
+  margin: 0.5em;
+  margin-left: 1.5em;
+  padding: 1em;
+  float: right;
+  width: 200px;
+  font-size: 88%;
+  background-color: #fffe1;
+  background-repeat:repeat-x;
+}
+
+.sidebar h2 {
+  margin-top: 0;
+  color: black;
+}
+
+.sidebar ul {
+  margin-left: 1.5em;
+  padding-left: 0;
+}
+
+
+
+#sb_top {
+  clear: right;
+}
+
+#sb_bottom {
+  clear: right;
+}
+
+#getting_started {
+  margin-left: 20px;
+}
+
+#getting_started_steps a {
+  text-decoration: none;
+}
+
+#getting_started_steps a:hover {
+  text-decoration: underline;
+}
+
+#getting_started_steps li {
+  margin-bottom: 0.5em;
+}
+
+/* Other and footer */
+#footer {
+	background: #fff;
+	padding: 10px;
+	color:#888888;
+	font-size:90%;
+}
+.flogo {
+	float:left;
+	margin-bottom:0px;
+	padding-left:20px;
+	padding-right:20px;
+	padding-top:0px;
+}
+.foottext p {
+	margin: 0; padding: 0;
+}
+.code {
+font-family:monospace;
+font-size:127%;
+}
+span.code {
+background:#EEEEEE none repeat scroll 0% 0%;
+font-weight:bold;
+}
+#flash {
+font-size:120%;
+font-weight:bolder;
+margin:0pt auto 0.5em;
+width:680px;
+}
+#flash div {
+padding:5px 15px 15px 55px;
+}
+#flash .ok {
+background:#EEEEFF url(../images/ok.png) no-repeat scroll left center;
+border:1px solid #CCCCEE;
+}
+#flash .warning {
+background:#FF9999 url(../images/error.png) no-repeat scroll left center;
+border:1px solid #FF3333;
+}
+#flash .alert {
+background:#FFFF99 url(../images/info.png) no-repeat scroll left center;
+border:1px solid #FFFF00;
+}
+.notice {
+background:#EEEEFF url(../images/info.png) no-repeat scroll left center;
+border:1px solid #CCCCEE;
+margin:0.5em auto;
+padding:15px 10px 15px 55px;
+width:680px;
+}
+.fielderror {
+color:red;
+font-weight:bold;
+}
+div.clearingdiv {
+clear:both;
+}
Index: gutenbach-web/old/secure.py
===================================================================
--- gutenbach-web/old/secure.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/secure.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,29 @@
+"""Test Secure Controller"""
+from sipbmp3web.lib.base import BaseController
+from tg import expose, flash
+from pylons.i18n import ugettext as _
+#from tg import redirect, validate
+#from sipbmp3web.model import DBSession, metadata
+#from dbsprockets.dbmechanic.frameworks.tg2 import DBMechanic
+#from dbsprockets.saprovider import SAProvider
+from repoze.what.predicates import has_permission
+
+
+class SecureController(BaseController):
+    """Sample controller-wide authorization"""
+
+    # The predicate that must be met for all the actions in this controller:
+    allow_only = has_permission('manage',
+                                msg=_('Only for people with the "manage" permission'))
+
+    @expose('sipbmp3web.templates.index')
+    def index(self):
+        flash(_("Secure Controller here"))
+        return dict(page='index')
+
+    @expose('sipbmp3web.templates.index')
+    def some_where(self):
+        """should be protected because of the require attr
+        at the controller level.
+        """
+        return dict(page='some_where')
Index: gutenbach-web/old/setup.cfg
===================================================================
--- gutenbach-web/old/setup.cfg	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/setup.cfg	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,31 @@
+[egg_info]
+tag_build = dev
+tag_svn_revision = true
+
+[easy_install]
+find_links = http://www.pylonshq.com/download/
+
+[nosetests]
+with-pylons=test.ini
+
+# Babel configuration
+[compile_catalog]
+domain = sipbmp3web
+directory = sipbmp3web/i18n
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = sipbmp3web/i18n/sipbmp3web.pot
+width = 80
+
+[init_catalog]
+domain = sipbmp3web
+input_file = sipbmp3web/i18n/sipbmp3web.pot
+output_dir = sipbmp3web/i18n
+
+[update_catalog]
+domain = sipbmp3web
+input_file = sipbmp3web/i18n/sipbmp3web.pot
+output_dir = sipbmp3web/i18n
+previous = true
Index: gutenbach-web/old/setup.py
===================================================================
--- gutenbach-web/old/setup.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/setup.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,44 @@
+try:
+    from setuptools import setup, find_packages
+except ImportError:
+    from ez_setup import use_setuptools
+    use_setuptools()
+    from setuptools import setup, find_packages
+
+setup(
+    name='sipbmp3-web',
+    version='0.1',
+    description='',
+    author='',
+    author_email='',
+    #url='',
+    install_requires=[
+        "TurboGears2",
+        "ToscaWidgets >= 0.9.1",
+        "zope.sqlalchemy",
+        
+        "repoze.what-quickstart",
+                ],
+    setup_requires=["PasteScript>=1.6.3"],
+    paster_plugins=['PasteScript', 'Pylons', 'TurboGears2', 'tg.devtools'],
+    packages=find_packages(exclude=['ez_setup']),
+    include_package_data=True,
+    test_suite='nose.collector',
+    tests_require=['WebTest', 'BeautifulSoup'],
+    package_data={'sipbmp3web': ['i18n/*/LC_MESSAGES/*.mo',
+                                 'templates/*/*',
+                                 'public/*/*']},
+    message_extractors = {'sipbmp3web': [
+            ('**.py', 'python', None),
+            ('templates/**.mako', 'mako', None),
+            ('templates/**.html', 'genshi', None),
+            ('public/**', 'ignore', None)]},
+
+    entry_points="""
+    [paste.app_factory]
+    main = sipbmp3web.config.middleware:make_app
+
+    [paste.app_install]
+    main = pylons.util:PylonsInstaller
+    """,
+)
Index: gutenbach-web/old/sipbmp3web.po
===================================================================
--- gutenbach-web/old/sipbmp3web.po	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/sipbmp3web.po	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,24 @@
+# Russian translations for ${package}.
+# Copyright (C) 2008 ORGANIZATION
+# This file is distributed under the same license as the ${package} project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ${package} 0.0.0\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2008-01-13 14:00+0200\n"
+"PO-Revision-Date: 2008-01-13 14:00+0200\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: ru <LL@li.org>\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.1\n"
+
+#: ${package}/controllers/root.py:13
+msgid "Your application is now running"
+msgstr "Ваши приложение успешно запущено"
+
Index: gutenbach-web/old/slider.html
===================================================================
--- gutenbach-web/old/slider.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/slider.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,7 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+    xmlns:py="http://genshi.edgewall.org/"
+    id="${id}_container"
+    class="${css_class}"
+    py:attrs="attrs">
+    <input type='text' id="${id}" name="${name}" value="${value}" />
+</div>
Index: gutenbach-web/old/slider.py
===================================================================
--- gutenbach-web/old/slider.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/slider.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,36 @@
+from tw.api import Widget, JSLink, CSSLink, js_function, js_callback, js_symbol
+from tw.forms.fields import TextField
+from tw.forms.validators import Pipe, Int
+
+class UISlider(TextField):
+    javascript = [
+        JSLink(modname="sipbmp3web",filename="public/jquery/jquery-1.3.2.js"),
+        JSLink(modname="sipbmp3web",filename="public/jquery/jquery-ui-personalized-1.6rc6.js"),
+    ]
+    template = "genshi:sipbmp3web.widgets.templates.slider"
+    def __init__(self, *args, **kw):
+        self.min = kw.pop("min")
+        self.max = kw.pop("max")
+        validator = Int(min=self.min, max=self.max)
+        try:
+            validator = Pipe(kw["validator"], validator)
+        except KeyError:
+            pass
+        kw["validator"] = validator
+        super(UISlider, self).__init__(*args, **kw)
+    def update_params(self, d):
+        super(TextField, self).update_params(d)
+        if not getattr(d, "id", None):
+            raise ValueError, "Slider must have id"
+        container = '%s_container' % d.id
+        self.add_call(js_callback('$("#%s").slider({ \
+            min: %d, max: %d, step: 1, range: false, \
+            value: $("#%s").val(), \
+            slide: function (event, ui) { \
+                jQuery("#%s").val(ui.value);\
+            } \
+            })' % (container, self.min, self.max, d.id, d.id)))
+        jQuery = js_function('jQuery')
+        input = jQuery("#%s" % d.id)
+        self.add_call(input.css("display", "none"))
+
Index: gutenbach-web/old/style.css
===================================================================
--- gutenbach-web/old/style.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/style.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,108 @@
+html {
+    margin: 0;
+    height: 100%;
+}
+body {
+    font-size: 87.5%;
+    line-height: 1.5em;
+    font-family: Tahoma, Sans, sans-serif;
+    height: 100%;
+    margin: 0;
+}
+#header h1 {
+    margin-top: 0;
+    padding-top: 2em;
+    margin-bottom: 0.3em;
+}
+#header h1 .web {
+    color: #0BF;
+}
+#header .subtitle {
+    margin-bottom: 1em;
+    margin-top: 0;
+    margin-left: 9em;
+}
+ul#mainmenu {
+    margin: 0 0 2em 0;
+    padding: 0;
+}
+ul#mainmenu li {
+    list-style-type: none;
+    display: inline;
+    margin: 0 0.6em;
+    padding: 0 0.3em;
+}
+ul#mainmenu li.first {
+    margin-left: 0;
+}
+ul#mainmenu li .active {
+    color: #000;
+    font-weight: bold;
+    text-decoration: none;
+}
+.wrapper {
+    min-height: 100%;
+    margin: 0 auto -3em;
+    padding: 0 3em;
+    max-width: 50em;
+}
+.push {
+    height: 3em;
+}
+#footer {
+    height: 3em;
+    text-align: right;
+    padding: 0 3em;
+}
+#flash {
+    font-size:120%;
+    font-weight:bolder;
+    margin:0pt auto 0.5em;
+    width:680px;
+}
+#flash div {
+    padding:5px 15px 15px 55px;
+}
+#flash .ok {
+    background:#EEEEFF url(../images/ok.png) no-repeat scroll left center;
+    border:1px solid #CCCCEE;
+}
+#flash .warning {
+    background:#FF9999 url(../images/error.png) no-repeat scroll left center;
+    border:1px solid #FF3333;
+}
+#flash .alert {
+    background:#FFFF99 url(../images/info.png) no-repeat scroll left center;
+    border:1px solid #FFFF00;
+}
+.notice {
+    background:#EEEEFF url(../images/info.png) no-repeat scroll left center;
+    border:1px solid #CCCCEE;
+    margin:0.5em auto;
+    padding:15px 10px 15px 55px;
+    width:680px;
+}
+.fielderror {
+    color:red;
+    font-weight:bold;
+}
+div.clearingdiv {
+    clear: both;
+}
+
+/* Content CSS */
+#volume {
+    color: #009;
+    font-family: Helvetica;
+    font-weight: bold;
+    font-size: 1.1em;
+}
+.labelcol {
+    padding-right: 2em;
+}
+#volume_form_volume_container {
+    width: 18em;
+}
+.has_js .uislider input {
+    display:none;
+}
Index: gutenbach-web/old/template.py
===================================================================
--- gutenbach-web/old/template.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/template.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,27 @@
+from sipbmp3web.lib.base import *
+
+class TemplateController(BaseController):
+
+    def view(self, url):
+        """By default, the final controller tried to fulfill the request
+        when no other routes match. It may be used to display a template
+        when all else fails, e.g.::
+
+            def view(self, url):
+                return render('/%s' % url)
+
+        Or if you're using Mako and want to explicitly send a 404 (Not
+        Found) response code when the requested template doesn't exist::
+
+            import mako.exceptions
+
+            def view(self, url):
+                try:
+                    return render('/%s' % url)
+                except mako.exceptions.TopLevelLookupException:
+                    abort(404)
+
+        By default this controller aborts the request with a 404 (Not
+        Found)
+        """
+        abort(404)
Index: gutenbach-web/old/test.ini
===================================================================
--- gutenbach-web/old/test.ini	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/test.ini	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,22 @@
+#
+# sipbmp3-web - TurboGears 2 testing environment configuration
+#
+# The %(here)s variable will be replaced with the parent directory of this file
+#
+[DEFAULT]
+debug = true
+# Uncomment and replace with the address which should receive any error reports
+# email_to = you@yourdomain.com
+smtp_server = localhost
+error_email_from = paste@localhost
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 5000
+
+[app:main]
+sqlalchemy.url = sqlite:///:memory:
+use = config:development.ini
+
+# Add additional test specific configuration options as necessary.
Index: gutenbach-web/old/test_models.py
===================================================================
--- gutenbach-web/old/test_models.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/test_models.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+"""Test suite for the TG app's models"""
+
+from tg.testutil import DBTest
+from nose.tools import eq_
+
+from sipbmp3web import model
+
+
+class TestModel(DBTest):
+    """The base class for testing models in you TG project."""
+    model = model
+
+class TestUser(TestModel):
+    """Test case for the User model."""
+    
+    def setUp(self):
+        super(TestUser, self).setUp()
+        self.member = model.User()
+        self.member.user_name = u"ignucius"
+        self.member.email_address = u"ignucius@example.org"
+        
+    def test_member_creation_username(self):
+        """The member constructor must set the user name right"""
+        eq_(self.member.user_name, u"ignucius")
+        
+    def test_member_creation_email(self):
+        """The member constructor must set the email right"""
+        eq_(self.member.email_address, u"ignucius@example.org")
+    
+    def test_no_permissions_by_default(self):
+        """User objects should have no permission by default."""
+        eq_(len(self.member.permissions), 0)
+        
+    def test_getting_by_email(self):
+        """Users should be fetcheable by their email addresses"""
+        model.DBSession.add(self.member)
+        him = model.User.by_email_address(u"ignucius@example.org")
+        eq_(him, self.member)
Index: gutenbach-web/old/test_root.py
===================================================================
--- gutenbach-web/old/test_root.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/test_root.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+
+from sipbmp3web.tests import TestController
+from nose.tools import assert_true
+
+# This is an example of how you can write functional tests for your controller.
+# As opposed to a pure unit-test which test a small unit of functionallity,
+# these functional tests exercise the whole app and it's WSGI stack.
+# Please read http://pythonpaste.org/webtest/ for more information
+
+class TestRootController(TestController):
+    def test_index(self):
+        response = self.app.get('/')
+        msg = 'TurboGears 2 is rapid web application development toolkit '\
+              'designed to make your life easier.'
+        # You can look for specific strings:
+        assert_true(msg in response)
+        # You can also access a BeautifulSoup'ed version
+        # first run $ easy_install BeautifulSoup and then run this test 
+        links = response.html.findAll('a')
+        assert_true(links, "Mummy, there are no links here!")
Index: gutenbach-web/old/todo.html
===================================================================
--- gutenbach-web/old/todo.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/todo.html	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,42 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <xi:include href="master.html" />
+
+<head>
+  <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+  <title>Todo / sipbmp3 web</title>
+</head>
+
+<body>
+    <div>
+        <h2>Fixes</h2>
+        <ul>
+            <li>Create sipbmp3 adapter to hide remctl interface</li>
+            <li>Make server configurable</li>
+            <li>Parse and format song status</li>
+            <li>Reconsider ToscaWidgets JavaScript and CSS setup, especially
+            dealing with minified JavaScript in production, also
+            the ToscaWidgets middleware.</li>
+            <li>Package, as part of the export sipbmp3 project</li>
+            <li>Document concrete dependencies with package versions</li>
+            <li>Certificate authentication, or at the very least htaccess
+            limiting.</li>
+            <li>Contribute improvements upstream</li>
+            <li>Make other pages less sipbmp3@zsr specific</li>
+        </ul>
+        <h2>Features</h2>
+        <ul>
+            <li>Create song model, and keep track of songs</li>
+            <li>Have zsr ping this server when a song is being served</li>
+            <li>Aggregate statistics (who plays/what is played the most?)</li>
+            <li>Comet updates for songs and volume</li>
+            <li>Album art support</li>
+            <li>Interface for submitting URLs</li>
+        </ul>
+    </div>
+</body>
+</html>
Index: gutenbach-web/old/top_level.txt
===================================================================
--- gutenbach-web/old/top_level.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/top_level.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,1 @@
+sipbmp3web
Index: gutenbach-web/old/ui.accordion.css
===================================================================
--- gutenbach-web/old/ui.accordion.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/ui.accordion.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,9 @@
+/* Accordion
+----------------------------------*/
+.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.ui-accordion li {display: inline;}
+.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 2.2em; }
+.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; }
+.ui-accordion .ui-accordion-content-active { display: block; }
Index: gutenbach-web/old/ui.all.css
===================================================================
--- gutenbach-web/old/ui.all.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/ui.all.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,2 @@
+@import "ui.base.css";
+@import "ui.theme.css";
Index: gutenbach-web/old/ui.base.css
===================================================================
--- gutenbach-web/old/ui.base.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/ui.base.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,9 @@
+@import url("ui.core.css");
+
+@import url("ui.accordion.css");
+@import url("ui.datepicker.css");
+@import url("ui.dialog.css");
+@import url("ui.progressbar.css");
+@import url("ui.resizable.css");
+@import url("ui.slider.css");
+@import url("ui.tabs.css");
Index: gutenbach-web/old/ui.core.css
===================================================================
--- gutenbach-web/old/ui.core.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/ui.core.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,37 @@
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+*/
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden { display: none; }
+.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
+.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.ui-helper-clearfix { display:block; }
+/* end clearfix */
+.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
Index: gutenbach-web/old/ui.datepicker.css
===================================================================
--- gutenbach-web/old/ui.datepicker.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/ui.datepicker.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,62 @@
+/* Datepicker
+----------------------------------*/
+.ui-datepicker { width: 17em; padding: .2em .2em 0; }
+.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.ui-datepicker .ui-datepicker-prev { left:2px; }
+.ui-datepicker .ui-datepicker-next { right:2px; }
+.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px;  }
+.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; }
+.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.ui-datepicker select.ui-datepicker-month, 
+.ui-datepicker select.ui-datepicker-year { width: 49%;}
+.ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; }
+.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0;  }
+.ui-datepicker td { border: 0; padding: 1px; }
+.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi { width:auto; }
+.ui-datepicker-multi .ui-datepicker-group { float:left; }
+.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.ui-datepicker-row-break { clear:left; width:100%; }
+
+/* RTL support */
+.ui-datepicker-rtl { direction: rtl; }
+.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.ui-datepicker-cover {
+    display: none; /*sorry for IE5*/
+    display/**/: block; /*sorry for IE5*/
+    position: absolute; /*must have*/
+    z-index: -1; /*must have*/
+    filter: mask(); /*must have*/
+    top: -4px; /*must have*/
+    left: -4px; /*must have*/
+    width: 200px; /*must have*/
+    height: 200px; /*must have*/
+}
Index: gutenbach-web/old/ui.dialog.css
===================================================================
--- gutenbach-web/old/ui.dialog.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/ui.dialog.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,13 @@
+/* Dialog
+----------------------------------*/
+.ui-dialog { position: relative; padding: .2em; width: 300px; }
+.ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative;  }
+.ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; } 
+.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; }
+.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
+.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.ui-draggable .ui-dialog-titlebar { cursor: move; }
Index: gutenbach-web/old/ui.progressbar.css
===================================================================
--- gutenbach-web/old/ui.progressbar.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/ui.progressbar.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,4 @@
+/* Progressbar
+----------------------------------*/
+.ui-progressbar { height:2em; text-align: left; }
+.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
Index: gutenbach-web/old/ui.resizable.css
===================================================================
--- gutenbach-web/old/ui.resizable.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/ui.resizable.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,13 @@
+/* Resizable
+----------------------------------*/
+.ui-resizable { position: relative;}
+.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
+.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; }
+.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; }
+.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; }
+.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; }
+.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}
Index: gutenbach-web/old/ui.slider.css
===================================================================
--- gutenbach-web/old/ui.slider.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/ui.slider.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,17 @@
+/* Slider
+----------------------------------*/
+.ui-slider { position: relative; text-align: left; }
+.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: 1%; display: block; border: 0; }
+
+.ui-slider-horizontal { height: .8em; }
+.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.ui-slider-vertical { width: .8em; height: 100px; }
+.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.ui-slider-vertical .ui-slider-range-max { top: 0; }
Index: gutenbach-web/old/ui.tabs.css
===================================================================
--- gutenbach-web/old/ui.tabs.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/ui.tabs.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,9 @@
+/* Tabs
+----------------------------------*/
+.ui-tabs {padding: .2em;}
+.ui-tabs .ui-tabs-nav { padding: .2em .2em 0 .2em;  position: relative; }
+.ui-tabs .ui-tabs-nav li { float: left; border-bottom: 0 !important; margin: 0 .2em -1px 0; padding: 0; list-style: none; }
+.ui-tabs .ui-tabs-nav li a { display:block; text-decoration: none; padding: .5em 1em; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected {  padding-bottom: .1em; border-bottom: 0; }
+.ui-tabs .ui-tabs-panel { padding: 1em 1.4em;  display: block; border: 0; background: none; }
+.ui-tabs .ui-tabs-hide { display: none !important; }
Index: gutenbach-web/old/ui.theme.css
===================================================================
--- gutenbach-web/old/ui.theme.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/ui.theme.css	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,243 @@
+
+
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+* To view and modify this theme, visit http://ui.jquery.com/themeroller/?tr=&ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
+*/
+
+
+/* Component containers
+----------------------------------*/
+.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; }
+.ui-widget-header a { color: #222222; }
+.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; }
+.ui-widget-content a { color: #222222; }
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default, .ui-widget-content .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; outline: none; }
+.ui-state-default a { color: #555555; text-decoration: none; outline: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; outline: none; }
+.ui-state-hover a { color: #212121; text-decoration: none; outline: none; }
+.ui-state-active, .ui-widget-content .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; outline: none; }
+.ui-state-active a { color: #212121; outline: none; text-decoration: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; }
+.ui-state-error, .ui-widget-content .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text { color: #cd0a0a; }
+.ui-state-disabled, .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+.ui-priority-primary, .ui-widget-content .ui-priority-primary { font-weight: bold; }
+.ui-priority-secondary, .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
+
+/* positioning */
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-off { background-position: -96px -144px; }
+.ui-icon-radio-on { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; }
+.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; }
+.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; }
+.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; }
+.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; }
+.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; }
+.ui-corner-right {  -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; }
+.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; }
+.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; }
+
+/* Overlays */
+.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
+.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; }
Index: gutenbach-web/old/version.txt
===================================================================
--- gutenbach-web/old/version.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/version.txt	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,1 @@
+1.6rc6
Index: gutenbach-web/old/virtualenv-create
===================================================================
--- gutenbach-web/old/virtualenv-create	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/virtualenv-create	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,25 @@
+#!/bin/bash
+easy_install -i http://www.turbogears.org/2.0/downloads/current/index tg.devtools
+# deal with bugs that happen during install
+easy_install tg.devtools
+easy_install tg.devtools
+easy_install flup
+easy_install tw.forms
+
+# Install remctl
+VERSION="2.14"
+wget http://archives.eyrie.org/software/kerberos/remctl-$VERSION.tar.gz
+tar -xf remctl-$VERSION.tar.gz
+rm remctl-$VERSION.tar.gz
+cd remctl-$VERSION
+./configure --enable-python
+make
+cd python
+python setup.py build
+python setup.py install
+cd ../..
+rm -rf remctl-$VERSION
+
+python setup.py develop
+
+paster --help
Index: gutenbach-web/old/virtualenv-init.sh
===================================================================
--- gutenbach-web/old/virtualenv-init.sh	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/virtualenv-init.sh	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,9 @@
+#!/bin/bash
+CURRENT=virtualenv-1.3.2
+wget "http://pypi.python.org/packages/source/v/virtualenv/$CURRENT.tar.gz"
+tar xzf "$CURRENT.tar.gz"
+python "$CURRENT/virtualenv.py" --no-site-packages tg2env
+rm "$CURRENT.tar.gz"
+rm -rf "$CURRENT"
+source tg2env/bin/activate
+source virtualenv-create
Index: gutenbach-web/old/websetup.py
===================================================================
--- gutenbach-web/old/websetup.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
+++ gutenbach-web/old/websetup.py	(revision 27ee337ff8bc3e4965680157d86d64a4ded721e1)
@@ -0,0 +1,52 @@
+"""Setup the sipbmp3-web application"""
+import logging
+
+import transaction
+from tg import config
+
+from sipbmp3web.config.environment import load_environment
+
+log = logging.getLogger(__name__)
+
+def setup_app(command, conf, vars):
+    """Place any commands to setup sipbmp3web here"""
+    load_environment(conf.global_conf, conf.local_conf)
+    # Load the models
+    from sipbmp3web import model
+    print "Creating tables"
+    model.metadata.create_all(bind=config['pylons.app_globals'].sa_engine)
+
+    u = model.User()
+    u.user_name = u'manager'
+    u.display_name = u'Example manager'
+    u.email_address = u'manager@somedomain.com'
+    u.password = u'managepass'
+
+    model.DBSession.add(u)
+
+    g = model.Group()
+    g.group_name = u'managers'
+    g.display_name = u'Managers Group'
+
+    g.users.append(u)
+
+    model.DBSession.add(g)
+
+    p = model.Permission()
+    p.permission_name = u'manage'
+    p.description = u'This permission give an administrative right to the bearer'
+    p.groups.append(g)
+
+    model.DBSession.add(p)
+
+    u1 = model.User()
+    u1.user_name = u'editor'
+    u1.display_name = u'Example editor'
+    u1.email_address = u'editor@somedomain.com'
+    u1.password = u'editpass'
+
+    model.DBSession.add(u1)
+    model.DBSession.flush()
+
+    transaction.commit()
+    print "Successfully setup"
Index: gutenbach/README
===================================================================
--- gutenbach/README	(revision 593b5e1aa146f73b19a54598ff02d89816701a57)
+++ gutenbach/README	(revision 593b5e1aa146f73b19a54598ff02d89816701a57)
@@ -0,0 +1,70 @@
+So, you want to develop gutenbach, eh?  Good!
+
+Gutenbach is a distributed music player built on top of CUPs.  It
+essentially enables anybody with access to it to play music from their
+own computer -- all they have to do is "print" to the Gutenbach
+instance as they would any other document!
+
+The current repositories for various Gutenbach features are:
+    - gutenbach
+    - gutenbach-queue
+    - gutenbach-remctl
+    - gutenbach-rhythmbox-plugin
+    - gutenbach-itunes-plugin
+    - gutenbach-web
+
+All of these are located on GitHub:
+http://github.com/search?langOverride=&q=gutenbach&repo=&start_value=1&type=Repositories
+
+Additionally, see README.old for how Gutenbach /used/ to be installed.
+This can sometimes offer insight into how to fix things that are
+broken.
+
+Questions or comments should be directed at gutenbach@mit.edu
+
+NOTES:
+
+- If you keep getting zephyrs with the following errors:
+
+      Playback completed with the following errors:
+      bt_audio_service_open: connect() failed: Connection refused (111)
+      bt_audio_service_open: connect() failed: Connection refused (111)
+      bt_audio_service_open: connect() failed: Connection refused (111)
+
+  This is because you have alsa configured for bluetooth, but bluetooth is not
+  enabled.  You should either enable bluetooth, or uninstall the bluez-alsa
+  package.
+
+- To print to a machine without keytabs, you need to do:
+
+     lpr -Pprintername -Hhostname $file
+
+  The old `lpr -Pprintername@hostname $file` syntax will no longer work.
+
+TODO:
+- include mixer and channel in debconf, or even better, move it to
+  gutenbach-remctl and implement debconf in that package
+
+- the filter should die and send an error message if it can't find the
+  config file, not use defaults
+
+- this should really not conflict with pulse (the biggest problem at the moment is that
+  pulse will spew a bunch of errors like "Home directory /var/spool/cups/tmp not ours." I
+  can't figure out how to get rid of them, but they're really annoying.  As long as pulse
+  is in system mode, things seem to work otherwise.)
+
+- CUPS issues
+    * the CUPS daemon processes do not inherit groups from the lp user (so, for example,
+      even if we add 'lp' to 'audio', the process will not be running in group 'audio').
+      For the time being, I've set the CUPS daemon to always run under group 'audio',
+      but there should really be a better solution.
+    * debathena printer configuration defaults to using ipp, e.g.:
+       jhamrick@lemon-meringue:~$ lpstat -v sipbmp3 
+       device for sipbmp3: ipp://zsr.mit.edu:631/printers/sipbmp3
+     so you can't just do `lpq -Psipbmp3`, you have to use `lpq -Psipbmp3 -hzsr` or
+     something similar.  Same thing goes with lprm.  It is interesting that this is
+     only the case with lpq and lprm, though, and lpr Just Works.  This shouldn't be
+     an issue on non-debathena machines.
+
+- Create wrapper scripts for queuing/dequeuing jobs?  Making it easier to use across
+  multiple platforms, regardless of debathena/normal linux/etc?
Index: gutenbach/README.old
===================================================================
--- gutenbach/README.old	(revision 593b5e1aa146f73b19a54598ff02d89816701a57)
+++ gutenbach/README.old	(revision 593b5e1aa146f73b19a54598ff02d89816701a57)
@@ -0,0 +1,369 @@
+gutenbach
+    The SIPB MP3 Music Spooler
+
+We would love you forever if you took this README/INSTALL document and made
+a Debian package out of it.  That would be uber-awesome.  But otherwise, we're
+assuming that you just want to get gutenbach up and running on your own
+system.  Well, maybe we shouldn't assume that.
+
+
+What does gutenbach do?
+---------------------
+
+gutenbach is a music spooler.  As it turns out, streaming music is a hard
+problem, with lots of proprietary protocols, client and server software.
+All of these jiggety bits are kind of a pain to get working correctly,
+so, we built gutenbach on top of... a printer system.  It's very simple:
+you print an mp3 file to a special "printer", and it gets played on
+whatever speakers you're hooked up to.
+
+
+Prerequisites
+-------------
+
+You'll need a server hooked up to an audio system that you want to use, with
+Debathena installed on top of it.  Ubuntu Server Edition tends to work
+well.
+
+
+Installing gutenbach
+------------------
+
+
+1. Configuring your sound
+
+If you're installing gutenbach on a server environment, it's highly likely
+that you have a bare bones audio setup and everything is muted.  Make sure
+your sound works!  One common problem is that the daemon user (the user which
+will be actually playing the songs, or the user that your lpd, the print server,
+will be running under), isn't in the group audio, and thus can't play
+anything. If you're on Ubuntu Desktop, you'll probably have pulse-audio
+installed, in which case you should add lpd to the pulse-audio group
+as well, and make pulse-audio a single, system-wide instance. See
+this URL for detailed instructions:
+
+    http://www.pulseaudio.org/wiki/SystemWideInstance
+
+
+2. Install some prerequisite software
+
+On a vanilla Ubuntu server install, gutenbach will also need the following
+packages to work properly:
+
+    * mplayer
+    * libimage-exiftool-perl
+
+mpg123 can also be useful for testing, but is not strictly necessary.
+
+
+3. Setup gutenbach
+
+The Git repository for gutenbach is located in:
+
+    /afs/sipb/project/sipbmp3/gutenbach.git
+
+Place this in /root/gutenbach; the remctl scripts will assume that the
+scripts can be found in this location.
+
+
+4. Configure gutenbach
+
+gutenbach will send zephyrs during playback start and end.  Since you don't
+want to be spamming sipb-auto (which is the default zephyr class these
+are sent to), you should create a configuration file:
+
+    /etc/gutenbach-filter-config.pl
+
+That looks like:
+
+    $zephyr_class = "my-auto-class"
+    $host = "hostname"
+    $queue = "printername"
+
+XXX: Auto-detect hostname and invent a sane default zephyr class off of it.
+
+
+5. Configure gutenbach init scripts
+
+gutenbach will attempt to drop some information into /var/run/gutenbach, which
+needs to exist and you can't simply mkdir since /var/run on Ubuntu is
+a tempfs. Thus, add the following lines to a new file, /etc/init.d/gutenbach:
+
+#!/bin/sh
+mkdir /var/run/gutenbach
+touch /var/run/gutenbach/status
+chmod 0777 /var/run/gutenbach/status
+
+You should also run these commands yourself. :-)
+
+XXX: Note from jhamrick: I would recomment actually setting this up as a nice startup script, with a switch case for {start|stop|restart} (where with stop you just remove the directory, and with restart you remove the directory and then recreate it).  Also, gutenbach won't actually run on startup unless there are scripts in the /etc/rc*.d directory, so we need to do:
+
+> ln -s ../init.d/gutenbach /etc/rc2.d/S61gutenbach
+> ln -s ../init.d/gutenbach /etc/rc3.d/S61gutenbach
+> ln -s ../init.d/gutenbach /etc/rc4.d/S61gutenbach
+> ln -s ../init.d/gutenbach /etc/rc5.d/S61gutenbach
+> ln -s ../init.d/gutenbach /etc/rc0.d/K39gutenbach
+> ln -s ../init.d/gutenbach /etc/rc1.d/K39gutenbach
+> ln -s ../init.d/gutenbach /etc/rc6.d/K39gutenbach
+
+XXX: We need better permissions for status. Also, quentin wants to put this
+file in AFS.
+
+
+6. Configure lpd
+
+You'll need to setup a printcap file so that your machine will be actually
+running a printer! The magic lines are:
+
+    :if=|/path/to/gutenbach-filter
+    :lp=/dev/null
+
+Which mean that we pipe the mp3 files through gutenbach-filter (which does
+the actual playing), and then dump the rest of the output into /dev/null,
+since we don't actually care about it.
+
+A standard printcap entry looks like this:
+
+printername
+  :server
+  :cm=Human Readable Printer Name
+  :lp=/dev/null
+  :if=|/path/to/gutenbach-filter
+  :sd=/var/spool/lpd/printername
+  :ml=0:mx=0:sh:sf
+  :create_files
+
+printcap is located in /etc/printcap, but you can also edit the symlink
+in /etc/lprng/printcap.
+
+In /etc/lprng/lpd.conf:
+
+    printcap_path=...
+
+add /etc/lprng/printcap to the beginning of the pathlist, so it looks like:
+
+    printcap_path=/etc/lprng/printcap:|/usr/lib/get_hesiod_pcap
+
+/etc/printcap will also work, assuming lprng doesn't do something funky
+in the future.
+
+WARNING: On a cluster machine, get_hesiod_pcap will clobber gutenbach
+because it sets all:all=someotherprinter, making lpr not see gutenbach.
+There are two ways of fixing this:
+
+1. Patch /usr/lib/get_hesiod_pcap to return a full pcap line instead
+of all when it gets "all" from stdin.
+
+2. Remove get_hesiod_pcap from the list.
+
+To check your modifications, and create the necessary files and folders,
+you should run:
+
+    checkpc -V -f
+
+(As root, of course). You might get a failure on a directory
+/var/spool/lpd/%P doesn't exist; simply mkdir it and run checkpc -V -f
+
+With recent versions of lprng, you will also need to make lpd less paranoid
+about accepting connections.  This usually means the following two changes:
+
+In /etc/lprng/lpd.perms:
+
+    REJECT NOT SERVER
+
+should be commented out, as:
+
+    #REJECT NOT SERVER
+
+You will also need to add this line in lpd.conf:
+
+    lpd_listen_port=515
+
+By default, printers don't allow other users to remove other people's print
+jobs.  For an MP3 spooler, however, this may be a desirable trait, especially
+if someone trys to rickroll you.  You can let everyone lprm jobs by
+commenting out the line:
+
+    REJECT SERVICE=M
+
+XXX: On scourge, ACCEPT SERVICE=M SAMEHOST SAMEUSER doesn't seem to work
+properly, so removing the REJECT line is a must!
+
+XXXXXX: gutenbach accepts no responsibility for social faux pas that may
+be committed with this system.  It is consider good etiquette, however,
+to clear the status file if you're on zsr.
+
+XXXXXXXX: Another note from jhamrick (09/2009): debathena-lprng does not start up with the system by default.  debathena developers are currently deciding whether to change this, but in the meantine you will want to do:
+
+> ln -s ../etc/init.d/debathena-lprng /etc/rc2.d/S60debathena-lprng
+> ^rc2^rc3
+> ^rc3^rc4
+> ^rc4^rc5
+> ln -s ../etc/init.d/debathena-lprng /etc/rc0.d/K40debathena-lprng
+> ^rc0^rc1
+> ^rc1^rc6
+
+7. Test, round one
+
+The first test you should do to make sure gutenbach is configured properly is
+to attempt to play an mp3 file locally, via lpr.  The command you should use
+is:
+
+    mit-lpr -Pprintername@localhost filename.mp3
+
+Note the "mit-" prefix; Debathena defaults to CUPS, which we have not set
+up yet.
+
+See below for troubleshooting tips.
+
+
+8. Test, round two
+
+The second test you should do is try playing an MP3 remotely. The appropriate
+command is:
+
+    mit-lpr -Pprintername@hostname filename.mp3
+
+(Once again, with "mit-").  See below for troubleshooting tips.
+
+
+9. Troubleshooting
+
+So, it didn't work.  How did it fail?
+
+----
+sending job 'ezyang@vivace+800' to asdfasdf@localhost
+ connecting to 'localhost', attempt 1
+ cannot open connection to localhost - No such file or directory
+----
+
+This means that lpd is not accepting connections properly.  Check to see
+if you configured lpd.perms and lpd.conf correctly.  Try rebooting lpd
+by pkill lpd && lpd.  Use nmap to check of port 515 is open, both on the
+local machine and on a remote machine.  Check if networking is working
+on the server.  Use netstat to see what programs are listening to port
+515, and from which hosts.  Check iptables.
+
+----
+lpr: The printer or class was not found.
+----
+
+You're using lpr, not mit-lpr.
+
+----
+(no output, but no sound)
+----
+
+It looks like you managed to send the file over.  To diagnose the problems
+further:
+
+    * Check the zephyr logs of the class gutenbach was configured to
+      send messages to:
+
+        - If you see a reasonably full and formatted message, that means
+          that either: 1. your volume is turned off, or 2. mplayer is
+          horribly broken (XXX: it probably isn't, but I don't know what
+          error message it gives in that case)
+
+          By the way, be sure to check both the *hardware* and the
+          *software* volume.
+
+        - If you see a blank zephyr, that means that gutenbach-filter died
+          trying to get a handle to /var/run/gutenbach/status and failing.
+          Check if you created the file correctly, and that daemon has
+          permissions to write to it. If you rebooted recently, check if
+          you have the appropriate init.d script setup.
+
+    * Otherwise, check /var/spool/lpd/printername/status.pr (most of all the
+      other files are useless, especially including log):
+
+        - If you see a Perl error, fix it. The most common cause is because
+          exiftools is not installed.
+
+    * Otherwise, double check the userland code in gutenbach-filter. XXX: There
+      are some edge-cases that don't give nice error messages, for example
+      the failed write to /var/run/gutenbach/status
+
+
+10. Deploying CUPS
+------------------
+
+The final step is to give your newfangled printer a cups.mit.edu record.
+In order to do this, you will need to ask IS&T for a Moira pcap entry
+request.  Send mail to hesreq@mit.edu, specifying that you are setting
+up a gutenbach clone, that you want a Moira pcap entry, the printer name
+and server name of your queue, and that you want hwtype=LOCAL,ka=1. It wouldn't
+hurt to also tell them where your print queue is, and a contact list.
+
+Once you get the pcap entry, ping sipb-cups@mit.edu to add your machine
+to the public list.
+
+Troubleshooting:
+
+----
+Send_auth_transfer: on client client_krb5_auth failed - krb5_sendauth failed -
+Bad application version was sent (via sendauth)
+----
+
+This means that the ka field in your Hesiod pcap entry is incorrectly set to
+0.  Send mail to hesreq@mit.edu requesting ka be set to 1.
+
+
+11. Setting up keytabs on the machine
+-------------------------------------
+
+Email accounts@mit.edu and ask them for two keytabs: one for your machine,
+and one for "daemon" at your machine.  When you get the keytab, reset their
+passwords by running:
+
+    k5srvutil change -f your.keytab
+    k5srvutil delold -f your.keytab
+
+Now, install the machine-wide keytab in /etc/krb5.keytab, and install the
+daemon keytab as /etc/daemon.keytab.  (The first filename is required; the
+second can be changed.)  Make sure you chown and chgrp it to daemon.
+
+Finally, edit /etc/printcap and give it information about the Kerberos:
+
+gutenbach: server
+    :cm=SIPB MP3 Spooler
+    ...
+    :auth_forward=kerberos5
+    :use_auth=kerberos5
+    :kerberos_id=daemon/your-machine-name.mit.edu@ATHENA.MIT.EDU
+    :kerberos_keytab=/etc/daemon.keytab
+
+Troubleshooting
+
+-----
+Printer 'pikamp3@LESBIAN-BABY-SEAGULL-GODDESS.MIT.EDU' - 
+on client client_krb5_auth failed - krb5_sendauth failed - Bad response (during sendauth exchange)
+-----
+
+Your daemon.keytab is not readable by daemon.
+
+12. Setting up remctl commands
+-----------------------------
+
+It's useful to be able to control the volume. Remctl is the way to do
+this without asking people to ssh in, and then alsamix(er).
+
+You will probably need to apt-get install remctl-server. Then, replace
+the /etc/remctl/conf.d directory with a Git checkout of
+/afs/sipb/project/sipbmp3/gutenbach-remctl.git.
+
+Please note that remctl will NOT work without a keytab setup.
+
+
+X. Deploying gutenbachweb
+------------------------
+
+gutenbach web provides a nice and user-friendly interface for some common
+tasks. XXX: It is currently not portable beyond zsr. Stay tuned!
+
+X. Troubleshooting
+------------------
+
+If things are broken on a reboot, make sure you're running:
+
+    /etc/init.d/{gutenbach,zhm,debatehna-lprng}
Index: gutenbach/inst/create-filter-config
===================================================================
--- gutenbach/inst/create-filter-config	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/inst/create-filter-config	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,39 @@
+#!/bin/sh -e
+
+echo "Creating /usr/lib/gutenbach/config/gutenbach-filter-config.pl..." >&2
+if [ -e "/usr/lib/gutenbach/config/gutenbach-filter-config.pl" ]; then
+    rm /usr/lib/gutenbach/config/gutenbach-filter-config.pl
+fi
+
+failed=
+if [ -e /usr/lib/gutenbach/config/printername ]; then
+    printername=$(cat /usr/lib/gutenbach/config/printername)
+else
+    failed="yes"
+fi
+if [ -e /usr/lib/gutenbach/config/hostname ]; then
+    hostname=$(cat /usr/lib/gutenbach/config/hostname)
+else
+    failed="yes"
+fi
+if [ -e /usr/lib/gutenbach/config/zephyrclass ]; then
+    zephyrclass=$(cat /usr/lib/gutenbach/config/zephyrclass)
+else
+    failed="yes"
+fi
+
+mixer="PCM"
+channel="Front Left"
+config_file="/usr/lib/gutenbach/config/gutenbach-filter-config.pl"
+
+if [ -z "$failed" ]; then
+    echo "\$zephyr_class = \""$zephyrclass"\";" >> "$config_file"
+    echo "\$host = \""$hostname"\";" >> "$config_file"
+    echo "\$queue = \""$printername"\";" >> "$config_file"
+    echo "\$mixer = \""$mixer"\";" >> "$config_file"
+    echo "\$channel = \""$channel"\";" >> "$config_file"
+    echo "1;" >> "$config_file"
+else
+    echo "Error: One of /usr/lib/gutenbach/config/{printername,hostname,zephyrclass} does not exist!" >&2
+    exit 1
+fi
Index: gutenbach/inst/cups-add-printer
===================================================================
--- gutenbach/inst/cups-add-printer	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/inst/cups-add-printer	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+if [ -e /usr/lib/gutenbach/config/printername ]; then
+    printername="$(cat /usr/lib/gutenbach/config/printername)"
+    echo "Adding new printer $printername..."
+
+    lpadmin -p "$printername" -E -v gutenbach:/dev/null -D "Gutenbach Music Spooler"
+
+else
+    echo "Error: /usr/lib/gutenbach/config/printername does not exist"
+    exit 1
+fi
Index: gutenbach/inst/cups-set-perms
===================================================================
--- gutenbach/inst/cups-set-perms	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/inst/cups-set-perms	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+# Set the CUPS permissions such that anyone can add/remove jobs
+# from the queue for gutenbach.
+
+if [ -e /usr/lib/gutenbach/config/printername ]; then
+    printername="$(cat /usr/lib/gutenbach/config/printername)"
+
+    conffile="/etc/cups/cupsd.conf"
+    mv "$conffile" "$conffile.bak"
+    touch "$conffile"
+
+    while read line; do
+	listen_localhost=$(echo "$line" | grep '^Listen localhost' || true)
+	if [ -n "$listen_localhost" ]; then
+	    echo "#GUTENBACH$line" >> "$conffile"
+       	else
+	    root_location=$(echo "$line" | grep '^<Location />$' || true)
+	    if [ -n "$root_location" ]; then
+		echo "<Location />" >> "$conffile"
+		echo "Allow from All # added by Gutenbach" >> "$conffile"
+	    else
+		echo "$line" >> "$conffile"
+	    fi
+	fi
+
+    done < "$conffile.bak"
+    rm "$conffile.bak"
+
+    cat >> "$conffile" <<EOF
+# Begin configurations for gutenbach
+
+Group audio
+Listen *
+ServerAlias *
+
+<Location /printers/$printername>
+    <Limit Cancel-Job Purge-Jobs CUPS-Authenticate-Job>
+        Order deny,allow
+        Allow from All
+    </Limit>
+
+    Order Deny, Allow
+    Allow from All
+</Location>
+
+# End configurations for gutenbach
+EOF
+
+    /etc/init.d/cups reload
+
+else
+    echo "Error: /usr/lib/gutenbach/config/printername does not exist"
+    exit 1
+fi
Index: gutenbach/inst/edit-lpd.conf
===================================================================
--- gutenbach/inst/edit-lpd.conf	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/inst/edit-lpd.conf	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,34 @@
+#!/bin/sh -e
+
+echo "Editing /etc/lprng/lpd.conf..." >&2
+# add the printcap path to lpd.conf
+if [ -e /etc/lprng/lpd.conf ]; then
+    mv /etc/lprng/lpd.conf /etc/lprng/lpd.conf.bak
+    touch /etc/lprng/lpd.conf	
+
+    while read line; do
+	printcap=$(echo "$line" | grep ^printcap_path= || true)
+	if [ -n "$printcap" ]; then
+	    lprngprintcap=$(echo "$line" | grep /etc/lprng/printcap || true)
+	    printcap=$(echo "$line" | grep /etc/printcap || true)
+	    if [ ! -n "$lprngprintcap" ] && [ ! -n "$printcap" ]; then
+		echo "#$line" >> /etc/lprng/lpd.conf
+		echo "$line" | sed 's,printcap_path=,printcap_path=/etc/lprng/printcap:\|,' >> /etc/lprng/lpd.conf
+	    else
+		echo "$line" >> /etc/lprng/lpd.conf
+	    fi
+	else
+	    echo "$line" >> /etc/lprng/lpd.conf
+	fi
+    done < /etc/lprng/lpd.conf.bak
+    rm /etc/lprng/lpd.conf.bak
+else
+    touch /etc/lprng/lpd.conf
+    echo "printcap_path=/etc/lprng/printcap" >> /etc/lprng/lpd.conf
+fi
+
+# add lpd_listen_port to lpd.conf
+lpd_listen_port=$(cat /etc/lprng/lpd.conf | grep ^lpd_listen_port=515 || true)
+if [ ! -n "$lpd_listen_port" ]; then
+    echo "lpd_listen_port=515" >> /etc/lprng/lpd.conf
+fi
Index: gutenbach/inst/edit-lpd.perms
===================================================================
--- gutenbach/inst/edit-lpd.perms	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/inst/edit-lpd.perms	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# make lpd less paranoid about accepting connections
+# (comment out REJECT NOT SERVER)
+# allow other users to dequeue music
+# (comment out REJECT SERVICE=M)
+# make it only accept A jobs
+# (add 
+#    ACCEPT SERVICE=R,P SERVER
+#    ACCEPT C=CA* SERVICE=R,P
+#    REJECT SERVICE=R,P)
+
+echo "Editing /etc/lprng/lpd.perms..." >&2
+if [ -e /etc/lprng/lpd.perms ]; then
+    mv /etc/lprng/lpd.perms /etc/lprng/lpd.perms.bak
+    touch /etc/lprng/lpd.perms	
+
+    while read line; do
+	reject_not_server=$(echo "$line" | grep '^REJECT NOT SERVER' || true)
+	if [ -n "$reject_not_server" ]; then
+	    echo "#$line" >> /etc/lprng/lpd.perms
+	    
+	    echo "## BEGIN gutenbach configuration" >> /etc/lprng/lpd.perms
+	    echo "ACCEPT SERVICE=R,P SERVER" >> /etc/lprng/lpd.perms
+	    echo "ACCEPT C=CA* SERVICE=R,P" >> /etc/lprng/lpd.perms
+	    echo "REJECT SERVICE=R,P" >> /etc/lprng/lpd.perms
+	    echo "## END gutenbach configuration" >> /etc/lprng/lpd.perms
+      	else
+	    reject_service=$(echo "$line" | grep '^REJECT SERVICE=M' || true)
+	    if [ -n "$reject_service" ]; then
+		echo "#line" >> /etc/lprng/lpd.perms
+	    else
+		echo "$line" >> /etc/lprng/lpd.perms
+	    fi
+	fi
+    done < /etc/lprng/lpd.perms.bak
+    rm /etc/lprng/lpd.perms.bak
+fi
Index: gutenbach/inst/edit-printcap
===================================================================
--- gutenbach/inst/edit-printcap	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/inst/edit-printcap	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,35 @@
+#!/bin/sh -e
+
+echo "Editing /etc/printcap..." >&2
+if [ ! -e /etc/printcap ]; then
+    touch /etc/printcap
+fi
+
+if [ ! -e /etc/lprng/printcap ]; then
+    ln -s /etc/printcap /etc/lprng/printcap
+fi
+
+if [ -e /usr/lib/gutenbach/config/printername ]; then
+    printername=$(cat /usr/lib/gutenbach/config/printername)
+
+    printcap=$(cat /etc/printcap | grep ^`echo $printername` || true)
+    if [ ! -n "$printcap" ]; then
+    # add the appropriate entry into /etc/printcap, /etc/lprng/printcap
+	echo "$printername" >> /etc/printcap
+	echo "  :server" >> /etc/printcap
+	echo "  :cm=Gutenbach Music Spooler" >> /etc/printcap
+	echo "  :lp=/dev/null" >> /etc/printcap
+	echo "  :if=|/usr/lib/gutenbach/gutenbach-filter" >> /etc/printcap
+	echo "  :lf=/var/spool/lpd/$printername/log" >> /etc/printcap
+	echo "  :sd=/var/spool/lpd/$printername" >> /etc/printcap
+	echo "  :ml=0:mx=0:sh" >> /etc/printcap
+	echo "  :create_files" >> /etc/printcap
+	echo "  :auth_forward=kerberos5" >> /etc/printcap
+	echo "  :use_auth=kerberos5" >> /etc/printcap
+	echo "  :kerberos_id=daemon/"`hostname`".mit.edu@ATHENA.MIT.EDU" >> /etc/printcap
+	echo "  :kerberos_keytab=/etc/daemon.keytab" >> /etc/printcap
+    fi
+else
+    echo "Error: /usr/lib/gutenbach/config/printername does not exist!" >&2
+    exit 1
+fi
Index: gutenbach/inst/run-checkpc
===================================================================
--- gutenbach/inst/run-checkpc	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/inst/run-checkpc	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,22 @@
+#!/bin/sh -e
+
+# make sure directories exist for checkpc
+echo "Running checkpc..." >&2
+if [ ! -d /var/spool/lpd ]; then
+    mkdir /var/spool/lpd
+    mkdir /var/spool/lpd/%P
+else
+    if [ ! -d /var/spool/lpd/%P ]; then
+	mkdir /var/spool/lpd/%P
+    fi
+fi
+chmod og=rwx /var/spool/lpd
+chmod og=rwx /var/spool/lpd/%P
+
+if [ -e /usr/lib/gutenbach/config/printername ]; then
+    printername=$(cat /usr/lib/gutenbach/config/printername)
+    /usr/sbin/checkpc -P"$printername" -f
+else
+    echo "Error: /usr/lib/gutenbach/config/printername does not exist!" >&2
+    exit 1
+fi
Index: gutenbach/lib/README
===================================================================
--- gutenbach/lib/README	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/lib/README	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,20 @@
+If you want to help make changes to gutenbach, do NOT make changes
+locally!  They will most likely get overwritten by the next package
+install.  Instead, you should check out the source and edit the
+package itself:
+
+/afs/sipb/project/sipbmp3/gutenbach.git
+
+It may be useful to just edit things locally to make sure they work,
+and then add the change into the package.
+
+zsr (in SIPB) is set up to build packages, and already has a git clone
+in /root/gutenbach.  You can edit the source there, build the package,
+and then reinstall the package.
+
+A good guide to get started with debian packaging is here:
+http://debathena.mit.edu/packaging/
+
+jhamrick (Jessica Hamrick) is currently the active maintainer for
+gutenbach, so if you're interested in contributing but don't know
+quite where to start, you should talk to her.
Index: gutenbach/lib/TODO
===================================================================
--- gutenbach/lib/TODO	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/lib/TODO	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,2 @@
+- gutenbach-get-config should fail and error out if it can't find a
+   config file -- we can't use defaults since what would the defaults be?
Index: gutenbach/lib/gutenbach
===================================================================
--- gutenbach/lib/gutenbach	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/lib/gutenbach	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,333 @@
+#!/usr/bin/perl
+# Play the data on STDIN as an audio file
+#
+# $Id: gutenbach-filter,v 1.26 2009/02/20 00:27:17 geofft Exp root $
+# $Source: /usr/local/bin/RCS/gutenbach-filter,v $
+#
+# TODO
+# ----
+# Make this structured code. It's a mess.
+# Repeat what we just played for EXT files too
+# Support HTTP Auth on ogg streams
+# License, cleanup and package
+#
+# Jered Floyd <jered@mit.edu> takes very little credit for this code
+# apparently neither does Quentin Smith <quentin@mit.edu>
+
+use strict;
+use warnings;
+use Image::ExifTool qw(ImageInfo);
+use File::Spec::Functions;
+use File::Temp qw{tempfile tempdir};
+use File::Basename qw(basename);
+use LWP::UserAgent;
+use Data::Dumper;
+use IPC::Open2;
+use English;
+
+use vars qw/$zephyr_class $host $queue $mixer $channel/;
+
+require "/usr/lib/gutenbach/config/gutenbach-filter-config.pl" or die "Unable to load configuration";
+
+my $ua = new LWP::UserAgent;
+
+# This variable contains the pid of the child process (which runs
+# mplayer) once it has been forked, so that we can kill it on SIGTERM
+my $pid;
+
+# Replace STDERR with a log file in /tmp.
+open(CUPS, ">&STDERR") or die "Unable to copy CUPS filehandle";
+close(STDERR);
+open(STDERR, ">>", "/tmp/gutenbach.log") or warn "Couldn't open log: $!";
+
+# Set the TERM environment (for the benefit of mplayer?)
+# I don't know why we do this --quentin
+$ENV{"TERM"}="vt100";
+
+print STDERR "STDERR FROM SPOOL FILTER\n";
+
+# CUPS provides us with these arguments:
+#
+# argv[1]
+# The job ID
+# argv[2]
+# The user printing the job
+# argv[3]
+# The job name/title
+# argv[4]
+# The number of copies to print
+# argv[5]
+# The options that were provided when the job was submitted
+# argv[6]
+# The file to print (first program only)
+#
+# The scheduler runs one or more of these programs to print any given
+# job. The first filter reads from the print file and writes to the
+# standard output, while the remaining filters read from the standard
+# input and write to the standard output. The backend is the last
+# filter in the chain and writes to the device.
+
+printf(STDERR "Got \@ARGV: %s\n", Dumper(\@ARGV));
+
+my %arguments = (
+		 "job-id" => $ARGV[0],
+		 user => $ARGV[1],
+		 "job-title" => $ARGV[2],
+		 copies => $ARGV[3],
+		 options => {split(/[= ]/, $ARGV[4])},
+		 file => $ARGV[5],
+		);
+
+# If we weren't given a filename, we need to read from stdin. Since
+# mplayer really wants a file, let's write out to a temporary file
+# first.
+if (!$arguments{"file"}) {
+  my ($fh, $file) = tempfile("gutenbachXXXXX", UNLINK => 1); # Ask File::Temp for a safe temporary file
+  my $buf;
+  while (read(STDIN, $buf, 1024*1024)) { # Read 1M at a time and put it in the temporary file
+    print $fh $buf;
+  }
+  close($fh);
+  $arguments{"file"} = $file;
+}
+
+printf(STDERR "Got \%arguments: %s\n", Dumper(\%arguments));
+
+# Open up a zwrite command to announce the current track.
+my @zwrite_command = (qw(/usr/bin/zwrite -d -n -c), $zephyr_class, "-i", $queue.'@'.$host, "-s", "Gutenbach Music Spooler");
+
+print STDERR "Invoking @zwrite_command\n";
+open(ZEPHYR, "|-", @zwrite_command) or die "Couldn't launch zwrite: $!";
+
+my $status;
+if (exists($arguments{"options"}{"job-originating-host-name"})) {
+    print(ZEPHYR $arguments{"user"},"\@",$arguments{"options"}{"job-originating-host-name"}," is playing:\n");
+    $status = "User: ".$arguments{"user"}."\@".$arguments{"options"}{"job-originating-host-name"};
+} else {
+    print(ZEPHYR $arguments{"user"}," is playing:\n");
+    $status = "User: ".$arguments{"user"};
+}
+
+# SIGHUP handler, in case we were aborted
+sub clear_status {
+  kill 15, $pid if $pid;
+  my @zwrite_command = (qw(/usr/bin/zwrite -d -n -c), $zephyr_class, "-i", $queue.'@'.$host, "-s", "Gutenbach Music Spooler");
+  open(ZEPH, "|-", @zwrite_command);
+  print(ZEPH "Playback aborted.\n");
+  close(ZEPH);
+  die;
+}
+
+$SIG{HUP} = \&clear_status;
+$SIG{TERM} = \&clear_status;
+
+# Read the metadata information from the file.
+my ($filepath) = $arguments{"file"};
+my ($fileinfo) = ImageInfo($filepath);
+my ($magic) = $fileinfo->{FileType};
+my ($tempdir);
+my ($newpath);
+
+if ($magic) {
+  # $magic means that Image::ExifTool was able to identify the type of file
+  printf(ZEPHYR "%s file %s\n", $magic, $arguments{"job-title"});
+  $status .= sprintf(" Filetype: %s.", $magic);
+  $status .= sprintf(" Filename: %s.", $arguments{"job-title"});
+
+  if (exists $fileinfo->{'Title'}) {
+    printf(ZEPHYR "\@b{%s}\n", $fileinfo->{'Title'}) if exists $fileinfo->{'Title'};
+    $status .= sprintf(" Title: %s.", $fileinfo->{'Title'});
+  }
+  foreach my $key (qw/Artist Album AlbumArtist/) {
+    if (exists $fileinfo->{$key}) {
+      printf(ZEPHYR "%s\n", $fileinfo->{$key}) if exists $fileinfo->{$key};
+      $status .= sprintf(" %s: %s\n", $key, $fileinfo->{$key});
+    }
+  }
+
+  $tempdir = tempdir();
+  #awful hack -- geofft
+  #== -- quentin
+  # This code appears to create a new temporary directory and symlink
+  # the job file into the temporary directory under the original
+  # filename. I think this is because mplayer sometimes uses the file
+  # extension to identify a filetype.
+  $newpath = $tempdir . '/' . basename($arguments{"job-title"});
+  symlink($filepath, $newpath);
+  $filepath = $newpath;
+}
+elsif ($arguments{copies} == 42) {
+  # This is a flag that is set by jobs queued by split_playlist(); it tells us to not try to split the playlist again.
+  # Call resolve_external_reference to apply some heuristics to determine the filetype.
+  $filepath = resolve_external_reference($filepath, \%arguments);
+  if ($filepath =~ m|http://www\.youtube\.com/watch\?v=|) {
+    # YouTube URLs are resolved by the youtube-dl command.
+    # Launch youtube-dl
+    open(YTDL, "-|", "youtube-dl", "-g", $filepath) or die "Unable to invoke youtube-dl";
+    # Read the title (currently not doing so because youtube-dl doesn't know how to get the title.
+    my $title = ""; # <YTDL>
+    # Print the title to zephyr and the status string.
+    print ZEPHYR "YouTube video $filepath\n$title";
+    $status .= " YouTube video $filepath. $title.";
+    # youtube-dl prints the URL of the flash video, which we pass to mplayer as a filename.
+    $filepath = <YTDL>;
+    chomp $filepath;
+  } else { # Doesn't appear to be a YouTube URL.
+    print STDERR "Resolved external reference to $filepath\n";
+    printf(ZEPHYR "%s\n", $filepath);
+    $status .= sprintf(" External: %s\n", $filepath);
+  }
+}
+elsif (-T $filepath) { # If the file appears to be a text file, treat it as a playlist.
+  split_playlist($filepath, \%arguments);
+  close(ZEPHYR);
+  # See http://www.cups.org/documentation.php/api-filter.html#MESSAGES
+  print CUPS "NOTICE: $status\n";
+  exit 0;
+}
+
+close(ZEPHYR);
+print CUPS "NOTICE: $status\n";
+play_mplayer_audio($filepath, \%arguments);
+
+# Remove the symlink we made earlier for the filetype.
+if ($magic) {
+  unlink($newpath);
+  rmdir($tempdir);
+}
+
+# Play an external stream reference
+sub resolve_external_reference {
+  # Retrieve those command line opts.
+  my ($filepath, $arguments) = @_;
+
+  my ($format, $uri, $userpass);
+
+  open(FILE, "<", $filepath) or die "Couldn't open spool file";
+  if (<FILE> =~ /^(\S+)/) {
+    # Take the leading non-whitespace as a URL
+    $uri=$1;
+
+    if ($uri =~ m|http://www\.youtube\.com/watch\?v=|) {
+      return $uri;
+    }
+
+    # Fetch the URL with a HEAD request to get the content type
+    my $response = $ua->head($uri);
+
+    my $contenttype=($response->content_type() or "unknown");
+
+    if ($contenttype eq "audio/mpeg") { $format="MP3" }
+    elsif ($contenttype eq "application/x-ogg") { $format="OGG" }
+    elsif ($contenttype eq "application/ogg") { $format="OGG" }
+    elsif ($contenttype eq "audio/x-scpls") { $format="SHOUTCAST" }
+    else {
+      print ZEPHYR
+	"Unknown Content-Type $contenttype for URI $uri\n";
+    }
+  } else { # Unable to match the URL regex
+    print ZEPHYR "Couldn't read URI for external reference\n";
+    # Return the existing path, in the hopes that mplayer knows what to do with it.
+    return $filepath;
+  }
+
+  if ($format eq "SHOUTCAST") {
+    print ZEPHYR "Shoutcast playlist...\n";
+    return get_shoutcast($uri);
+  } elsif ($format eq "MP3") {
+  } elsif ($format eq "OGG") {
+  } else {
+    print ZEPHYR "Unrecognized stream format: $format\n";
+  }
+  return $uri;
+}
+
+sub split_playlist {
+  my ($file, $arguments) = @_;
+
+  my $i = 0;
+
+  open(FILE, "<", $filepath) or die "Couldn't open spool file";
+  while (<FILE>) {
+    chomp;
+    if (/^([^#]\S+)/) {
+      printf (STDERR "Found playlist line: %s\n", $_);
+      open(LPR, "|-", 'lpr', '-P'.$queue.'@localhost', '-#', '42', '-J', $arguments->{"job-title"}, '-o', 'job-priority=100');
+      print LPR $1;
+      close(LPR);
+      $i++;
+    }
+  }
+  printf(ZEPHYR "Playlist containing %d valid entries, split into separate jobs.\n", $i);
+}
+
+# Process a Shoutcast playlist
+# get_shoutcast(URI)
+sub get_shoutcast {
+  my $uri = shift(@_);
+
+  my $response = $ua->get($uri);
+  my (@titles, @uris);
+
+  foreach (split("\n", $response->content())) {
+      if (/^File\d+=(\S+)/) {
+	  push(@uris, $1);
+      }
+      if (/^Title\d+=(.+)$/) {
+	  push(@titles, $1);
+      }
+  }
+
+  # choose a random server
+  my $server = int(rand scalar(@uris));
+  # print the name of the stream if available
+  print ZEPHYR "$titles[$server]\n";
+  return $uris[$server];
+}
+
+sub play_mplayer_audio {
+  my ($filepath, $opts) = @_;
+
+  # Open up a zwrite command to show the mplayer output
+  my @zwrite_command = (qw(/usr/bin/zwrite -d -n -c), $zephyr_class, "-i", $queue.'@'.$host, "-s", "Gutenbach Music Spooler");
+
+  print STDERR "Invoking (from play_mplayer_audio): @zwrite_command\n";
+
+  # fork for mplayer
+  $pid = open(MP3STATUS, "-|");
+  unless (defined $pid) {
+    open(ZEPHYR, "|-", @zwrite_command) or die "Couldn't launch zwrite: $!";
+    print ZEPHYR "Couldn't fork: $!\n";
+    close(ZEPHYR);
+    return;
+  }
+
+  if ($pid) { #parent
+    # Check if there were any errors
+    if ($_ = <MP3STATUS>) {
+      open(ZEPHYR, "|-", @zwrite_command) or die "Couldn't launch zwrite: $!";
+      print ZEPHYR "Playback completed with the following errors:\n";
+      while (<MP3STATUS>) {
+	print ZEPHYR $_;
+      }
+      close(ZEPHYR);
+    } else {
+      open(ZEPHYR, "|-", @zwrite_command) or die "Couldn't launch zwrite: $!";
+      print ZEPHYR "Playback completed successfully.\n";
+      close(ZEPHYR);
+    }
+    close(MP3STATUS) || print ZEPHYR "mplayer exited $?\n";
+  }
+  else { # child
+    # redirect STDERR to STDOUT
+    open STDERR, '>&STDOUT';
+    # make sure that mplayer doesn't try to intepret the file as keyboard input
+    close(STDIN);
+    open(STDIN, "/dev/null");
+
+    my @args = (qw|/usr/bin/mplayer -vo fbdev2 -zoom -x 1024 -y 768 -framedrop -nolirc -cache 512 -ao alsa -really-quiet |, $filepath);
+    #print STDERR "About to exec: ", Dumper([@args]);
+    exec(@args) ||
+      die "Couldn't exec";
+  }
+}
Index: gutenbach/lib/gutenbach-get-config
===================================================================
--- gutenbach/lib/gutenbach-get-config	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/lib/gutenbach-get-config	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,35 @@
+#!/usr/bin/perl
+use strict;
+# Get configuration of gutenbach
+
+my $zephyr_class = `hostname`;
+chomp($zephyr_class);
+my $host = `hostname`;
+chomp($zephyr_class);
+my $queue = "gutenbach";
+my $mixer = "PCM";
+my $channel = "Front Left";
+
+# Configuration
+my $config_file = "/usr/lib/gutenbach/config/gutenbach-filter-config.pl";
+if (-r $config_file) {
+    # Inline the configuration file
+    local $/;
+    my $fh;
+    open $fh, $config_file;
+    eval <$fh>;
+}
+
+my %config = (
+    'zephyr-class' => $zephyr_class,
+    'host' => $host,
+    'queue' => $queue,
+    'mixer' => $mixer,
+    'channel' => $channel,
+);
+
+foreach my $argv (@ARGV)
+{
+    #print $argv . "\n";
+    print $config{$argv};
+}
Index: gutenbach/rm/cups-remove-printer
===================================================================
--- gutenbach/rm/cups-remove-printer	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/rm/cups-remove-printer	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+echo "Removing gutenbach printer..." >&2
+
+if [ -e /usr/lib/gutenbach/config/printername ]; then
+    printername="$(cat /usr/lib/gutenbach/config/printername)"
+
+    lpadmin -x "$printername"
+
+else
+    echo "Error: /usr/lib/gutenbach/config/printername does not exist"
+fi
Index: gutenbach/rm/cups-revert-perms
===================================================================
--- gutenbach/rm/cups-revert-perms	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/rm/cups-revert-perms	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,25 @@
+#!/bin/sh 
+
+echo "Reverting changes to /etc/cups/cupsd.conf..." >&2
+conffile="/etc/cups/cupsd.conf"
+
+mv "$conffile" "$conffile.bak"
+touch "$conffile"
+sed '/# Begin configurations for gutenbach/,/# End configurations for gutenbach/ d' < "$conffile.bak" > "$conffile"
+
+mv "$conffile" "$conffile.bak"
+touch "$conffile"
+while read line; do
+    listen_localhost=$(echo "$line" | grep '^#GUTENBACH' || true)
+    if [ -n "$listen_localhost" ]; then
+	echo "$(echo $line | sed s/#GUTENBACH//)" >> "$conffile"
+    else
+	root_location=$(echo "$line" | grep '^Allow from All # added by Gutenbach$' || true)
+	if [ -z "$root_location" ]; then
+	    echo "$line" >> "$conffile"
+	fi
+    fi
+
+done < "$conffile.bak"
+
+rm "$conffile.bak"
Index: gutenbach/rm/revert_lpd.conf
===================================================================
--- gutenbach/rm/revert_lpd.conf	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/rm/revert_lpd.conf	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,11 @@
+#!/bin/sh -e
+
+echo "Reverting changes to /etc/lprng/lpd.conf..." >&2
+mv /etc/lprng/lpd.conf /etc/lprng/lpd.conf.bak
+touch /etc/lprng/lpd.conf
+while read line; do
+    lpd_listen_port=$(echo "$line" | grep ^lpd_listen_port=515$ || true)
+    if [ -z "$lpd_listen_port" ]; then
+	echo "$line" >> /etc/lprng/lpd.conf
+    fi
+done < /etc/lprng/lpd.conf.bak
Index: gutenbach/rm/revert_lpd.perms
===================================================================
--- gutenbach/rm/revert_lpd.perms	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/rm/revert_lpd.perms	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,19 @@
+#!/bin/sh -e
+
+echo "Reverting changes to /etc/lprng/lpd.perms..." >&2
+mv /etc/lprng/lpd.perms /etc/lprng/lpd.perms.bak
+touch /etc/lprng/lpd.perms
+while read line; do
+    reject_not_server=$(echo "$line" | grep '^#REJECT NOT SERVER$' || true)
+    if [ ! -z "$reject_not_server" ]; then
+	echo "REJECT NOT SERVER" >> /etc/lprng/lpd.perms
+    else
+	reject_service=$(echo "$line" | grep '^#REJECT SERVICE=M$' || true)
+	if [ ! -z "$reject_service" ]; then
+	    echo "REJECT SERVICE=M" >> /etc/lprng/lpd.perms
+	else
+            echo "$line" >> /etc/lprng/lpd.perms
+	fi
+    fi
+done < /etc/lprng/lpd.perms.bak
+rm /etc/lprng/lpd.perms.bak
Index: gutenbach/rm/revert_printcap
===================================================================
--- gutenbach/rm/revert_printcap	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
+++ gutenbach/rm/revert_printcap	(revision 04f7e0593d10b7f59bee40314f6e3e81da8c34ed)
@@ -0,0 +1,30 @@
+#!/bin/sh -e
+
+echo "Reverting changes to /etc/printcap..." >&2
+
+if [ -e /usr/lib/gutenbach/config/printername ]; then
+    printername=$(cat /usr/lib/gutenbach/config/printername)
+
+    mv /etc/printcap /etc/printcap.bak
+    touch /etc/printcap
+    foundprinter="false"
+    while read line; do
+	printername=$(echo "$line" | grep ^`echo $printername`$ || true)
+	if [ -n "$printername" ]; then
+            foundprinter="true"
+	else
+            if [ "$foundprinter"="true" ]; then
+		configline=$(echo "$line" | grep ^[[:space:]]*: || true)
+		if [ -z "$configline" ]; then
+                    foundprinter="false"
+                    echo "$line" >> /etc/printcap
+		fi
+            else
+		echo "$line" >> /etc/printcap
+            fi
+	fi
+    done < /etc/printcap.bak
+    rm /etc/printcap.bak
+else
+    echo "Error: /usr/lib/gutenbach/config/printername does not exist!" >&2
+fi
