commit:7f476a519a8abeecf6c1e19e4e91635ea324d608
author:Chip Black
committer:Chip Black
date:Tue Aug 23 03:36:53 2011 -0500
parents:
Initial commit, v1.0.0
diff --git a/COPYING b/COPYING
line changes: +22/-0
index 0000000..840acd4
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,22 @@
+Copyright (c) 2011, The Dominion of Awesome
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

diff --git a/HacksSelectorCanvas.js b/HacksSelectorCanvas.js
line changes: +79/-0
index 0000000..6eb4405
--- /dev/null
+++ b/HacksSelectorCanvas.js
@@ -0,0 +1,79 @@
+/* Copyright 2011 The Dominion of Awesome
+ * See COPYING for licensing information */
+enyo.kind({
+	name: "HacksSelector",
+	kind: "Control",
+	nodeTag: "canvas",
+	className: "hacks-selector",
+	style: "opacity: 0",
+	width: "748px",
+	height: "192px",
+	index: 0,
+	published: {
+		hacksList: null
+	},
+	hacksThumbs: {},
+	rendered: function() {
+		this.inherited(arguments);
+		var node = this.hasNode();
+		node.width = 748;
+		node.height = 192;
+		this.ctx = node.getContext('2d');
+		this.ctx.fillStyle = 'white';
+		this.fadeGradient = this.ctx.createLinearGradient(0, 0, 0, 128);
+		this.fadeGradient.addColorStop(0.6, 'rgba(0,0,0,0)');
+		this.fadeGradient.addColorStop(1.0, 'rgba(255,255,255,0.5)');
+	},
+	setHacksList: function(newHacksList) {
+		this.hacksList = newHacksList;
+		for (var i = 0; i < this.hacksList.length; i++) {
+			if (this.hacksThumbs[this.hacksList[i]])
+				continue;
+			var img = new Image();
+			img.src = "hacks/" + this.hacksList[i] + "/thumbnail.png";
+			img.onload = this.draw.bind(this);
+			this.hacksThumbs[this.hacksList[i]] = img;
+		}
+	},
+	show: function() {
+		if (this.hideTimer)
+			clearTimeout(this.hideTimer);
+		this.hideTimer = setTimeout(function() {
+			this.setStyle('opacity: 0; -webkit-transition: opacity 0.5s linear');
+		}.bind(this), 1500);
+		this.setStyle('opacity: 1.0; -webkit-transition: opacity 0.25s linear');
+	},
+	draw: function() {
+		this.ctx.clearRect(0, 0, 750, 256);
+		if (!this.hacksList) return;
+		for (var i = -2; i <= 3; i++) {
+			var frac = this.index - Math.floor(this.index);
+			var x = 311 + (i - frac) * 146;
+			var j = Math.floor(this.index) + i;
+			if (j < 0 || j > this.hacksList.length - 1)
+			       continue;
+			var h = this.hacksList[j];
+
+			this.ctx.save();
+			this.ctx.translate(x, 0);
+			this.ctx.drawImage(this.hacksThumbs[h], 0, 0);
+			this.ctx.scale(1, -1);
+			this.ctx.translate(0, -256);
+			this.ctx.beginPath();
+			this.ctx.fillStyle = this.fadeGradient;
+			this.ctx.rect(0, 0, 128, 128);
+			this.ctx.fill();
+			this.ctx.globalCompositeOperation = 'source-in';
+			this.ctx.drawImage(this.hacksThumbs[h], 0, 0);
+			this.ctx.restore();
+
+			this.ctx.save();
+			this.ctx.shadowColor = 'white';
+			this.ctx.shadowBlur = '3';
+			this.ctx.beginPath();
+			this.ctx.arc(374, 160, 5, 0, Math.PI * 2);
+			this.ctx.fill();
+			this.ctx.restore();
+		}
+	}
+});

diff --git a/Main.css b/Main.css
line changes: +29/-0
index 0000000..1fe2ca5
--- /dev/null
+++ b/Main.css
@@ -0,0 +1,29 @@
+/* Copyright 2011 The Dominion of Awesome
+ * See COPYING for licensing information */
+@font-face {
+	font-family: PCSenior;
+	src: url(fonts/pcsenior.ttf);
+}
+
+.hacks-selector {
+	position: absolute;
+	bottom: 64px;
+	margin-left: -374px;
+	left: 50%;
+}
+
+.info {
+	position: absolute;
+	left: 0;
+	bottom: 0;
+	width: 100%;
+	padding: 24px;
+	font-size: 24px;
+	-webkit-transition-property: opacity;
+	-webkit-transition-duration: 0.6s;
+	color: #303030;
+}
+
+.info.dark {
+	color: #AFAFAf;
+}

diff --git a/Main.js b/Main.js
line changes: +139/-0
index 0000000..eb02da9
--- /dev/null
+++ b/Main.js
@@ -0,0 +1,139 @@
+/* Copyright 2011 The Dominion of Awesome
+ * See COPYING for licensing information */
+enyo.kind({
+	name: "Main",
+	kind: "VFlexBox",
+	style: "background-color: black",
+	components: [
+		{kind: "ApplicationEvents", onWindowActivated: "windowActivated", onWindowDeactivated: "windowDeactivated"},
+		{name: "hacksCarousel", kind: "Carousel", flex: 1, onGetLeft: "getLeft", onGetRight: "getRight", onScroll: "scrolling", onScrollStart: "startScroll", onScrollStop: "stopScroll"},
+		//{name: "hacksSelector", kind: "HacksSelector"}
+		{name: "info", kind: "HFlexBox", className: "info", style: "opacity: 0", showing: false, components: [
+			{name: "title"},
+			{kind: "Spacer"},
+			{name: "notice", className: "notice"}
+		]}
+	],
+	hacksList: [
+		{name: "Nimbus", kind: "Nimbus", dark: false},
+		{name: "Landscape", kind: "Landscape", dark: false},
+		{name: "Munch", kind: "Munch", dark: true},
+		{name: "Orbit", kind: "Orbit", dark: false},
+		{name: "Pixelfade", kind: "Pixelfade", dark: true},
+		//{name: "Swarm", kind: "Swarm", dark: false},       // crashy
+		//{name: "Spinner.CGA", kind: "XSpinnerCGA", dark: true}, // no webfont support
+		{name: "Spinner", kind: "XSpinner", dark: true}
+	],
+	create: function() {
+		this.inherited(arguments);
+		/*
+		this.index = localStorage.getItem('hack.index');
+		if (!this.index)
+			this.index = 0;
+		*/
+		this.index = 0;
+		this.lastScrollPos = 0;
+		this.$.hacksCarousel.setCenterView(this.getHack(this.index));
+		//this.$.hacksSelector.setHacksList(this.hacksList);
+	},
+	ready: function() {
+		this.startHack();
+		window.addEventListener('resize', this.resizeHack.bind(this), false);
+		this.setNotice('Swipe for more...');
+		this.setTitle(this.hacksList[this.index].name);
+	},
+	setTitle: function(title) {
+		this.$.title.setContent(title);
+		this.infoFade();
+	},
+	setNotice: function(notice) {
+		this.$.notice.setContent(notice);
+		this.infoFade();
+	},
+	infoFade: function() {
+		this.$.info.setStyle('opacity: 1');
+		this.$.info.show();
+
+		clearTimeout(this.noticeTimer);
+		this.noticeTimer = setTimeout(function() {
+			this.$.info.setStyle('opacity: 0');
+			setTimeout(function() {
+				this.$.info.hide();
+				this.setNotice('');
+				this.setTitle('');
+			}.bind(this), 600);
+		}.bind(this), 5000);
+	},
+	windowActivated: function() {
+		this.startHack();
+	},
+	windowDeactivated: function() {
+		this.stopHack();
+	},
+	startHack: function(direction) {
+		var view = this.$.hacksCarousel.fetchView(direction || 'center');
+		//enyo.log('starting view ' + view);
+		if (view)
+			view.start();
+		//localStorage.setItem('hack.index', this.index);
+	},
+	stopHack: function(direction) {
+		var view = this.$.hacksCarousel.fetchView(direction || 'center');
+		//enyo.log('stopping view ' + view);
+		if (view)
+			view.stop();
+	},
+	resizeHack: function() {
+		var view = this.$.hacksCarousel.fetchView('center');
+
+		//enyo.log('resizing view ' + view);
+		if (view)
+			view.resize(window.innerWidth, window.innerHeight);
+	},
+	hackHidden: function(direction) {
+		var view = this.$.hacksCarousel.fetchView(direction);
+		//enyo.log('view ' + view + ' hidden');
+		if (view)
+			view.hidden();
+	},
+	getHack: function(index) {
+		return {kind: this.hacksList[index].kind};
+	},
+	getLeft: function(inSender, inSnap) {
+		if (inSnap && this.index > 0) {
+			this.index--;
+			this.hackHidden('right');
+			this.setTitle(this.hacksList[this.index].name);
+			this.$.info.addRemoveClass('dark', this.hacksList[this.index].dark);
+		}
+		if (this.index == 0)
+			return null;
+		else
+			return this.getHack(this.index - 1);
+	},
+	getRight: function(inSender, inSnap) {
+		if (inSnap && this.index < this.hacksList.length) {
+			this.index++;
+			this.hackHidden('left');
+			this.setTitle(this.hacksList[this.index].name);
+			this.$.info.addRemoveClass('dark', this.hacksList[this.index].dark);
+		}
+		if (this.index == this.hacksList.length - 1)
+			return null;
+		else
+			return this.getHack(this.index + 1);
+	},
+	scrolling: function(inSender) {
+		if (inSender.scrollLeft == 0 || inSender.scrollLeft == inSender.getBoundaries().right)
+			this.startHack();
+		//this.$.hacksSelector.index = this.index + ((inSender.scrollLeft - this.lastScrollPos) / window.innerWidth);
+		//this.$.hacksSelector.draw();
+	},
+	startScroll: function(inSender) {
+		this.stopHack();
+	},
+	stopScroll: function(inSender) {
+		this.lastScrollPos = this.$.hacksCarousel.scrollLeft;
+		this.startHack();
+	}
+});

diff --git a/appinfo.json b/appinfo.json
line changes: +11/-0
index 0000000..7c95063
--- /dev/null
+++ b/appinfo.json
@@ -0,0 +1,11 @@
+{
+	"id": "com.dominionofawesome.hacks",
+	"version": "1.0.0",
+	"vendor": "The Dominion of Awesome",
+	"type": "web",
+	"main": "index.html",
+	"title": "Hacks!",
+	"icon": "icon.png",
+	"uiRevision": 2,
+	"dockMode": true
+}

diff --git a/depends.js b/depends.js
line changes: +14/-0
index 0000000..6e775a3
--- /dev/null
+++ b/depends.js
@@ -0,0 +1,14 @@
+enyo.depends(
+	"Main.js",
+	"Main.css",
+	//"HacksSelector.js",
+	"hacks/Hack.js",
+	"hacks/Nimbus/Nimbus.js",
+	"hacks/Landscape/Landscape.js",
+	"hacks/Munch/Munch.js",
+	"hacks/Orbit/Orbit.js",
+	"hacks/Pixelfade/Pixelfade.js",
+	//"hacks/Swarm/Swarm.js",
+	//"hacks/XSpinnerCGA/XSpinnerCGA.js",
+	"hacks/XSpinner/XSpinner.js"
+);

diff --git a/hacks/Hack.js b/hacks/Hack.js
line changes: +14/-0
index 0000000..28cfad7
--- /dev/null
+++ b/hacks/Hack.js
@@ -0,0 +1,14 @@
+/* Copyright 2011 The Dominion of Awesome
+ * See COPYING for licensing information */
+enyo.kind({
+	name: "Hack",
+	kind: "VFlexBox",
+	stop: function() {
+	},
+	start: function() {
+	},
+	resize: function() {
+	},
+	hidden: function() {
+	}
+});

diff --git a/hacks/Landscape/Landscape.js b/hacks/Landscape/Landscape.js
line changes: +33/-0
index 0000000..590cb2c
--- /dev/null
+++ b/hacks/Landscape/Landscape.js
@@ -0,0 +1,33 @@
+/* Copyright 2011 The Dominion of Awesome
+ * See COPYING for licensing information */
+enyo.kind({
+	name: "Landscape",
+	kind: "Hack",
+	style: "background-color: #2ab40f",
+	components: [
+		{name: "clouds", style: "height: 200px; background-image: url(hacks/landscape/clouds.png)"},
+		{name: "mountains", style: "height: 200px; background-image: url(hacks/landscape/mountains.png)"},
+		{name: "trees", style: "height: 200px; background-image: url(hacks/landscape/trees.png)"}
+	],
+	create: function() {
+		this.inherited(arguments);
+		this.n = 0;
+	},
+	start: function() {
+		if (!this.timer)
+			this.timer = setInterval(this.draw.bind(this), 33);
+	},
+	stop: function() {
+		clearInterval(this.timer);
+		this.timer = null;
+	},
+	draw: function() {
+		this.n--;
+		if (this.n == -1600)
+		       this.n = 0;
+
+		this.$.clouds.applyStyle("background-position", (this.n * 1.5) + "px top");
+		this.$.mountains.applyStyle("background-position", (this.n * 2.5) + "px top");
+		this.$.trees.applyStyle("background-position", (this.n * 7.5) + "px top");
+	}
+});

diff --git a/hacks/Landscape/clouds.png b/hacks/Landscape/clouds.png
line changes: +0/-0
index 0000000..7c7fe85
--- /dev/null
+++ b/hacks/Landscape/clouds.png

diff --git a/hacks/Landscape/mountains.png b/hacks/Landscape/mountains.png
line changes: +0/-0
index 0000000..99a5a51
--- /dev/null
+++ b/hacks/Landscape/mountains.png

diff --git a/hacks/Landscape/trees.png b/hacks/Landscape/trees.png
line changes: +0/-0
index 0000000..c395192
--- /dev/null
+++ b/hacks/Landscape/trees.png

diff --git a/hacks/Munch/Munch.js b/hacks/Munch/Munch.js
line changes: +49/-0
index 0000000..2acc90a
--- /dev/null
+++ b/hacks/Munch/Munch.js
@@ -0,0 +1,49 @@
+/* Copyright 2011 The Dominion of Awesome
+ * See COPYING for licensing information */
+enyo.kind({
+	name: 'Munch',
+	kind: 'Hack',
+	pack: "center",
+	align: "center",
+	style: "background-color: black",
+	components: [
+		{kind: enyo.Control, nodeTag: "canvas", name: "display", width: "512px", height: "512px"}
+	],
+
+	t: 0,
+	timer: null,
+
+	rendered: function() {
+		var node = this.$.display.hasNode();
+		node.width = 256;
+		node.height = 256;
+		this.ctx = node.getContext('2d');
+		this.ctx.fillStyle = 'black';
+		this.ctx.fillRect(0, 0, 255, 255);
+		this.ctx.fillStyle = 'rgba(0,0,0,0.075)';
+		this.ctx.strokeStyle = '#00FF00';
+		this.draw();
+	},
+
+	draw: function() {
+		this.ctx.fillRect(0, 0, 256, 256);
+		this.ctx.beginPath();
+		for (var x = 0; x < 256; x++) {
+			var y = x ^ this.t;
+			this.ctx.moveTo(x, y);
+			this.ctx.lineTo(x+1, y+1);
+		}
+		this.ctx.stroke();
+		this.t = (this.t + 1) % 255;
+	},
+
+	start: function() {
+		if (this.timer) return;
+		this.timer = setInterval(this.draw.bind(this), 20);
+	},
+
+	stop: function() {
+		clearInterval(this.timer);
+		this.timer = null;
+	}
+});

diff --git a/hacks/Nimbus/Nimbus.js b/hacks/Nimbus/Nimbus.js
line changes: +85/-0
index 0000000..f8ed719
--- /dev/null
+++ b/hacks/Nimbus/Nimbus.js
@@ -0,0 +1,85 @@
+/* Copyright 2011 The Dominion of Awesome
+ * See COPYING for licensing information */
+enyo.kind({
+	name: "Drop",
+	constructor: function() {
+		this.live = false;
+	},
+	start: function(w, h, perspective) {
+		this.x = Math.random() * w;
+		this.y = (Math.random() * h) / perspective;
+		this.t = (new Date()).getTime();
+		this.live = true;
+	},
+	draw: function(ctx, t) {
+		var dt = t - this.t;
+		if (dt < 100) {
+			ctx.save();
+			ctx.globalAlpha = 0.25;
+			ctx.beginPath();
+			ctx.moveTo(this.x, 0);
+			ctx.lineTo(this.x, this.y);
+			ctx.stroke();
+			ctx.restore();
+		} else if (dt < 750) {
+			ctx.save();
+			ctx.globalAlpha = (1.0 - ((dt - 500) / 250));
+			ctx.beginPath();
+			ctx.arc(this.x, this.y, Math.sqrt(dt) * 5, 0, 2 * Math.PI, false);
+			ctx.stroke();
+			ctx.restore();
+		} else {
+			this.live = false;
+		}
+	}
+});
+
+enyo.kind({
+	name: "Nimbus",
+	kind: "Hack",
+	style: "background-color: white",
+	drop_c: 0,
+	drops: [],
+	perspective: 0.3,
+	components: [
+		{name: "raindrops", nodeTag: "canvas"}
+	],
+	rendered: function() {
+		this.raindrops = this.$.raindrops.hasNode();
+		this.ctx = this.raindrops.getContext('2d');
+		this.resize(window.innerWidth, window.innerHeight);
+
+		for (var i = 0; i < 100; i++)
+			this.drops.push(new Drop());
+	},
+	start: function() {
+		if (!this.timer)
+			this.timer = setInterval(this.draw.bind(this), 50);
+	},
+	stop: function() {
+		clearInterval(this.timer);
+		this.timer = null;
+	},
+	resize: function(w, h) {
+		this.raindrops.width = this.w = w;
+		this.raindrops.height = this.h = h;
+
+		this.ctx.fillStyle = 'rgba(255,255,255,0.7)';
+		this.ctx.strokeStyle = 'rgb(128,128,128)';
+		this.ctx.lineWidth = '2';
+	},
+	draw: function() {
+		if (Math.random() > 0.3) {
+			this.drops[this.drop_c].start(this.w, this.h, this.perspective);
+			this.drop_c = (this.drop_c + 1) % 100;
+		}
+		var now = (new Date()).getTime();
+		this.ctx.fillRect(0, 0, this.w, this.h);
+		this.ctx.save();
+		this.ctx.scale(1.0, this.perspective);
+		for (var i = 0; i < this.drops.length; i++)
+			if (this.drops[i].live)
+				this.drops[i].draw(this.ctx, now);
+		this.ctx.restore();
+	}
+});

diff --git a/hacks/Orbit/Orbit.js b/hacks/Orbit/Orbit.js
line changes: +65/-0
index 0000000..0bc7fac
--- /dev/null
+++ b/hacks/Orbit/Orbit.js
@@ -0,0 +1,65 @@
+/* Copyright 2011 The Dominion of Awesome
+ * See COPYING for licensing information */
+enyo.kind({
+	name: "Orbit",
+	kind: "Hack",
+	pack: "center",
+	align: "center",
+	style: "background-color: lightgray",
+	components: [
+		{kind: "Control", nodeTag: "canvas", name: "canvas", width: "250px", height: "250px"}
+	],
+	rendered: function() {
+		var canvas = this.$.canvas.hasNode();
+		canvas.width = 250;
+		canvas.height = 250;
+		this.context = canvas.getContext('2d');
+
+		this.center_x = canvas.width / 2;
+		this.center_y = canvas.height / 2;
+		this.radius = 100;
+		this.zr = 0;
+		this.xr = 0;
+
+		this.chromeball = new Image();
+		this.chromeball.onload = this.setReady.bind(this);
+		this.chromeball.src = "hacks/orbit/chrome_ball.png";
+	},
+	setReady: function() {
+		this.ready = true;
+		this.engine();
+	},
+	start: function() {
+		if (this.ready && !this.timer)
+			this.timer = setInterval(this.engine.bind(this), 20);
+	},
+	stop: function() {
+		clearInterval(this.timer);
+		this.timer = null;
+	},
+	engine: function() {
+		this.context.clearRect(this.center_x - this.radius - 25, this.center_y - this.radius - 25, this.radius * 2 + 50, this.radius * 2 + 50);
+		this.zr += 0.1;
+		this.xr += 0.05;
+		var objects = [];
+		for (var i = 0; i < 8; i++) {
+			var czr = this.zr + i * Math.PI/4;
+			var cxr = this.xr + Math.PI + i * Math.PI/4;
+			var cx = this.center_x + this.radius * Math.sin(czr);
+			var cy = this.center_y + this.radius * Math.cos(czr) * Math.cos(this.xr);
+			var r = 20 + 10 * Math.cos(czr) * Math.cos(this.xr + Math.PI/2);
+
+			objects.push({
+				x: cx - r,
+				y: cy - r,
+				s: r * 2
+			});
+		}
+		// Sort by size to simulate z-ordering
+		objects.sort(function(a,b) {
+			return a.s - b.s;
+		});
+		for (var i = 0; i < 8; i++)
+			this.context.drawImage(this.chromeball, objects[i].x, objects[i].y, objects[i].s, objects[i].s);
+	}
+});

diff --git a/hacks/Orbit/chrome_ball.png b/hacks/Orbit/chrome_ball.png
line changes: +0/-0
index 0000000..6614330
--- /dev/null
+++ b/hacks/Orbit/chrome_ball.png

diff --git a/hacks/Pixelfade/Pixelfade.js b/hacks/Pixelfade/Pixelfade.js
line changes: +148/-0
index 0000000..b91d5d2
--- /dev/null
+++ b/hacks/Pixelfade/Pixelfade.js
@@ -0,0 +1,148 @@
+/* Copyright 2011 The Dominion of Awesome
+ * See COPYING for licensing information */
+enyo.kind({
+	name: "Pixelfade",
+	kind: "Hack",
+	style: "background-color: yellow; background-position: center center",
+	components: [
+		{name: "canvas", nodeTag: "canvas", onclick: "canvasClick"},
+		{name: "wallpaperService", kind: "PalmService",
+		 service: 'palm://com.palm.systemservice',
+		 method: 'getPreferences',
+		 onSuccess: "gotWallpaper"}
+	],
+	create: function() {
+		this.inherited(arguments);
+		this.$.wallpaperService.call({
+			keys: ['wallpaper'],
+			subscribe: false
+		});
+	},
+	rendered: function() {
+		this.canvas = this.$.canvas.hasNode();
+		this.context = this.canvas.getContext('2d');
+		this.resize(window.innerWidth, window.innerHeight);
+		this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
+
+		this.faders = [];
+		for (var i = 0; i < 10; i++) {
+			var img = new Image();
+			img.src = "hacks/Pixelfade/pixelfade" + i + ".gif";
+			this.faders.push(img);
+		}
+	},
+	start: function() {
+		this.resize(window.innerWidth, window.innerHeight);
+
+		if (this.timer)
+			clearInterval(this.timer);
+		this.fade_val = 9;
+		this.fade_dir = -1;
+		this.fade_end = -1;
+		this.timer = setInterval(this.fade_all.bind(this), 50);
+
+		if (!this.spotTimer)
+			this.spotTimer = setInterval(this.do_fade_spot.bind(this), 3000);
+	},
+	stop: function() {
+		clearInterval(this.timer);
+		this.timer = null;
+		clearInterval(this.spotTimer);
+		this.spotTimer = null;
+		clearTimeout(this.fadeInTimer);
+		this.fadeInTimer = null;
+	},
+	resize: function(w, h) {
+		this.canvas.style.width = w + 'px';
+		this.canvas.style.height = h + 'px';
+		this.canvas.width = w;
+		this.canvas.height = h;
+		this.context.fillColor = 'black';
+		this.context.fillRect(0, 0, w, h);
+	},
+	hidden: function() {
+		this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
+	},
+	canvasClick: function(inSender, inEvent) {
+		clearTimeout(this.fadeInTimer);
+		clearInterval(this.spotTimer);
+		this.spotTimer = setInterval(this.do_fade_spot.bind(this), 3000);
+		this.fader_go(inEvent.clientX, inEvent.clientY);
+	},
+	gotWallpaper: function(inSender, inResponse) {
+		enyo.log("got background: " + JSON.stringify(inResponse));
+		this.setStyle('background-image: url(' + inResponse.wallpaper.wallpaperFile + '); background-alignment: center center');
+	},
+	do_fade_spot: function() {
+		var x = Math.floor(Math.random() * this.canvas.width);
+		var y = Math.floor(Math.random() * this.canvas.height);
+		this.fader_go(x, y);
+	},
+	fade_all: function() {
+		this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+		if (this.fade_val == this.fade_end) {
+			clearInterval(this.timer);
+			this.timer = null;
+			if (this.fade_dir == -1)
+				this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+			else
+				this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
+		} else {
+			for (var x = 0; x < this.canvas.width; x += 64) {
+				for (var y = 0; y < this.canvas.height; y += 64) {
+					this.context.drawImage(this.faders[this.fade_val], x, y);
+				}
+			}
+		}
+		this.fade_val += this.fade_dir;
+	},
+	fader_go: function(x, y) {
+		this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+		this.fader_count = 0;
+		this.time_faders(x, y);
+		this.faderptr = new Array(10);
+		for (var i=0; i < 10; i++)
+			this.faderptr[i] = 0;
+		this.t0 = (new Date()).getTime();
+		this.faderRunning = true;
+		clearInterval(this.timer);
+		this.timer = setInterval(this.fader.bind(this), 50);
+	},
+	time_faders: function(xpos, ypos) {
+		this.fadertimes = [];
+		for (var y = 0; y < this.canvas.height; y += 64) {
+			for (var x = 0; x < this.canvas.width; x += 64) {
+				var xd = x - xpos;
+				var yd = y - ypos;
+				this.fadertimes.push({
+					t: 125 - Math.sqrt(xd*xd + yd*yd) / 8,
+					x: x,
+					y: y
+				});
+			}
+		}
+		this.fadertimes.sort(function(a, b) { return a.t - b.t });
+	},
+	fader: function() {
+		var tnow = (new Date()).getTime();
+		var td = (tnow - this.t0) / 8.0;
+		for (var i = 0; i < 10; i++) {
+			while (this.faderptr[i] < this.fadertimes.length && this.fadertimes[this.faderptr[i]].t < td - i * 10) {
+				var xpos = this.fadertimes[this.faderptr[i]].x;
+				var ypos = this.fadertimes[this.faderptr[i]].y;
+				this.context.clearRect(xpos, ypos, 64, 64);
+				this.context.drawImage(this.faders[i], xpos, ypos);
+				this.faderptr[i]++;
+			}
+		}
+		if (this.faderptr[9] >= this.fadertimes.length) {
+			clearTimeout(this.timer);
+			this.fadeInTimer = setTimeout(function() {
+				this.fade_val = 9;
+				this.fade_dir = -1;
+				this.fade_end = -1;
+				this.timer = setInterval(this.fade_all.bind(this), 50);
+			}.bind(this), 750);
+		}
+	}
+});

diff --git a/hacks/Pixelfade/pixelfade0.gif b/hacks/Pixelfade/pixelfade0.gif
line changes: +0/-0
index 0000000..5521700
--- /dev/null
+++ b/hacks/Pixelfade/pixelfade0.gif

diff --git a/hacks/Pixelfade/pixelfade1.gif b/hacks/Pixelfade/pixelfade1.gif
line changes: +0/-0
index 0000000..f4dda0e
--- /dev/null
+++ b/hacks/Pixelfade/pixelfade1.gif

diff --git a/hacks/Pixelfade/pixelfade2.gif b/hacks/Pixelfade/pixelfade2.gif
line changes: +0/-0
index 0000000..20ef3ed
--- /dev/null
+++ b/hacks/Pixelfade/pixelfade2.gif

diff --git a/hacks/Pixelfade/pixelfade3.gif b/hacks/Pixelfade/pixelfade3.gif
line changes: +0/-0
index 0000000..d586808
--- /dev/null
+++ b/hacks/Pixelfade/pixelfade3.gif

diff --git a/hacks/Pixelfade/pixelfade4.gif b/hacks/Pixelfade/pixelfade4.gif
line changes: +0/-0
index 0000000..dd226d9
--- /dev/null
+++ b/hacks/Pixelfade/pixelfade4.gif

diff --git a/hacks/Pixelfade/pixelfade5.gif b/hacks/Pixelfade/pixelfade5.gif
line changes: +0/-0
index 0000000..c7f2eed
--- /dev/null
+++ b/hacks/Pixelfade/pixelfade5.gif

diff --git a/hacks/Pixelfade/pixelfade6.gif b/hacks/Pixelfade/pixelfade6.gif
line changes: +0/-0
index 0000000..c4633ce
--- /dev/null
+++ b/hacks/Pixelfade/pixelfade6.gif

diff --git a/hacks/Pixelfade/pixelfade7.gif b/hacks/Pixelfade/pixelfade7.gif
line changes: +0/-0
index 0000000..5bc10e1
--- /dev/null
+++ b/hacks/Pixelfade/pixelfade7.gif

diff --git a/hacks/Pixelfade/pixelfade8.gif b/hacks/Pixelfade/pixelfade8.gif
line changes: +0/-0
index 0000000..93cef76
--- /dev/null
+++ b/hacks/Pixelfade/pixelfade8.gif

diff --git a/hacks/Pixelfade/pixelfade9.gif b/hacks/Pixelfade/pixelfade9.gif
line changes: +0/-0
index 0000000..f72f9f9
--- /dev/null
+++ b/hacks/Pixelfade/pixelfade9.gif

diff --git a/hacks/Swarm/Swarm.js b/hacks/Swarm/Swarm.js
line changes: +137/-0
index 0000000..cfe7f32
--- /dev/null
+++ b/hacks/Swarm/Swarm.js
@@ -0,0 +1,137 @@
+/* Copyright 2011 The Dominion of Awesome
+ * See COPYING for licensing information */
+enyo.kind({
+	name: "Pixel",
+	constructor: function(canvas) {
+		this.x = Math.random() * canvas.width;
+		this.y = Math.random() * canvas.height;
+		this.vx = Math.random() * 20.0 - 10.0;
+		this.vy = Math.random() * 20.0 - 10.0;
+		this.lastupdate = (new Date()).getTime();
+	},
+	accel: function(x, y) {
+		this.vx += x;
+		if (this.vx > 200.0)
+			this.vx = 200.0;
+		else if (this.vx < -200.0)
+			this.vx = -200.0;
+		this.vy += y;
+		if (this.vy > 200.0)
+			this.vy = 200.0;
+		else if (this.vy < -200.0)
+			this.vy = -200.0;
+	},
+	update: function() {
+		var now = (new Date()).getTime();
+		var dt = (now - this.lastupdate) / 75.0;
+		this.x += this.vx * dt;
+		this.y += this.vy * dt;
+		this.lastupdate = now;
+	},
+	draw: function(ctx) {	
+		ctx.beginPath();
+		ctx.moveTo(this.x,this.y);
+		ctx.lineTo(this.x - this.vx / 4.0, this.y - this.vy / 4.0);
+		ctx.stroke();
+		ctx.closePath();
+	}	
+});
+
+enyo.kind({
+	name: "Swarm",
+	kind: "Hack",
+	align: "stretch",
+	pack: "stretch",
+	components: [
+		{name: "canvas", nodeTag: "canvas", style: "background-color: gray",
+		 onmousemove: "mouseMove", onmousedown: "mouseDown"}
+	],
+	npixels: 100,
+	pixels: [],
+	rendered: function() {
+		this.canvas = this.$.canvas.hasNode();
+		this.resize();
+		this.mx = this.canvas.width / 2;
+		this.my = this.canvas.width / 2;
+		for (var x = 0; x < this.npixels; x++)
+			this.pixels.push(new Pixel(this.canvas));
+		//this.stopTime = (new Date()).getTime();
+		this.engine();
+	},
+	start: function() {
+		if (this.stopTime) {
+			var delta = (new Date()).getTime() - this.stopTime;
+			for (var i = 0; i < this.npixels; i++)
+				this.pixels[i].lastupdate += delta;
+			this.stopTime = null;
+		}
+		if (!this.timer)
+			this.timer = setInterval(this.engine.bind(this), 20);
+	},
+	stop: function() {
+		this.stopTime = (new Date()).getTime();
+		clearInterval(this.timer);
+		this.timer = null;
+	},
+	resize: function(w, h) {
+		this.canvas.width = w;
+		this.canvas.height = h;
+		//this.canvas.style.width = w + 'px';
+		//this.canvas.style.height = h + 'px';
+		this.context = this.canvas.getContext('2d');
+		this.context.strokeStyle = '#000000';
+		this.context.lineCap = 'round';
+		this.context.lineWidth = 1.5;
+	},
+	mouseMove: function(inSender, inEvent) {
+		this.mx = inEvent.clientX;
+		this.my = inEvent.clientY;
+	},
+	mouseDown: function(inSender, inEvent) {
+		this.mouseMove(inSender, inEvent);
+		// Apply force to each pixel outwards from the mouse
+		for (var i = 0; i < this.npixels; i++) {
+			// Get the direction
+			var dx = this.pixels[i].x - inEvent.clientX;
+			var dy = this.pixels[i].y - inEvent.clientY;
+			var mag = Math.sqrt(dx*dx + dy*dy);
+			dx /= mag;
+			dy /= mag;
+
+			// Apply force.
+			this.pixels[i].accel(dx * 40.0, dy * 40.0);
+		}
+	},
+	engine: function() {
+		// Clear the canvas
+		this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+		// Update all pixel objects
+		for (var i = 0; i < this.npixels; i++) {
+			// Get distance
+			var dx = this.mx - this.pixels[i].x;
+			var dy = this.my - this.pixels[i].y;
+			var d = Math.sqrt(dx*dx + dy*dy);
+
+			// Apply acceleration to pixels towards the mouse cursor
+			var ax = dx / 50.0;
+			var ay = dy / 50.0;
+
+			// Apply a drag proportional to the distance from the cursor
+			if (d > 100.0) {
+				ax += -this.pixels[i].vx * (d - 100.0) / 2000.0;
+				ay += -this.pixels[i].vy * (d - 100.0) / 2000.0;
+			}
+
+			// And a "chaotic" acceleration inversely proportional
+			// to the distance from the cursor
+			ax += (Math.random() * 160.0 - 80.0) / d;
+			ay += (Math.random() * 160.0 - 80.0) / d;
+
+			this.pixels[i].accel(ax,ay);
+			this.pixels[i].update();
+			this.pixels[i].draw(this.context);
+		}
+
+		this.context.strokeRect(this.mx - 10, this.my - 10, 20, 20);
+	}
+});

diff --git a/hacks/XSpinner/XSpinner.js b/hacks/XSpinner/XSpinner.js
line changes: +80/-0
index 0000000..9fd0a33
--- /dev/null
+++ b/hacks/XSpinner/XSpinner.js
@@ -0,0 +1,80 @@
+/* Copyright 2011 The Dominion of Awesome
+ * See COPYING for licensing information */
+enyo.kind({
+	name: "XSpinner",
+	kind: "Hack",
+	style: "background-color: black; color: #00FF00; font-family: Courier, sans-serif; font-size: 12px",
+	pack: "center",
+	align: "center",
+	components: [
+		{style: "border: 2px solid green; padding: 4px", components: [
+			{name: "terminal", nodeTag: "pre", style: "margin: 0"},
+			{nodeTag: "pre", style: "margin: 0",
+content: "SESSION 1      80x24                                           CAPS NUM SCR <span style=\"color: black; background-color: #00FF00\" id=\"spinner_blinker\">RAVE</blink>"}
+		]}
+	],
+	create: function() {
+		this.inherited(arguments);
+		this.w = 80;
+		this.h = 24;
+		this.delay = 20;
+		this.rate = 360.0 / 1000.0;
+		this.cw = this.w / 2;
+		this.ch = this.h / 2;
+	},
+	rendered: function() {
+		this.terminal = this.$.terminal.hasNode();
+		this.blinker = document.getElementById('spinner_blinker');
+		this.blink_active = true;
+		this.spinner();
+	},
+	start: function() {
+		if (!this.timer) {
+			this.timer = setInterval(this.spinner.bind(this), this.delay);
+			this.blinkerTimer = setInterval(this.blink.bind(this), 500);
+		}
+	},
+	stop: function() {
+		clearInterval(this.timer);
+		clearInterval(this.blinkerTimer);
+		this.timer = null;
+		this.blinkerTimer = null;
+	},
+	spinner: function() {
+		// Get date
+		var ms = (new Date()).getMilliseconds();
+		var r = ms * this.rate;
+		var a = Math.tan((r / 180.0) * Math.PI);
+		var b = Math.tan(((r+90) / 180.0) * Math.PI);
+
+		var t = '';
+		for (var j=0; j < this.h; j++) {
+			for (var i=0; i < this.w; i++) {
+				if ((r > 90 && r <= 180) || (r > 270 && r < 360) || r == 0) {
+					if ((j-this.ch) * 2 > a * (i-this.cw) && (j-this.ch) * 2 < b * (i-this.cw) ||
+					    (j-this.ch) * 2 < a * (i-this.cw) && (j-this.ch) * 2 > b * (i-this.cw) ) {
+						t += '*';
+					} else {
+						t += ' ';
+					}
+				} else {
+					if ((j-this.ch) * 2 > a * (i-this.cw) && (j-this.ch) * 2 > b * (i-this.cw) ||
+					    (j-this.ch) * 2 < a * (i-this.cw) && (j-this.ch) * 2 < b * (i-this.cw) ) {
+						t += '*';
+					} else {
+						t += ' ';
+					}
+				}
+			}
+			t += "\n";
+		}
+		this.terminal.innerHTML = t;
+	},
+	blink: function() {
+		if (this.blink_active)
+			this.blinker.style.color = '#00FF00';
+		else
+			this.blinker.style.color = 'black';
+		this.blink_active = !this.blink_active;
+	}
+});

diff --git a/hacks/XSpinnerCGA/XSpinnerCGA.js b/hacks/XSpinnerCGA/XSpinnerCGA.js
line changes: +78/-0
index 0000000..9ff7634
--- /dev/null
+++ b/hacks/XSpinnerCGA/XSpinnerCGA.js
@@ -0,0 +1,78 @@
+/* Copyright 2011 The Dominion of Awesome
+ * See COPYING for licensing information */
+enyo.kind({
+	name: "XSpinnerCGA",
+	kind: "Hack",
+	style: "background-color: black",
+	pack: "center",
+	align: "center",
+	components: [
+		{style: "border: 2px solid green; padding: 4px", components: [
+			{name: "terminal", nodeTag: "pre", style: "margin: 0; font-family: PCSenior; font-size: 16px"}
+		]}
+	],
+	create: function() {
+		this.inherited(arguments);
+		this.w = 40;
+		this.h = 24;
+		this.delay = 20;
+		this.rate = 360.0 / 1000.0;
+		this.cw = this.w / 2;
+		this.ch = this.h / 2;
+	},
+	rendered: function() {
+		this.terminal = this.$.terminal.hasNode();
+		this.blinker = document.getElementById('spinner_blinker');
+		this.blink_active = true;
+		this.spinner();
+	},
+	start: function() {
+		if (!this.timer) {
+			this.timer = setInterval(this.spinner.bind(this), this.delay);
+			this.blinkerTimer = setInterval(this.blink.bind(this), 500);
+		}
+	},
+	stop: function() {
+		clearInterval(this.timer);
+		clearInterval(this.blinkerTimer);
+		this.timer = null;
+		this.blinkerTimer = null;
+	},
+	spinner: function() {
+		// Get date
+		var ms = (new Date()).getMilliseconds();
+		var r = ms * this.rate;
+		var a = Math.tan((r / 180.0) * Math.PI);
+		var b = Math.tan(((r+90) / 180.0) * Math.PI);
+
+		var t = '';
+		for (var j=0; j < this.h; j++) {
+			for (var i=0; i < this.w; i++) {
+				if ((r > 90 && r <= 180) || (r > 270 && r < 360) || r == 0) {
+					if ((j-this.ch) * 2 > a * (i-this.cw) && (j-this.ch) * 2 < b * (i-this.cw) ||
+					    (j-this.ch) * 2 < a * (i-this.cw) && (j-this.ch) * 2 > b * (i-this.cw) ) {
+						t += '*';
+					} else {
+						t += ' ';
+					}
+				} else {
+					if ((j-this.ch) * 2 > a * (i-this.cw) && (j-this.ch) * 2 > b * (i-this.cw) ||
+					    (j-this.ch) * 2 < a * (i-this.cw) && (j-this.ch) * 2 < b * (i-this.cw) ) {
+						t += '*';
+					} else {
+						t += ' ';
+					}
+				}
+			}
+			t += "\n";
+		}
+		this.terminal.innerHTML = t;
+	},
+	blink: function() {
+		if (this.blink_active)
+			this.blinker.style.color = '#00FF00';
+		else
+			this.blinker.style.color = 'black';
+		this.blink_active = !this.blink_active;
+	}
+});

diff --git a/icon.png b/icon.png
line changes: +0/-0
index 0000000..f5d77e6
--- /dev/null
+++ b/icon.png

diff --git a/icon_48.png b/icon_48.png
line changes: +0/-0
index 0000000..6a77b6a
--- /dev/null
+++ b/icon_48.png

diff --git a/index.html b/index.html
line changes: +14/-0
index 0000000..95db2f1
--- /dev/null
+++ b/index.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- Copyright 2011 The Dominion of Awesome
+     See COPYING for license information -->
+<html>
+<head>
+<title>Hacks!</title>
+<script src="/Program%20Files%20(x86)/HP%20webOS/SDK/share/refcode/webos-framework/enyo/1.0/framework/enyo.js" type="text/javascript"></script>
+</head>
+<body>
+<script type="text/javascript">
+var munch = enyo.create({kind: "Main"}).renderInto(document.body);
+</script>
+</body>
+</html>