Checkpoint. default tip
authorMahlon E. Smith <mahlon@martini.nu>
Mon, 23 Sep 2013 09:10:55 -0700
changeset 1 b3419d05eabb
parent 0 80c32ef237c6
Checkpoint. * Got fonts working cross platform by only using SVG. This will change with the new webkit engine when Tide 1.4 is released. * Use the Tide API accessors for cross-window data. * Make the ThingFish server object an observable, so models and their URI bases can be changed easily. * Add options for saving window state and position. * Contextual menu updates/tests for later.
.hgignore
NOTES
Resources/css/main.css
Resources/css/prefs.css
Resources/icon.png
Resources/img/tray_icon.png
Resources/js/d/about.js
Resources/js/d/dascyllus.js
Resources/js/d/main.js
Resources/js/d/prefs.js
Resources/js/d/splash.js
Resources/misc/blinky_squares.pjs
Resources/misc/draw.pjs
Resources/window/about.html
Resources/window/main.html
Resources/window/prefs.html
Resources/window/prefs/servers.ejs
Resources/window/splash.html
manifest
tiapp.xml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Mon Sep 23 09:10:55 2013 -0700
@@ -0,0 +1,1 @@
+packages/*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NOTES	Mon Sep 23 09:10:55 2013 -0700
@@ -0,0 +1,3 @@
+
+- Make D.tf observable.
+
--- a/Resources/css/main.css	Mon Sep 02 02:22:21 2013 -0700
+++ b/Resources/css/main.css	Mon Sep 23 09:10:55 2013 -0700
@@ -1,37 +1,64 @@
+
+
+@font-face {
+	font-family: 'batch';
+	src: url('app://font/batch/batch.svg#batch_iconsregular') format('svg');
+	font-weight: normal;
+	font-style: normal;
+}
 
 @font-face {
 	font-family: 'exo';
-	src: url('app://font/exo/exo-regular.eot');
-	src: url('app://font/exo/exo-regular.eot?#iefix') format('embedded-opentype'),
-		 url('app://font/exo/exo-regular.woff') format('woff'),
-		 url('app://font/exo/exo-regular.ttf') format('truetype'),
-		 url('app://font/exo/exo-regular.svg#batchregular') format('svg');
+	src: url('app://font/exo/exo-regular.svg#exoregular') format('svg');
 	font-weight: normal;
 	font-style: normal;
 }
 
 @font-face {
 	font-family: 'exobold';
-	src: url('app://font/exo/exo-bold.eot');
-	src: url('app://font/exo/exo-bold.eot?#iefix') format('embedded-opentype'),
-		 url('app://font/exo/exo-bold.woff') format('woff'),
-		 url('app://font/exo/exo-bold.ttf') format('truetype'),
-		 url('app://font/exo/exo-bold.svg#batchregular') format('svg');
+	src: url('app://font/exo/exo-bold.svg#exoextrabolditalic') format('svg');
+	font-weight: normal;
+	font-style: normal;
+}
+
+/* TODO: TideKit 1.4, w/ newer webkit. */
+/*
+@font-face {
+	font-family: 'exo';
+	src: url('app://font/exo/exo-regular.eot'),
+		 url('app://font/exo/exo-regular.eot?#iefix') format('embedded-opentype'),
+		 url('app://font/exo/exo-regular.woff') format('woff'),
+		 url('app://font/exo/exo-regular.ttf') format('truetype'),
+		 url('app://font/exo/exo-regular.svg#exoregular') format('svg');
 	font-weight: normal;
 	font-style: normal;
 }
 
-/* http://adamwhitcroft.com/batch/ */
 @font-face {
-	font-family: 'batch';
-	src: url('app://font/batch/batch.eot');
-	src: url('app://font/batch/batch.eot?#iefix') format('embedded-opentype'),
-		 url('app://font/batch/batch.woff') format('woff'),
-		 url('app://font/batch/batch.ttf') format('truetype'),
-		 url('app://font/batch/batch.svg#batchregular') format('svg');
+	font-family: 'exobold';
+	src: url('app://font/exo/exo-bold.eot'),
+		 url('app://font/exo/exo-bold.eot?#iefix') format('embedded-opentype'),
+		 url('app://font/exo/exo-bold.woff') format('woff'),
+		 url('app://font/exo/exo-bold.ttf') format('truetype'),
+		 url('app://font/exo/exo-bold.svg#exobold') format('svg');
 	font-weight: normal;
 	font-style: normal;
 }
+*/
+
+/* http://adamwhitcroft.com/batch/ */
+/*
+@font-face {
+	font-family: 'batch';
+	src: url('app://font/batch/batch.eot'),
+		 url('app://font/batch/batch.eot?#iefix') format('embedded-opentype'),
+		 url('app://font/batch/batch.woff') format('woff'),
+		 url('app://font/batch/batch.ttf') format('truetype'),
+		 url('app://font/batch/batch.svg#batch') format('svg');
+	font-weight: normal;
+	font-style: normal;
+}
+*/
 
 a { 
 	text-decoration: none;
@@ -52,6 +79,7 @@
 	font-family: "exo", Helvetica, Geneva, sans-serif;
 	font-size: 1em;
 	text-shadow: 2px 2px 2px #000;
+	user-select: none;
 }
 
 fieldset {
--- a/Resources/css/prefs.css	Mon Sep 02 02:22:21 2013 -0700
+++ b/Resources/css/prefs.css	Mon Sep 23 09:10:55 2013 -0700
@@ -1,6 +1,34 @@
 
-#test {
-	margin-top: 10px;
+#sections {
+	text-align: center;
+	padding-left: 0px !important; /* override tide default style */
+}
+
+#sections .icon {
+	display: inline-block;
+	padding: 4px 10px;
+	font-size: 3em;
+}
+
+#sections li {
+	display: inline;
+	list-style-type: none;
+}
+
+#sections li a {
+	text-shadow: 2px 2px 1px #000;
+}
+
+#sections li a:active {
+	cursor: pointer;
+	text-shadow: none;
+	position: relative;
+	top: 1px;
+	left: 1px;
+}
+
+#test .info {
+	font-size: 0.8em;
 }
 
 input[type="text"] {
@@ -22,32 +50,7 @@
 	padding-right: 10px;
 }
 
-.icon {
-	display: inline-block;
-	padding: 4px 10px;
-	font-size: 3em;
-}
-
-#sections {
-	text-align: center;
-	padding-left: 0px !important; /* override tide default style */
+.selected {
+	border-bottom: 4px solid #444;
 }
 
-#sections li {
-	display: inline;
-	list-style-type: none;
-}
-
-#sections li a {
-	text-shadow: 2px 2px 1px #000;
-}
-
-#sections li a:active {
-	cursor: pointer;
-	text-shadow: none;
-	position: relative;
-	top: 1px;
-	left: 1px;
-}
-
-
Binary file Resources/icon.png has changed
Binary file Resources/img/tray_icon.png has changed
--- a/Resources/js/d/about.js	Mon Sep 02 02:22:21 2013 -0700
+++ b/Resources/js/d/about.js	Mon Sep 23 09:10:55 2013 -0700
@@ -1,3 +1,5 @@
+
+D = Ti.API.get( 'D' );
 
 // Animate the background image.
 //
@@ -28,7 +30,7 @@
 		img.src = "app://img/about/" + i + ".jpg"; 
 	}
 
-	D.delay( function(){ set_frame( 1 ) }, 5000 );
+	D.delay( function(){ set_frame( 1 ) }, 5000, true );
 });
 
 
--- a/Resources/js/d/dascyllus.js	Mon Sep 02 02:22:21 2013 -0700
+++ b/Resources/js/d/dascyllus.js	Mon Sep 23 09:10:55 2013 -0700
@@ -10,6 +10,7 @@
 	version:      Ti.API.Application.getVersion(),
 	ti_version:   Ti.getVersion(),
 	architecture: Ti.Platform.getArchitecture(),
+	copyright:    Ti.App.getCopyright(),
 
 	// Declared windows, for easy referencing
 	window: {},
@@ -20,11 +21,95 @@
 	// Persistent storage DB handle.
 	db: null,
 
-	// The current TF server we'll be connected to;
+	// The primary main tray menu.
+	menu: null,
+
+	// The current TF server we're connected to.
+	//
 	//     uri: http://localhost:8080/
 	//     version: Thingfish 0.5.0 (build ...)
 	//
-	tf: null,
+	tf: new can.Observe({
+		uri: null,
+		version: null
+	}),
+
+
+	// Actions to perform at startup.
+	//
+	init: function() {
+		var D = this;
+
+		D.initDB();
+		D.window.main = Ti.UI.getMainWindow();
+		D.initMenu();
+
+		// Thingfish server object event handlers.
+		//
+		D.tf.bind( 'uri', D.initTF );
+		var server_uri = D.getPref( 'server_uri' );
+		if ( server_uri ) D.tf.attr( 'uri', server_uri );
+
+		// Main window event handlers.
+		//
+		D.window.main.addEventListener( 'moved', function() { D.saveWindowState(); });
+		D.window.main.addEventListener( 'resized', function() {
+			D.delay( function() {
+				D.saveWindowState();
+			},
+			500, true );
+		});
+		$( document ).contextmenu( function() { D.window.main.setContextMenu( D.menu ); });
+
+		// Each Tide window is an environment unto itself.  There is a global
+		// key/val namespace, though.  Make the 'D' namespace accessible to other windows here.
+		Ti.API.set( 'D', D );
+	},
+
+
+	// Determine if we should be running in developer mode.
+	// This requires that the user has previously enabled it in preferences,
+	// and the --debug command line option was passed.
+	//
+	devmode: function() {
+		return Ti.App.getArguments()[0] == "--debug" && this.getBoolPref( 'devmode' );
+	},
+
+
+	// Event handler for changing the Thingfish server URI.
+	// Sanity check and instantiate appropriate Model(s).
+	//
+	initTF: function( event, uri, old_uri ) {
+		var serverOk = function( data, status, xhr ) {
+			var info = xhr.getResponseHeader( 'x-thingfish' );
+			if ( ! info ) {
+				D.tf.attr( 'version', null );
+				D.notify( 'Unable to connect.', 'Not a Thingfish server?' );
+				return;
+			}
+
+			D.tf.attr( 'version', data.version )
+			D.notify( 'Connected.', info );
+
+			// FIXME: models?
+		};
+
+		try {
+			$.ajax({
+				url:      uri + '/serverinfo',
+				async:    false,
+				success:  serverOk,
+				error:    function( xhr, status, err ) {
+					D.tf.attr( 'version', null );
+					D.notify( 'Unable to connect.', err ? err : 'Network error.' );
+				},
+				dataType: 'json'
+			});
+		}
+		catch( err ) {
+			console.log( err );
+		};
+	},
 
 
 	// Open a handle to the local database, initializing it
@@ -147,45 +232,111 @@
 	},
 
 
-	// Attempt a connection to a remote Thingfish server, and
-	// populate the D.tf variable.
+	// Record the main window position and state.
 	//
-	checkServer: function( uri ) {
-		this.tf = null;
+	saveWindowState: function() {
+		var w      = this.window.main;
+		var x      = w.getX();
+		var y      = w.getY();
+		var width  = w.getWidth();
+		var height = w.getHeight();
+
+		var hiddenMenu     = this.menu.getItemAt( 2 ).getSubmenu().getItemAt( 0 );
+		var fullScreenMenu = this.menu.getItemAt( 2 ).getSubmenu().getItemAt( 1 );
+
+		var pos = this.getPref( 'window_pos' );
+		pos = Ti.JSON.parse( pos );
+
+		// first time
+		//
+		if ( ! pos ) {
+			pos =  {
+				x: x,
+				y: y,
+				width: width,
+				height: height,
+				fullscreen: false,
+				hidden: false
+			};
+		}
+
+		if ( w.isVisible() ) {
+			pos.hidden = false;
+			fullScreenMenu.enable();
+		}
+		else {
+			pos.hidden = true;
+			fullScreenMenu.disable();
+		}
 
-		var serverOk = function( data, status, xhr ) {
-			var info = xhr.getResponseHeader( 'x-thingfish' );
-			if ( info ) {
-				D.tf = {
-					uri: uri,
-					version: data.version
-				};
-				D.notify( 'Connected.', info );
+		if ( w.isFullScreen() ) {
+			pos.fullscreen = true;
+			hiddenMenu.disable();
+		}
+		else {
+			pos.fullscreen = false;
+			hiddenMenu.enable();
+		}
+
+		if ( ! pos.fullscreen ) {
+			pos.x      = x;
+			pos.y      = y;
+			pos.width  = width;
+			pos.height = height;
+		}
+
+		this.setPref( 'window_pos', Ti.JSON.stringify(pos) );
+	},
+
+
+	// Set the main window according to previously saved values.
+	//
+	setWindowState: function() {
+		var w   = this.window.main;
+		var pos = this.getPref( 'window_pos' );
+
+		var hiddenMenu     = this.menu.getItemAt( 2 ).getSubmenu().getItemAt( 0 );
+		var fullScreenMenu = this.menu.getItemAt( 2 ).getSubmenu().getItemAt( 1 );
+
+		pos = Ti.JSON.parse( pos );
+		if ( ! pos ) return;
+
+		if ( pos.fullscreen ) {
+			w.setFullscreen( true );
+			hiddenMenu.disable();
+		}
+		else {
+
+			console.log( "Moving to x:" + pos.x + ", y:" + pos.y );
+			console.log( "Sizing to w:" + pos.width + ", h:" + pos.height );
+			w.moveTo(  pos.x, pos.y );
+			w.setSize( pos.width, pos.height );
+
+			hiddenMenu.enable();
+
+			if ( pos.hidden ) {
+				w.hide();
+				fullScreenMenu.disable();
 			}
 			else {
-				D.notify( 'Unable to connect.', 'Not a Thingfish server?' );
+				w.show();
+				fullScreenMenu.enable();
 			}
-		};
+		}
 
-		$.ajax({
-			url:      uri,
-			sync:     false,
-			success:  serverOk,
-			error:    function( xhr, status, err ) {
-				D.notify( 'Unable to connect.', err );
-			},
-			dataType: 'json'
-		});
+		this.saveWindowState();
 	},
 
 
 	// Build the main menu.
 	//
-	setupMenu: function() {
-		var menu  = Ti.UI.createMenu();
-		var yep   = Ti.UI.createMenuItem( 'Yep' );
+	initMenu: function() {
+		this.menu = Ti.UI.createMenu();
+		var tray  = Ti.UI.addTray( 'app://img/tray_icon.png' );
 
-		yep.addItem( 'About', function() {
+		// windows
+		// ---------------------------------------------------------------
+		this.menu.addItem( 'About', function() {
 			if ( D.window.about && D.window.about.isVisible() ) {
 				D.window.about.focus();
 			}
@@ -196,7 +347,7 @@
 			}
 		});
 
-		yep.addItem( 'Preferences', function() {
+		this.menu.addItem( 'Preferences', function() {
 			if ( D.window.prefs && D.window.prefs.isVisible() ) {
 				D.window.prefs.focus();
 			}
@@ -206,12 +357,39 @@
 			}
 		});
 
-		yep.addItem( 'Toggle Full Screen', function() {
-			D.window.main.setFullscreen( ! D.window.main.isFullscreen() );
+		// view menu
+		// ---------------------------------------------------------------
+		var viewMenu = Ti.UI.createMenuItem( 'View' );
+
+		viewMenu.addItem( 'Toggle Hidden', function() {
+			if ( D.window.main.isVisible() ) {
+				D.window.main.hide();
+			}
+			else {
+				D.window.main.show();
+			}
+			D.saveWindowState();
 		});
 
-		menu.appendItem( yep );
-		Ti.UI.setMenu( menu );
+		viewMenu.addItem( 'Toggle Full Screen', function() {
+			D.window.main.setFullscreen( ! D.window.main.isFullscreen() );
+			D.saveWindowState();
+		});
+
+		this.menu.appendItem( viewMenu );
+
+		// other
+		// --------------------------------------------------------------
+		if ( this.getBoolPref('devmode') ) {
+			this.menu.addItem( 'Restart', function() {
+				Ti.App.restart();
+			});
+	   }
+
+		tray.setMenu( this.menu );
+		Ti.UI.setDockMenu( this.menu );
+		// D.window.main.setContextMenu( this.menu );
+		// Ti.UI.setMenu( menu );
 	},
 
 
@@ -219,38 +397,31 @@
 	### M O D E L S
 	######################################################################*/
 
-	// A Thingfish asset.
-	//
-	initModel: function() {
-		D.Asset = can.Model({
-			id     : 'oid',
-			findAll: 'GET '    + D.tf.uri + '/',
-			findOne: 'GET '    + D.tf.uri + '/{oid}',
-			create : 'POST '   + D.tf.uri + '/',
-			update : 'PUT '    + D.tf.uri + '/{oid}',
-			destroy: 'DELETE ' + D.tf.uri + '/{oid}',
+	// // A Thingfish asset.
+	// //
+	// initModel: function() {
+	//     D.Asset = can.Model({
+	//         id     : 'oid',
+	//         findAll: 'GET '    + D.tf.uri + '/',
+	//         findOne: 'GET '    + D.tf.uri + '/{oid}',
+	//         create : 'POST '   + D.tf.uri + '/',
+	//         update : 'PUT '    + D.tf.uri + '/{oid}',
+	//         destroy: 'DELETE ' + D.tf.uri + '/{oid}',
 
-			// TODO: other metadata hooks?  diff model?
-			metadata: 'GET ' + D.tf.uri + '/{oid}/metadata',
+	//         // TODO: other metadata hooks?  diff model?
+	//         metadata: 'GET ' + D.tf.uri + '/{oid}/metadata',
 
-		}, {}).
-			bind( 'created', function( ev, asset ) {
-				console.debug( "Created a new asset " + asset.id );
-			}).
-			bind( 'updated', function( ev, asset ) {
-				console.debug( "Updated asset " + asset.id );
-			}).
-			bind( 'destroyed', function( ev, asset ) {
-				console.debug( "Removed asset " + asset.id );
-			}
-		);
-	},
+	//     }, {}).
+	//         bind( 'created', function( ev, asset ) {
+	//             console.debug( "Created a new asset " + asset.id );
+	//         }).
+	//         bind( 'updated', function( ev, asset ) {
+	//             console.debug( "Updated asset " + asset.id );
+	//         }).
+	//         bind( 'destroyed', function( ev, asset ) {
+	//             console.debug( "Removed asset " + asset.id );
+	//         }
+	//     );
+	// },
 };
 
-
-// Initialization.
-//
-D.window.main = Ti.UI.getMainWindow();
-D.setupMenu();
-D.initDB();
-
--- a/Resources/js/d/main.js	Mon Sep 02 02:22:21 2013 -0700
+++ b/Resources/js/d/main.js	Mon Sep 23 09:10:55 2013 -0700
@@ -1,43 +1,102 @@
+
+// Initialization.
+D.init();
+
 
 $( window ).ready( function() {
-	if ( D.getBoolPref('fullscreen') ) D.window.main.setFullscreen( true );
+
+	// FIXME: screwin around
+	var updateIdle = function() {
+		var idlesecs = Math.round( Ti.UI.getIdleTime() / 1000 );
+		Ti.UI.setBadge( idlesecs.toString() );
+		D.delay( updateIdle, 1000, true );
+	};
+	updateIdle();
+
+	// FIXME: more screwin around
+	$( '.thing' ).contextmenu( function( event ) {
+		var menu = Ti.UI.createMenu();
+		var val = $(this).data( 'value' );
+
+		menu.addItem( 'Whoa!', function() {
+			console.log( "This seems to have worked: " + val );
+		});
+
+		D.window.main.setContextMenu( menu );
+		event.stopPropagation();
+	});
+
+	$( '.save' ).click( function() {
+		var canvas = $( '#draw' )[0];
+		var img = canvas.toDataURL( "image/png" ).replace( 'data:image/png;base64,', '' );
+		img = window.atob( img );
+
+		D.window.main.openSaveAsDialog( function( path ) {
+			if ( path.length != 1 ) return;
+
+			var file = Ti.Filesystem.getFileStream( path[0] );
+			file.open( Ti.Filesystem.MODE_WRITE, true, false );
+			file.write( img );
+			file.close();
 
-	// If we're in development mode, just display the main window,
-	// the inpector, and stop here.
+			Processing.getInstanceById( 'draw' ).setup();
+		},
+		{
+			title: "Save image..",
+			path:  Ti.Filesystem.getDesktopDirectory().toString(),
+			types: [ 'png' ],
+			defaultFile: 'image.png'
+		});
+	});
+
+
+	// If we're in development mode, automatically display the inspector.
+	if ( D.devmode() ) D.window.main.showInspector( true );
+
+	// Make sure we're able to talk to the remote server.
 	//
-	if ( D.getBoolPref('devmode') ) {
-		D.window.main.showInspector( true );
-		return;
+	var remoteCheck = function() {
+		if ( D.tf.version ) return;  // all is well
+
+		// URI configured, but unable to get the version.
+		//
+		if ( D.tf.uri ) {
+			D.notify( 'Offline', 'Unable to talk to the Thingfish server!' );
+		}
+
+		// No URI configured.
+		//
+		else {
+			D.window.prefs = D.window.main.createWindow( 'app://window/prefs.html#server' );
+			D.window.prefs.open();
+			D.notify( 'Configuration needed.', 'Please point me to a Thingfish server URI!' );
+		}
+	};
+
+	// Skip the splash screen.
+	//
+	if ( D.getBoolPref('hidesplash') ) {
+		if ( D.getBoolPref('remember_window') ) D.setWindowState();
+		remoteCheck();
 	}
 
-	// "Regular" behavior.  Hide the main window, show splash.
+	// "Regular" behavior.  Hide the main window, show splash
+	// before continuing.
 	//
-	if ( ! D.getBoolPref('hidesplash') ) {
+	else {
 		D.window.main.hide();
 		D.window.splash = D.window.main.createWindow( 'app://window/splash.html' );
 		D.window.splash.setTopMost( true );
 		D.window.splash.open();
-	}
 
-	var server_uri = D.getPref( 'server_uri' );
-	if ( server_uri ) D.checkServer( server_uri );
-
-
-	// Show the main window after a few moments.
-	//
-	D.delay(function() {
-		if ( ! D.getBoolPref('hidesplash') ) {
+		// Show the main window after a few moments.
+		//
+		D.delay(function() {
+			D.window.splash.close();
 			D.window.main.show();
-			D.window.splash.close();
-		}
-
-		// Throw up the prefs window if this is the first time.
-		//
-		if ( D.firstrun ) {
-			D.window.prefs = D.window.main.createWindow( 'app://window/prefs.html#server' );
-			D.window.prefs.open();
-			D.notify( 'Initial setup', 'Please point me to a Thingfish server!' );
-		}
-	}, 3000 );
+			if ( D.getBoolPref('remember_window') ) D.setWindowState();
+			remoteCheck();
+		}, 3000, true );
+	}
 });
 
--- a/Resources/js/d/prefs.js	Mon Sep 02 02:22:21 2013 -0700
+++ b/Resources/js/d/prefs.js	Mon Sep 23 09:10:55 2013 -0700
@@ -1,3 +1,5 @@
+
+D = Ti.API.get( 'D' );
 
 $( window ).ready( function() {
 	new PrefsRouter( $('#content') );
@@ -9,7 +11,7 @@
 /* ---------------------------------------------- */
 PrefsRouter = can.Control({
 	defaults: {
-		menu: $( '#sections' ),
+		menu: '#sections',
 		default_route: 'app'
 	}
 }, {
@@ -26,12 +28,10 @@
 	reset: function() {
 		var self = this;
 		var current_route = can.route.attr( 'route' );
-		var menu = self.options.menu;
+		var menu = $( self.options.menu );
 
-		// FIXME
 		menu.find( 'a' ).removeClass( 'selected' );
-		console.log( menu.find( 'a' ) );
-		console.log( menu.find( 'a[href="#' + current_route + '"]' ) );
+		menu.find( 'a[href="#' + current_route + '"]' ).addClass( 'selected' );
 
 		while ( control = self.controls.pop() ) control.destroy();
 	},
@@ -62,11 +62,16 @@
 		// Get the current preference values.
 		//
 		var bool_prefs = {
-			hidesplash: [ D.getBoolPref( 'hidesplash' ), 'Skip splash screen at startup' ],
-			fullscreen: [ D.getBoolPref( 'fullscreen' ), 'Go full screen by default' ],
-			devmode:    [ D.getBoolPref( 'devmode' ), 'Developer mode' ]
+			hidesplash:      [ D.getBoolPref( 'hidesplash' ), 'Skip splash screen at startup' ],
+			remember_window: [ D.getBoolPref( 'remember_window' ), 'Remember main window position' ],
 		}
 
+		// Running from the builder env (or manually with --debug), include
+		// developer options.
+		//
+		if ( Ti.App.getArguments()[0] == "--debug" )
+			bool_prefs.devmode = [ D.getBoolPref( 'devmode' ), 'Developer mode' ];
+
 		self.element.html(
 			can.view( self.options.view, {
 				bool_prefs: bool_prefs
@@ -109,9 +114,19 @@
 	// Update the server URI preference.
 	//
 	updateServer: function( uri ) {
-		console.log( 'Updated server_uri to ' + uri + '.' );
-		D.setPref( 'server_uri', uri );
-		D.tf.uri = uri;
+		if ( uri.length == 0 ) return;
+		D.tf.attr( 'uri', uri );
+
+		if ( D.tf.version ) {
+			D.setPref( 'server_uri', uri );
+			console.log( 'Updated server_uri to ' + uri + '.' );
+			$( '#test .info' ).html( D.tf.version );
+			$( '#test .icon' ).html( '&#xf15d;' );
+		}
+		else {
+			$( '#test .info' ).empty();
+			$( '#test .icon' ).html( '&#xf15b;' );
+		}
 	},
 
 	// Hitting the return key on input fields defer to the 'blur' event,
@@ -121,21 +136,14 @@
 		if ( event.which == 13 ) ele.trigger( 'blur' );
 	},
 	'#server change': function( ele, event ) {
-		this.updateServer( ele.val() );
+		var self = this;
+		self.updateServer( ele.val() );
 	},
 	'#server keyup': function( ele, event ) {
+		var self = this;
 		D.delay( function() {
-			this.updateServer( ele.val() );
-		}, 500, true );
-	},
-
-
-	// Talk to the remote server, check to see if it's really
-	// a ThingFish handler or not.
-	//
-	'#test click': function( ele, event ) {
-		var server = $( '#server' ).val();
-		D.checkServer( server );
-	},
+			self.updateServer( ele.val() );
+		}, 1000, true );
+	}
 });
 
--- a/Resources/js/d/splash.js	Mon Sep 02 02:22:21 2013 -0700
+++ b/Resources/js/d/splash.js	Mon Sep 23 09:10:55 2013 -0700
@@ -1,3 +1,5 @@
+
+D = Ti.API.get( 'D' );
 
 $( window ).ready( function() {
 	$( '#name' ).html( D.name );
--- a/Resources/misc/blinky_squares.pjs	Mon Sep 02 02:22:21 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-
-void setup() {
-	size( 510, 510 );
-	smooth();
-
-	background( 200 );
-}
-
-void draw() {
-	if ( mousePressed ) {
-		rects(1);
-	}
-	else {
-		rects(0);
-	}
-}
-
-void rects( int rand ) {
-	int cwidth = 50;
-	int cheight = 50;
-
-	int curwidth = cwidth-(cwidth/5);
-	int curheight = cheight-(cwidth/5);
-
-	noStroke();
-
-	for ( int y = cheight/5; y <= height; y += cheight ) {
-		for ( int x = cwidth/5; x <= width; x += cwidth ) {
-			if ( rand == 1 ) {
-				fill( random(180,220) );
-			} 
-			else {
-				fill( 210 );
-			}
-
-			if ( (mouseX > x) && (mouseX < x + curwidth) &&
-					(mouseY > y) && (mouseY < y + curheight) ) { 
-
-				// mouse is inside rectangle
-			}
-			else {
-				rect( x, y, curwidth, curheight );
-			}
-		}
-	}
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/misc/draw.pjs	Mon Sep 23 09:10:55 2013 -0700
@@ -0,0 +1,14 @@
+
+void setup() {
+	size( 500, 500 );
+	background( 333 );
+}
+
+void draw()
+{
+	stroke( 255 );
+	if ( mousePressed == true ) {
+		line( mouseX, mouseY, pmouseX, pmouseY );
+	}
+}
+
--- a/Resources/window/about.html	Mon Sep 02 02:22:21 2013 -0700
+++ b/Resources/window/about.html	Mon Sep 23 09:10:55 2013 -0700
@@ -6,7 +6,6 @@
 		<link rel="stylesheet" type="text/css" href="app://css/main.css" />
 		<link rel="stylesheet" type="text/css" href="app://css/about.css" />
 		<script src="app://js/jquery-2.0.3.js" type="text/javascript" charset="utf-8"></script>
-		<script src="app://js/d/dascyllus.js" type="text/javascript" charset="utf-8"></script>
 		<script src="app://js/d/about.js" type="text/javascript" charset="utf-8"></script>
 	</head>
 	<body>
--- a/Resources/window/main.html	Mon Sep 02 02:22:21 2013 -0700
+++ b/Resources/window/main.html	Mon Sep 23 09:10:55 2013 -0700
@@ -20,10 +20,17 @@
 		<script src="app://js/d/main.js" type="text/javascript" charset="utf-8"></script>
 	</head>
 	<body>
-		<h1>HUH.</h1>
-		<p>That's right!</p>
+		<h1>Huh, lookit that.</h1>
+		<p>woo hoo!</p>
+
+		<progress></progress><br />
 
-		<canvas data-processing-sources="app://misc/blinky_squares.pjs"></canvas>  
+		<p class="thing" data-value="pork!">thing2</p>
+		<p class="thing" data-value="hooooray">thing2</p>
+
+		<canvas id="draw" data-processing-sources="app://misc/draw.pjs"></canvas>  
+		<br />
+		<input type="submit" class="button save" value="save" />
 	</body>
 </html>
 
--- a/Resources/window/prefs.html	Mon Sep 02 02:22:21 2013 -0700
+++ b/Resources/window/prefs.html	Mon Sep 23 09:10:55 2013 -0700
@@ -8,7 +8,6 @@
 		<link rel="stylesheet" type="text/css" href="app://css/prefs.css" />
 		<script src="app://js/jquery-2.0.3.js" type="text/javascript" charset="utf-8"></script>
 		<script src="app://js/can.jquery-1.1.6.js" type="text/javascript" charset="utf-8"></script>
-		<script src="app://js/d/dascyllus.js" type="text/javascript" charset="utf-8"></script>
 		<script src="app://js/d/prefs.js" type="text/javascript" charset="utf-8"></script>
 	</head>
 	<body>
--- a/Resources/window/prefs/servers.ejs	Mon Sep 02 02:22:21 2013 -0700
+++ b/Resources/window/prefs/servers.ejs	Mon Sep 23 09:10:55 2013 -0700
@@ -6,7 +6,10 @@
 		<input id="server" type="text" placeholder="(thingfish server uri)" data-field="server"
 			value="<%= server %>" autocomplete="off" />
 
-		<input id="test" class="button" type="submit" data-field="test" value="Test" />
+		<div id="test">
+			<span class="info"></span>
+			<span class="icon"></span>
+		</div>
 	</div>
 </fieldset>
 
--- a/Resources/window/splash.html	Mon Sep 02 02:22:21 2013 -0700
+++ b/Resources/window/splash.html	Mon Sep 23 09:10:55 2013 -0700
@@ -6,7 +6,6 @@
 		<link rel="stylesheet" type="text/css" href="app://css/main.css" />
 		<link rel="stylesheet" type="text/css" href="app://css/splash.css" />
 		<script src="app://js/jquery-2.0.3.js" type="text/javascript" charset="utf-8"></script>
-		<script src="app://js/d/dascyllus.js" type="text/javascript" charset="utf-8"></script>
 		<script src="app://js/d/splash.js" type="text/javascript" charset="utf-8"></script>
 	</head>
 	<body>
--- a/manifest	Mon Sep 02 02:22:21 2013 -0700
+++ b/manifest	Mon Sep 23 09:10:55 2013 -0700
@@ -1,8 +1,8 @@
 #appname:Dascyllus
 #appid:nu.martini.dascyllus
-#publisher:Mahlon E. Smith <mahlon@martini.nu>
+#publisher:Mahlon E. Smith
 #image:icon.png
-#url:http//projects.martini.nu/dascyllus
+#url:http//projects.martini.nu/dascyllus/
 #guid:3f5509eb-3861-41da-a715-4a0d6d3739fc
 #desc:A client for the Thingfish network media store.
 #type:desktop
--- a/tiapp.xml	Mon Sep 02 02:22:21 2013 -0700
+++ b/tiapp.xml	Mon Sep 23 09:10:55 2013 -0700
@@ -15,9 +15,9 @@
 		<title>Dascyllus</title>
 		<url>app://window/main.html</url>
 		<width>800</width>
-		<min-width>0</min-width>
+		<min-width>600</min-width>
 		<height>450</height>
-		<min-height>0</min-height>
+		<min-height>450</min-height>
 		<fullscreen>false</fullscreen>
 		<resizable>true</resizable>
 		<chrome scrollbars="true">true</chrome>