Pastie now auto-senses if line-wrap is a bad or good idea. Feedback?
## mark a section (Learn more)
<!DOCTYPE HTML> <html lang="en"> <head> <title>Harmony - Procedural drawing tool</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/> <script type="text/javascript" src="colorpicker/js/jquery.js"></script> <script type="text/javascript" src="colorpicker/js/colorpicker.js"></script> <script type="text/javascript" src="colorpicker/js/eye.js"></script> <script type="text/javascript" src="colorpicker/js/utils.js"></script> <script type="text/javascript" src="colorpicker/js/layout.js?ver=1.0.2"></script> <link rel="stylesheet" href="colorpicker/css/colorpicker.css" type="text/css" /> <link rel="stylesheet" media="screen" type="text/css" href="colorpicker/css/layout.css" /> <style type="text/css"> * { color: #444; font-family:Monospace; font-size:12px; } body { background-color: #fff; margin: 0px; overflow: hidden; } #menu { position: absolute; width: 400px; background-color: #eee; margin: auto; padding: 5px 10px; text-align: center; text-transform: uppercase; visibility: hidden; } input, select { text-transform: uppercase; text-align:center; } #colorBoxes{ float: left; left:20px; top:0px; display:none;} #wrap{ background-color: #cf0; z-index:50; position: absolute; left: 35px; top: 100px; visibility: hidden;} #container{z-index:-10;} </style> </head> <body> <!-- <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-86951-7']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga); })(); </script> <script type="text/javascript" charset="utf-8"> </script> --> <div id="menu"><a href="http://mrdoob.com/blog/post/689" target="_blank">Harmony</a> <select id="selector"></select> <input id="save" type="button" value="Save"><input id="clear" type="button" value="Clear"><div id="dash">Default influence</div> <div id="colorSelector"><div style="background-color: #0000ff"></div> <div id="colorBoxes"> <p><input type="text" maxlength="3" size="3" id="R" value="000" /></p> <p><input type="text" maxlength="3" size="3" id="G" value="000" /></p> <p><input type="text" maxlength="3" size="3" id="B" value="000" /></p> </div> </div> <p><input type="button" id="toggle" value="Hide Content"/></p> <p><label id="edit" for="eraserSize">Eraser Size :</label><input type="text" size="3" id="eraserSize" value="10" /></p> </div> <div id="overlay"> <div id="wrap"> <h1 class="title"> Testing in overlaying, canvas element. </h1> </div> </div> <div id="container"> </div> <script type="text/javascript"> // Sketchy, Shaded, Chrome, Fur, LongFur and Web are variations of the idea of connecting neightbour points // first implemented (afaik) by Ze Frank's The Scribbler (http://www.zefrank.com/scribbler/) // This code uses the MIT license: // http://www.opensource.org/licenses/mit-license.php var STYLES = ["eraser","sketchy", "shaded", "chrome", "fur", "longfur", "web", "", "squares", "ribbon", "", "circles", "grid"]; var SCREEN_WIDTH = window.innerWidth; var SCREEN_HEIGHT = window.innerHeight; var container, menu, selector; var saveButton, clearButton, toggleButton; var canvas, context; var style; var dash; var wrap; var mouseX = 0, mouseY = 0; var isMenuMouseOver = false, isMouseDown = false; //Marco: Keycapture stuff var _key = 0; var _influence = { sketch: { low: 250, medium: 1000, large: 4000, xl: 6000,def: 4000 } } function init() { dash = document.getElementById('dash'); container = document.getElementById('container'); wrap = document.getElementById('wrap'); wrap.style.visibility = 'hidden'; wrap.style.width = (SCREEN_WIDTH / 2) + 'px' ; wrap.style.left = ((SCREEN_WIDTH - wrap.offsetWidth) / 2) + 'px'; menu = document.getElementById('menu'); menu.style.left = ((SCREEN_WIDTH - menu.offsetWidth) / 2) + 'px'; menu.style.visibility = 'visible'; menu.addEventListener('mouseover', onMenuMouseOver, false); menu.addEventListener('mouseout', onMenuMouseOut, false); canvas = document.createElement("canvas"); canvas.width = SCREEN_WIDTH; canvas.height = SCREEN_HEIGHT; canvas.style.cursor = 'crosshair'; container.appendChild(canvas); context = canvas.getContext("2d"); context.fillStyle = "rgb(255, 255, 255)"; context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); saveButton = document.getElementById('save'); saveButton.addEventListener('click', save, false); clearButton = document.getElementById('clear'); clearButton.addEventListener('click', clear, false); selector = document.getElementById('selector'); selector.addEventListener('change', onSelectorChange, false); for (var i = 0; i < STYLES.length; i++) { var option = document.createElement("option"); option.id = i; option.innerHTML = STYLES[i].toUpperCase(); selector.appendChild(option); } if (window.location.hash) { var hash = window.location.hash.substr(1,window.location.hash.length); for (var i = 0; i < STYLES.length; i++) { if (hash == STYLES[i]) { style = eval("new " + STYLES[i] + "(context)"); selector.selectedIndex = i; break; } } } if (!style) { style = eval("new " + STYLES[0] + "(context)"); } document.onmousedown = onDocumentMouseDown; document.onmouseout = onCanvasMouseUp; //Marco: Key event document.onkeydown = KeyCheck; canvas.onmousedown = onCanvasMouseDown; canvas.onmouseup = onCanvasMouseUp; canvas.onmousemove = onCanvasMouseMove; canvas.ontouchstart = onCanvasTouchStart; canvas.ontouchend = onCanvasTouchEnd; canvas.ontouchmove = onCanvasTouchMove; } //Marco: Pass the pressed key to the global. function KeyCheck(e) { _key = e.keyCode; if(_key == "49") { dash.innerHTML = "low influence"; } if(_key == "50") { dash.innerHTML = "medium influence"; } if(_key == "51") { dash.innerHTML = "large influence"; } if(_key == "52") { dash.innerHTML = "XL influence"; } } function onDocumentMouseDown(e) { return isMenuMouseOver; } function onSelectorChange(e) { if (STYLES[selector.selectedIndex] == "") return; style.destroy(); style = eval("new " + STYLES[selector.selectedIndex] + "(context)"); window.location.hash = STYLES[selector.selectedIndex]; } function onMenuMouseOver(e) { isMenuMouseOver = true; } function onMenuMouseOut(e) { isMenuMouseOver = false; } function ondocumentMouseDown(e) { return !isMenuMouseOver; } function onCanvasMouseDown(e) { isMouseDown = true; style.strokeStart( mouseX, mouseY ); } function onCanvasMouseUp(e) { isMouseDown = false; style.strokeEnd( mouseX, mouseY ); } function onCanvasMouseMove(e) { if (!e) var e = window.event; mouseX = e.clientX; mouseY = e.clientY; if (!isMouseDown) return; style.stroke( mouseX, mouseY ); } function onCanvasTouchStart(e) { if(e.touches.length == 1) { var touch = e.touches[0]; style.strokeStart( touch.pageX, touch.pageY ); } return false; } function onCanvasTouchEnd(e) { if(e.touches.length == 1) { var touch = e.touches[0]; style.strokeEnd( touch.pageX, touch.pageY ); } } function onCanvasTouchMove(e) { if(e.touches.length == 1) { var touch = e.touches[0]; style.stroke( touch.pageX, touch.pageY ); } } function save() { window.open(canvas.toDataURL("image/png"),'mywindow'); } function clear() { var cR = $("#R").val(); var cG = $("#G").val(); var cB = $("#B").val(); context.globalCompositeOperation = 'source-over'; context.fillStyle = "rgb("+cR+","+cG+","+cB+")"; context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); style = eval("new " + STYLES[selector.selectedIndex] + "(context)"); points = new Array(); count = 0; } // SAVING MY POOR SERVER :S (These classes are supposed to be on external files) function chrome( context ) { this.init( context ); } chrome.prototype = { context: null, prevMouseX: null, prevMouseY: null, points: null, count: null, init: function( context ) { this.context = context; this.context.lineWidth = 1; if (RegExp(" AppleWebKit/").test(navigator.userAgent)) this.context.globalCompositeOperation = 'darker'; this.points = new Array(); this.count = 0; }, destroy: function() { }, strokeStart: function( mouseX, mouseY ) { this.prevMouseX = mouseX; this.prevMouseY = mouseY; }, stroke: function( mouseX, mouseY ) { var cR = $("#R").val(); var cG = $("#G").val(); var cB = $("#B").val(); this.points.push( [ mouseX, mouseY ] ); this.context.strokeStyle = "rgba("+cR+","+cG+","+cB+", 0.1)"; this.context.beginPath(); this.context.moveTo(this.prevMouseX, this.prevMouseY); this.context.lineTo(mouseX, mouseY); this.context.stroke(); for (var i = 0; i < this.points.length; i++) { var size = 0.2; var dx = this.points[i][0] - this.points[this.count][0]; var dy = this.points[i][1] - this.points[this.count][1]; var d = dx * dx + dy * dy; if (d < 1000) { this.context.strokeStyle = "rgba(" + Math.floor(Math.random() * 255) + ", " + Math.floor(Math.random() * 255) + ", " + Math.floor(Math.random() * 255) + ", 0.1 )"; this.context.beginPath(); this.context.moveTo( this.points[this.count][0] + (dx * size), this.points[this.count][1] + (dy * size)); this.context.lineTo( this.points[i][0] - (dx * size), this.points[i][1] - (dy * size)); this.context.stroke(); } } this.prevMouseX = mouseX; this.prevMouseY = mouseY; this.count ++; }, strokeEnd: function( mouseX, mouseY ) { } } function circles( context ) { this.init( context ); } circles.prototype = { context: null, prevMouseX: null, prevMouseY: null, points: null, count: null, init: function( context ) { this.context = context; this.context.lineWidth = 1; this.context.globalCompositeOperation = 'source-over'; this.points = new Array(); }, destroy: function() { }, strokeStart: function( mouseX, mouseY ) { this.prevMouseX = mouseX; this.prevMouseY = mouseY; }, stroke: function( mouseX, mouseY ) { this.points.push( [ mouseX, mouseY ] ); var dx = mouseX - this.prevMouseX; var dy = mouseY - this.prevMouseY; var d = Math.sqrt(dx * dx + dy * dy) * 2; var d_half = d / 2; var cx = Math.floor(mouseX / 100) * 100 + 50; var cy = Math.floor(mouseY / 100) * 100 + 50; var steps = Math.floor( Math.random() * 10 ); var step_delta = d / steps; for (var i = 0; i < steps; i++) { this.context.strokeStyle = "rgba(0, 0, 0, 0.1)"; this.context.beginPath(); this.context.arc( cx, cy, (steps - i) * step_delta, 0, Math.PI*2, true); this.context.stroke(); } this.prevMouseX = mouseX; this.prevMouseY = mouseY; }, strokeEnd: function( mouseX, mouseY ) { } } function fur( context ) { this.init( context ); } fur.prototype = { context: null, prevMouseX: null, prevMouseY: null, points: null, count: null, init: function( context ) { this.context = context; this.context.lineWidth = 1; this.points = new Array(); this.count = 0; }, destroy: function() { }, strokeStart: function( mouseX, mouseY ) { this.prevMouseX = mouseX; this.prevMouseY = mouseY; }, stroke: function( mouseX, mouseY ) { var cR = $("#R").val(); var cG = $("#G").val(); var cB = $("#B").val(); this.points.push( [ mouseX, mouseY ] ); this.context.strokeStyle = "rgba("+ cR +","+ cG +","+ cB +", 0.1)"; this.context.beginPath(); this.context.moveTo(this.prevMouseX, this.prevMouseY); this.context.lineTo(mouseX, mouseY); this.context.stroke(); for (var i = 0; i < this.points.length; i++) { var size = 0.5; var dx = this.points[i][0] - this.points[this.count][0]; var dy = this.points[i][1] - this.points[this.count][1]; var d = dx * dx + dy * dy; if (d < 2000 && Math.random() > d / 2000) { this.context.strokeStyle = "rgba("+ cR +","+ cG +","+ cB +", 0.1)"; this.context.beginPath(); this.context.moveTo( mouseX + (dx * size), mouseY + (dy * size)); this.context.lineTo( mouseX - (dx * size), mouseY - (dy * size)); this.context.stroke(); } } this.prevMouseX = mouseX; this.prevMouseY = mouseY; this.count ++; }, strokeEnd: function( mouseX, mouseY ) { } } function grid( context ) { this.init( context ); } grid.prototype = { context: null, init: function( context ) { this.context = context; this.context.lineWidth = 1; if (RegExp(" AppleWebKit/").test(navigator.userAgent)) this.context.globalCompositeOperation = 'darker'; this.context.strokeStyle = "rgba( 0, 0, 0, 0.01 )"; }, destroy: function() { }, strokeStart: function( mouseX, mouseY ) { }, stroke: function( mouseX, mouseY ) { var cx = Math.round(mouseX / 100) * 100; var cy = Math.round(mouseY / 100) * 100; var dx = (cx - mouseX) * 10; var dy = (cy - mouseY) * 10; for (var i = 0; i < 50; i++) { this.context.beginPath(); this.context.moveTo( cx, cy ); this.context.quadraticCurveTo(mouseX + Math.random() * dx, mouseY + Math.random() * dy, cx, cy); this.context.stroke(); } }, strokeEnd: function( mouseX, mouseY ) { } } function longfur( context ) { this.init( context ); } longfur.prototype = { context: null, points: null, count: null, init: function( context ) { var cR = $("#R").val(); var cG = $("#G").val(); var cB = $("#B").val(); this.context = context; this.context.lineWidth = 1; this.context.globalCompositeOperation = 'source-over'; this.context.strokeStyle = "rgba("+ cR +","+ cG +","+ cB +", 0.05 )"; this.points = new Array(); this.count = 0; }, destroy: function() { }, strokeStart: function( mouseX, mouseY ) { }, stroke: function( mouseX, mouseY ) { var cR = $("#R").val(); var cG = $("#G").val(); var cB = $("#B").val(); this.context.strokeStyle = "rgba("+ cR +","+ cG +","+ cB +", 0.05 )"; this.points.push( [ mouseX, mouseY ] ); for (var i = 0; i < this.points.length; i++) { var size = -Math.random(); var dx = this.points[i][0] - this.points[this.count][0]; var dy = this.points[i][1] - this.points[this.count][1]; var d = dx * dx + dy * dy; if (d < 4000 && Math.random() > d / 4000) { this.context.beginPath(); this.context.moveTo( this.points[this.count][0] + (dx * size), this.points[this.count][1] + (dy * size)); this.context.lineTo( this.points[i][0] - (dx * size) + Math.random() * 2, this.points[i][1] - (dy * size) + Math.random() * 2); this.context.stroke(); } } this.count ++; }, strokeEnd: function( mouseX, mouseY ) { } } function ribbon( context ) { this.init( context ); } ribbon.prototype = { context: null, mouseX: null, mouseY: null, painters: null, interval: null, init: function( context ) { this.context = context; this.context.lineWidth = 1; this.context.strokeStyle = "rgba( 0, 0, 0, 0.05 )"; this.context.globalCompositeOperation = 'source-over'; this.mouseX = SCREEN_WIDTH / 2; this.mouseY = SCREEN_HEIGHT / 2; this.painters = new Array(); for (var i = 0; i < 50; i++) { this.painters.push({ dx: SCREEN_WIDTH / 2, dy: SCREEN_HEIGHT / 2, ax: 0, ay: 0, div: 0.1, ease: Math.random() * 0.2 + 0.6 }); } this.isDrawing = false; this.interval = setInterval( bargs( function( _this ) { _this.update(); return false; }, this ), 1000/60 ); }, destroy: function() { clearInterval(this.interval); }, strokeStart: function( mouseX, mouseY ) { this.mouseX = mouseX; this.mouseY = mouseY for (var i = 0; i < this.painters.length; i++) { this.painters[i].dx = mouseX; this.painters[i].dy = mouseY; } this.shouldDraw = true; }, stroke: function( mouseX, mouseY ) { this.mouseX = mouseX; this.mouseY = mouseY; }, strokeEnd: function( mouseX, mouseY ) { }, update: function() { for (var i = 0; i < this.painters.length; i++) { this.context.beginPath(); this.context.moveTo(this.painters[i].dx, this.painters[i].dy); this.painters[i].dx -= this.painters[i].ax = (this.painters[i].ax + (this.painters[i].dx - this.mouseX) * this.painters[i].div) * this.painters[i].ease; this.painters[i].dy -= this.painters[i].ay = (this.painters[i].ay + (this.painters[i].dy - this.mouseY) * this.painters[i].div) * this.painters[i].ease; this.context.lineTo(this.painters[i].dx, this.painters[i].dy); this.context.stroke(); } } } function bargs( _fn ) { var args = []; for( var n = 1; n < arguments.length; n++ ) args.push( arguments[ n ] ); return function () { return _fn.apply( this, args ); }; } function shaded( context ) { this.init( context ); } shaded.prototype = { context: null, prevMouseX: null, prevMouseY: null, points: null, count: null, init: function( context ) { this.context = context; this.context.lineWidth = 1; this.context.globalCompositeOperation = 'source-over'; this.points = new Array(); this.count = 0; }, destroy: function() { }, strokeStart: function( mouseX, mouseY ) { this.prevMouseX = mouseX; this.prevMouseY = mouseY; }, stroke: function( mouseX, mouseY ) { var cR = $("#R").val(); var cG = $("#G").val(); var cB = $("#B").val(); this.points.push( [ mouseX, mouseY ] ); for (var i = 0; i < this.points.length; i++) { var size = 0.3; var dx = this.points[i][0] - this.points[this.count][0]; var dy = this.points[i][1] - this.points[this.count][1]; var d = dx * dx + dy * dy; if (d < 1000) { this.context.strokeStyle = "rgba("+ cR +","+cG+","+cB+", " + ((1 - (d / 1000)) * 0.1) + " )"; this.context.beginPath(); this.context.moveTo( this.points[this.count][0], this.points[this.count][1]); this.context.lineTo( this.points[i][0], this.points[i][1]); this.context.stroke(); } } this.prevMouseX = mouseX; this.prevMouseY = mouseY; this.count ++; }, strokeEnd: function( mouseX, mouseY ) { } } function sketchy( context ) { this.init( context ); } sketchy.prototype = { context: null, prevMouseX: null, prevMouseY: null, points: null, count: null, init: function( context ) { this.context = context; this.context.lineWidth = 1; this.context.globalCompositeOperation = 'source-over'; this.points = new Array(); this.count = 0; }, destroy: function() { }, strokeStart: function( mouseX, mouseY ) { this.prevMouseX = mouseX; this.prevMouseY = mouseY; }, stroke: function( mouseX, mouseY ) { var cR = $("#R").val(); var cG = $("#G").val(); var cB = $("#B").val(); this.points.push( [ mouseX, mouseY ] ); this.context.strokeStyle = "rgba("+cR+","+cG+","+cB+", 0.05)"; this.context.beginPath(); this.context.moveTo(this.prevMouseX, this.prevMouseY); this.context.lineTo(mouseX, mouseY); this.context.stroke(); this.context.strokeStyle = "rgba("+cR+","+cG+","+cB+", 0.05 )"; var inf = _influence.sketch.def; for (var i = 0; i < this.points.length; i++) { var dx = this.points[i][0] - this.points[this.count][0]; var dy = this.points[i][1] - this.points[this.count][1]; var d = dx * dx + dy * dy; //Marco: Just testing- need to integrate more. if (_key == 49) { inf = _influence.sketch.low; } if (_key == 50) { inf = _influence.sketch.medium;} if (_key == 51) { inf = _influence.sketch.large;} if (_key == 52) { inf = _influence.sketch.xl;} if (d < inf && Math.random() > d / 2000) { this.context.beginPath(); this.context.moveTo( this.points[this.count][0] + (dx * 0.3), this.points[this.count][1] + (dy * 0.3)); this.context.lineTo( this.points[i][0] - (dx * 0.3), this.points[i][1] - (dy * 0.3)); this.context.stroke(); } } this.prevMouseX = mouseX; this.prevMouseY = mouseY; this.count ++; }, strokeEnd: function( mouseX, mouseY ) { } } function squares( context ) { this.init( context ); } squares.prototype = { context: null, prevMouseX: null, prevMouseY: null, init: function( context ) { this.context = context; this.context.globalCompositeOperation = 'source-over'; this.context.strokeStyle = "rgba(0, 0, 0, 1)"; this.context.fillStyle = "rgba(255, 255, 255, 1)"; this.context.lineWidth = 1; }, destroy: function() { }, strokeStart: function( mouseX, mouseY ) { this.prevMouseX = mouseX; this.prevMouseY = mouseY; }, stroke: function( mouseX, mouseY ) { var dx = mouseX - this.prevMouseX; var dy = mouseY - this.prevMouseY; var angle = 1.57079633; var px = Math.cos(angle) * dx - Math.sin(angle) * dy; var py = Math.sin(angle) * dx + Math.cos(angle) * dy ; this.context.beginPath(); this.context.moveTo(this.prevMouseX - px, this.prevMouseY - py); this.context.lineTo(this.prevMouseX + px, this.prevMouseY + py); this.context.lineTo(mouseX + px, mouseY + py); this.context.lineTo(mouseX - px, mouseY - py); this.context.lineTo(this.prevMouseX - px, this.prevMouseY - py); this.context.fill(); this.context.stroke(); this.prevMouseX = mouseX; this.prevMouseY = mouseY; }, strokeEnd: function( mouseX, mouseY ) { } } function web( context ) { this.init( context ); } web.prototype = { context: null, prevMouseX: null, prevMouseY: null, points: null, count: null, init: function( context ) { this.context = context; this.context.lineWidth = 1; this.context.globalCompositeOperation = 'source-over'; this.points = new Array(); this.count = 0; }, destroy: function() { }, strokeStart: function( mouseX, mouseY ) { this.prevMouseX = mouseX; this.prevMouseY = mouseY; }, stroke: function( mouseX, mouseY ) { var cR = $("#R").val(); var cG = $("#G").val(); var cB = $("#B").val(); this.points.push( [ mouseX, mouseY ] ); this.context.strokeStyle = "rgba("+cR+","+cG+","+cB+", 0.5)"; this.context.beginPath(); this.context.moveTo(this.prevMouseX, this.prevMouseY); this.context.lineTo(mouseX, mouseY); this.context.stroke(); this.context.strokeStyle = "rgba("+cR+","+cG+","+cB+", 0.1)"; for (var i = 0; i < this.points.length; i++) { var dx = this.points[i][0] - this.points[this.count][0]; var dy = this.points[i][1] - this.points[this.count][1]; var d = dx * dx + dy * dy; if (d < 2500 && Math.random() > 0.9) { this.context.beginPath(); this.context.moveTo( this.points[this.count][0], this.points[this.count][1]); this.context.lineTo( this.points[i][0], this.points[i][1]); this.context.stroke(); } } this.prevMouseX = mouseX; this.prevMouseY = mouseY; this.count ++; }, strokeEnd: function( mouseX, mouseY ) { } } function eraser( context ) { this.init( context ); } eraser.prototype = { context: null, prevMouseX: null, prevMouseY: null, points: null, count: null, init: function( context ) { this.context = context; this.context.lineWidth = 1; this.points = new Array(); this.count = 0; }, destroy: function() { }, strokeStart: function( mouseX, mouseY ) { this.prevMouseX = mouseX; this.prevMouseY = mouseY; }, stroke: function( mouseX, mouseY ) { var eraserSize = $('#eraserSize').val(); var cR = $("#R").val(); var cG = $("#G").val(); var cB = $("#B").val(); this.points.push( [ mouseX, mouseY ] ); this.context.strokeStyle = "rgba("+ cR +","+ cG +","+ cB +", 0.5)"; this.context.lineWidth = eraserSize; this.context.lineCap = "round"; this.context.lineJoin = "round"; this.context.beginPath(); this.context.moveTo(this.prevMouseX, this.prevMouseY); this.context.lineTo(mouseX, mouseY); this.context.stroke(); this.prevMouseX = mouseX; this.prevMouseY = mouseY; this.count ++; }, strokeEnd: function( mouseX, mouseY ) { } } init(); // heh! </script> </body> </html>
This paste will be private.
From the Design Piracy series on my blog: