<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"> <channel><title>meloncholy</title> <link>http://meloncholy.com</link> <description>Andrew Weeks</description> <lastBuildDate>Tue, 26 Feb 2013 10:40:38 +0000</lastBuildDate> <language>en-US</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.5.1</generator> <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/meloncholy" /><feedburner:info uri="meloncholy" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><title>Making responsive SVG graphs</title><link>http://feedproxy.google.com/~r/meloncholy/~3/WY9WRcIkTCA/</link> <comments>http://meloncholy.com/blog/making-responsive-svg-graphs/#comments</comments> <pubDate>Fri, 23 Nov 2012 23:11:10 +0000</pubDate> <dc:creator>Andrew</dc:creator> <category><![CDATA[Code]]></category> <category><![CDATA[CSS]]></category> <category><![CDATA[D3]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[SVG]]></category> <guid isPermaLink="false">http://meloncholy.com/?p=409</guid> <description><![CDATA[One of the (many) nice things about Twitter&#8217;s Bootstrap is its responsive layout. I&#8217;m using it for a new project alongside some D3, and I&#8217;d really like the graphs to resize with the grid. Unfortunately SVG elements don&#8217;t seem particularly &#8230; <a
class="more-link" href="http://meloncholy.com/blog/making-responsive-svg-graphs/">Keep&#160;reading&#160;<span
class="meta-nav">&#62;</span></a>]]></description> <content:encoded><![CDATA[<p>One of the (many) nice things about Twitter&#8217;s Bootstrap is its responsive layout. I&#8217;m using it for a new project alongside some D3, and I&#8217;d really like the graphs to resize with the grid. Unfortunately SVG elements don&#8217;t seem particularly amenable to this, and all the D3 demos I&#8217;ve seen have had a fixed width and height.</p><h2>Building a responsive graph</h2><p>A responsive SVG graph should behave like any other styled element</p><ul><li>Expand to fill the containing element (if required)</li><li>Keep line widths and text the same size and prevent distortion, no matter how big the graph becomes</li></ul><p><span
id="more-409"></span>SVG does <a
href="http://www.w3.org/TR/SVG/coords.html#Introduction">allow percentages</a> as a unit, but I haven&#8217;t personally seen them used much (at all, actually) and D3 uses px for its ranges.</p><p>Luckily SVG elements can also define their own viewports through the <a
href="http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute">viewBox attribute</a>, so its contents will always fill the same portion of the viewport, no matter how big or small to the outer element becomes (or whatever other transformations are applied to it).</p><div
id="attachment_410" class="wp-caption alignnone" style="width: 495px"><a
href="http://meloncholy.com/wordpress/wp-content/uploads/w3c-svg-viewbox-example.png"><img
src="http://meloncholy.com/wordpress/wp-content/uploads/w3c-svg-viewbox-example.png" alt="" title="W3C SVG viewBox example" width="485" height="300" class="size-full wp-image-410" /></a><p
class="wp-caption-text">W3C SVG viewBox example</p></div><p>(A related attribute is <a
href="http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute">preserveAspectRatio</a>, which I&#8217;ve set to <em>none</em> as I want the graph to fill the whole space. There are other options to force uniform scaling if you want your graph to maintain a fixed aspect ratio.)</p><pre>
&lt;svg viewBox="0 0 1000 1000" preserveAspectRatio="none"&gt;
&lt;g&gt;
&lt;!-- graph here --&gt;
&lt;/g&gt;
&lt;/svg&gt;
</pre><p><a
href="http://meloncholy.com/static/demos/responsive-svg-graph-0/">This demo graph</a> resizes nicely, but the text and line width can get horribly distorted &#8211; not really what I wanted.</p><h2>Preventing the distortions</h2><p>Graphical SVG elements support the <em>vector-effect</em> attribute, and setting this to <em>non-scaling-stroke</em> stops the lines from being affected by any transforms that have been applied. Unfortunately there isn&#8217;t an equivalent for text, but <a
href="http://phrogz.net/svg/libraries/SVGPanUnscale.js ">this JavaScript snippet</a> from Gavin Kistner can help here by undoing the damage. Just set it to run on load and hook it up to fire when the window resizes as well.</p><p>(I&#8217;ve actually changed the code slightly as it uses <a
href="http://www.w3.org/TR/2004/WD-SVG12-20041027/vectoreffects.html">getTransformToElement</a> to, um, get the transform from the containing SVG element to its contents, but the <a
href="http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined">transform matrix</a> returned by the function doesn&#8217;t take into account transforms applied to the SVG element itself. So I&#8217;ve put an SVG element (with a custom viewBox) inside the outer SVG element and referenced that in the JS instead.)</p><h2>Putting it all together</h2><p><a
href="http://meloncholy.com/static/demos/responsive-svg-graph-1/">Demo graph</a></p><pre>
&lt;svg&gt;
&lt;svg viewBox="0 0 1000 1000" preserveAspectRatio="none"&gt;
&lt;g&gt;
&lt;!-- graph here --&gt;
&lt;/g&gt;
&lt;/svg&gt;
&lt;/svg&gt;
</pre><pre>
.line {
  fill: none;
  stroke-width: 3px;
  vector-effect: non-scaling-stroke;
}
.area {
  vector-effect: non-scaling-stroke;
}
</pre><pre>
$(function () {
	"use strict";
	var resizeTracker;
	// Counteracts all transforms applied above an element.
	// Apply a translation to the element to have it remain at a local position
	var unscale = function (el) {
		var svg = el.ownerSVGElement.ownerSVGElement;
		var xf = el.scaleIndependentXForm;
		if (!xf) {
			// Keep a single transform matrix in the stack for fighting transformations
			xf = el.scaleIndependentXForm = svg.createSVGTransform();
			// Be sure to apply this transform after existing transforms (translate)
			el.transform.baseVal.appendItem(xf);
		}
		var m = svg.getTransformToElement(el.parentNode);
		m.e = m.f = 0; // Ignore (preserve) any translations done up to this point
		xf.setMatrix(m);
	};
	[].forEach.call($("text, .tick"), unscale);
	$(window).resize(function () {
		if (resizeTracker) clearTimeout(resizeTracker);
		resizeTracker = setTimeout(function () { [].forEach.call($("text, .tick"), unscale); }, 100);
	});
});
</pre><h2>Notes</h2><p>There&#8217;s also <a
href="http://www.w3.org/TR/SVGTiny12/coords.html#transform-ref ">transform ref()</a> that applies transforms to an element relative to a specified SVG element rather than its parent. This would be ideal as I could ditch the JS, but unfortunately Opera seems to be the only browser that supports it at the moment.</p><p>If you want sharper lines you can disable antialiasing with <em>shape-rendering=optimizeSpeed</em> or <em>shape-rendering=crispEdges</em>.</p><p>Two useful <a
href="http://stackoverflow.com/questions/1301685/fixed-stroke-width-in-svg">StackOverflow</a> <a
href="http://stackoverflow.com/questions/8880668/preserve-descendant-elements-size-while-scaling-the-parent-element">questions</a> on this.</p><p>The demo doesn&#8217;t seem to work with Opera or IE. I may look into it at some point.</p> ]]></content:encoded> <wfw:commentRss>http://meloncholy.com/blog/making-responsive-svg-graphs/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://meloncholy.com/blog/making-responsive-svg-graphs/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=making-responsive-svg-graphs</feedburner:origLink></item> <item><title>Emergence and networked products – Songkick presentation</title><link>http://feedproxy.google.com/~r/meloncholy/~3/VD0k8na-eII/</link> <comments>http://meloncholy.com/blog/emergence-and-networked-products-songkick-presentation/#comments</comments> <pubDate>Tue, 25 Sep 2012 18:06:47 +0000</pubDate> <dc:creator>Andrew</dc:creator> <category><![CDATA[Emergence]]></category> <category><![CDATA[Presentation]]></category> <guid isPermaLink="false">http://meloncholy.com/?p=397</guid> <description><![CDATA[I gave a presentation on emergence and networked products to Songkick today after Ian very kindly invited me along. It&#8217;s largely based on stuff I covered in my thesis, though I did speculate rather more here on how these ideas &#8230; <a
class="more-link" href="http://meloncholy.com/blog/emergence-and-networked-products-songkick-presentation/">Keep&#160;reading&#160;<span
class="meta-nav">&#62;</span></a>]]></description> <content:encoded><![CDATA[<p>I gave a presentation on emergence and networked products to <a
href="http://songkick.com/">Songkick</a> today after <a
href="http://soundboy.tumblr.com/">Ian</a> very kindly invited me along. It&#8217;s largely based on stuff I covered in <a
href="http://meloncholy.com/thesis">my thesis</a>, though I did speculate rather more here on how these ideas could be applied in the real world too (that bit is quite a lot more woolly).</p><h2>Overview</h2><ul><li>Shiny examples</li><li>Theories of emergence overview</li><li>My view of emergence and (briefly) what I did with it</li><li>Some ideas for engineering emergence: solve another problem</li><li>Speculative real world examples</li></ul><p>If you&#8217;d like to take a look, I&#8217;ve put the presentation <a
href="http://meloncholy.com/static/songkick-presentation">up here</a>.</p> ]]></content:encoded> <wfw:commentRss>http://meloncholy.com/blog/emergence-and-networked-products-songkick-presentation/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://meloncholy.com/blog/emergence-and-networked-products-songkick-presentation/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=emergence-and-networked-products-songkick-presentation</feedburner:origLink></item> <item><title>Using D3 for realtime webserver stats</title><link>http://feedproxy.google.com/~r/meloncholy/~3/ij3BlrMmzwc/</link> <comments>http://meloncholy.com/blog/using-d3-for-realtime-webserver-stats/#comments</comments> <pubDate>Wed, 22 Aug 2012 16:20:06 +0000</pubDate> <dc:creator>Andrew</dc:creator> <category><![CDATA[Backbone]]></category> <category><![CDATA[Code]]></category> <category><![CDATA[D3]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Node.js]]></category> <guid isPermaLink="false">http://meloncholy.com/?p=364</guid> <description><![CDATA[I wanted to learn a bit of D3, so I made a small web app that shows (nearly) realtime webserver statistics as pretty graphs. Here&#8217;s a demo for my server and a setup guide. Source on Github Source on Github &#8230; <a
class="more-link" href="http://meloncholy.com/blog/using-d3-for-realtime-webserver-stats/">Keep&#160;reading&#160;<span
class="meta-nav">&#62;</span></a>]]></description> <content:encoded><![CDATA[<p>I wanted to learn a bit of D3, so I made a small web app that shows (nearly) realtime webserver statistics as pretty graphs. Here&#8217;s a <a
href="http://bits.meloncholy.com/mt-stats">demo for my server</a> and a <a
href="http://meloncholy.com/blog/using-d3-for-realtime-webserver-stats/#setup-guide">setup guide</a>.</p><p><a
href="http://meloncholy.com/wordpress/wp-content/uploads/mt-stats-screenshot.jpg"><img
src="http://meloncholy.com/wordpress/wp-content/uploads/mt-stats-screenshot-thumb.jpg" alt="" title="(mt) Stats screenshot" width="580" height="317" class="alignnone size-full wp-image-378" /></a></p><p><a
href="https://github.com/meloncholy/mt-stats-viewer" class="source-link">Source on Github</a> <a
href="https://github.com/meloncholy/mt-stats" class="source-link">Source on Github</a><br
/> <i>(You need both, but the first will get the second via NPM.)</i></p><h2>Client code</h2><p><a
href="http://www.mediatemple.net/go/order/?refdom=meloncholy.com" rel="nofollow">MediaTemple</a> were nice enough to introduce <a
href="http://mediatemple.net/api/">an API</a> that provides a load of stats about what your server is doing some time ago. This is the same info that you can see in the server status bit of the account centre. Unfortunately the interface there is a bit clunky and it also logs you out (sensibly) after a few minutes&#8217; inactivity.</p><p>Inspired by some of the really cool stuff <a
href="http://bost.ocks.org/mike/">Mike Bostock</a> has done with <a
href="http://d3js.org/">D3</a>, (mt) Stats gives you the same info with a (hopefully) nicer interface, and maybe even doing quite well with <a
href="http://thedoublethink.com/2009/08/tufte%E2%80%99s-principles-for-visualizing-quantitative-information/">Tufte&#8217;s data-ink ratio</a>. You can add as many graphs as you like (if your CPU can take it at least), move them around by dragging and adjust the interval over which stats are shown with the range slider. <span
id="more-364"></span></p><p>If you hover over a graph, you should see buttons to change the metric for the graph and use a log or zero-based scale. On my server at least, I often end up with some large peaks in the <i>kb out</i> graph, which can hide a lot of much smaller bumps when only a few kb was transferred, so a log scale can be a useful way to see them more clearly. Similarly, certain metrics like memory usage can work better with (or without) a zero-based scale.</p><p>The app settings are stored in your browser&#8217;s <i>localStorage</i> (under <code>mt-stats</code>), so if you close the tab they should be there when you revisit the site. Though obviously not if you use a different browser or clear the cache.</p><p>I used Backbone to look after (and modularise) the graphs collection and some jQuery UI goodness for the drag and drop and slider. The site uses Twitter&#8217;s Bootstrap, lightly restyled to give it a Metro-like appearance. (Much easier than <a
href="http://bits.meloncholy.com/node-video-gallery/">last time</a> as the look has now been rolled out beyond phones.)</p><h2>Server code</h2><p>The back end is written in Node. The reason there&#8217;s a server component at all is to hide the API key away from prying eyes, though I did end up abstracting away some of the API&#8217;s quirks too. (The same API key can be used to do all sorts of other things like reboot the server and change the root password. I wish it were possible to create keys at different privilege levels but it&#8217;s not.)</p><p>The stats API can get details of the server&#8217;s current state or its state for an interval in the past, in which case a resolution can be given. This resolution may or may not be respected by MediaTemple, e.g. there&#8217;s a minimum resolution of 15s and a request for 2 hour intervals will be returned at a resolution of 60 minutes.</p><p>The API server will also only serve up a fairly small amount of data at a given resolution &#8211; smaller than I&#8217;d like, so the server divides up the client&#8217;s range into several requests and combines them before returning. The server code&#8217;s currently set up to return up to a week&#8217;s worth of data at once; it will return more, but at a resolution that would hammer the MediaTemple server rather, so please add some more ranges to the <code>mt-stats.json</code> config file if you want to do that. All data are transferred using JSON.</p><h2 id="setup-guide">Setting up (mt) Stats on your server</h2><ul><li><a
href="https://ac.mediatemple.net/api">Get an API key</a> for your MediaTemple server. You&#8217;ll also need your service ID later, which you can get by visiting <code>https://api.mediatemple.net/api/v1/services/ids.json?apikey=XXXXX</code> (I only have one service ID as I have one server, but apparently you could see more.)</li><li><a
href="http://nodejs.org/">Get a copy of Node.js</a>. You can put (mt) Stats on your server if you want to access your stats from anywhere, or run it locally on your machine if you prefer. If you haven&#8217;t got Node already and you&#8217;re using Linux, there&#8217;s an installation guide <a
href="https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager">here</a>. If you&#8217;re running Windows or OSX then you can just download the installer from the Node site.</li><li><a
href="https://github.com/meloncholy/mt-stats-viewer">Download (mt) Stats Viewer</a> from Github and run <code>npm install</code> in the folder where you put it to get the packages it needs (list <a
href="#dependencies">further down</a> for reference).</li><li>You should now have a copy of <code>mt-stats</code>. Rename the config file <code>node_modules/mt-stats/config/mt-stats-sample.json</code> to <code>mt-stats.json</code> and change the service ID and API key to match your server and key.</li><li>Rename the config file <code>config/app-sample.json</code> to <code>app.json</code> and change the URL and port. <code>localPort</code> is the internal port on which the service runs and <code>url </code> is the external URL for the service. Also edit <code>gaAccount</code> and <code>gaDomain</code> to match your Google Analytics details if you want to track usage.</li><li>Run <code>app.js</code> and you should be good to go.</li></ul><h2>mt-stats-viewer settings</h2><p>Please rename <code>app-sample.json</code> to <code>app.json</code>.</p><ul><li><b>localPort</b> &#8211; Local port on which the app is running</li><li><b>url</b> &#8211; Public URL for the app</li><li><b>mode</b> &#8211; <code>development</code> uses uncompressed and (many) separate JavaScript files, while <code>production</code> uses Google&#8217;s CDN for jQuery</li><li><b>title</b> &#8211; Page title</li><li><b>description</b> &#8211; Meta description contents</li><li><b>graphRanges</b> &#8211; Timespans that are sent to the client for the range slider. Currently 5 mins &#8211; 1 week in ms</li><li><b>author</b> &#8211; Meta author contents</li><li><b>jQueryCdnUrl</b> &#8211; Yup</li><li><b>gaAccount</b> &#8211; Your Google Analytics account</li><li><b>gaDomain</b> &#8211; Your Google Analytics domain</li></ul><pre>
{
	"localPort": 3000,
	"url": "http://bits.meloncholy.com/mt-stats",
	"mode": "production",
	"title": "(mt) Stats",
	"description": "Visualise your MediaTemple stats with some gorgeous D3 graphs.",
	"graphRanges": [
		300000,
		600000,
		900000,
		1200000,
		1500000,
		1800000,
		2700000,
		3600000,
		5400000,
		7200000,
		10800000,
		14400000,
		18000000,
		21600000,
		32400000,
		43200000,
		54000000,
		64800000,
		75600000,
		86400000,
		172800000,
		259200000,
		345600000,
		432000000,
		518400000,
		604800000
	],
	"author": "Andrew Weeks",
	"jQueryCdnUrl": "http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js",
	"gaAccount": "UA-XXXXXXXX-X",
	"gaDomain": "meloncholy.com"
}
</pre><h2>mt-stats settings</h2><p>Please rename <code>mt-stats-sample.json</code> to <code>mt-stats.json</code>.</p><ul><li><b>serviceId</b> &#8211; Your server&#8217;s service ID</li><li><b>apiKey</b> &#8211; Your MediaTemple API key</li><li><b>mode</b> &#8211; Doesn&#8217;t do anything here, but its presence gives me a warm, comforting glow</li><li><b>rootPath</b> &#8211; The root URL path for all API calls</li><li><b>interval</b> &#8211; Server polling interval for client. MediaTemple&#8217;s stats update every 15s</li><li><b>ranges</b> &#8211; For each <code>range</code>, the <code>resolution</code> at which to request data from the API (e.g. every 15 seconds) and the maximum span (<code>step</code>) to request in one go (to stop the API server objecting). Range is the maximum timespan at which to use that resolution and step</li><li><b>metrics</b> &#8211; Metrics supplied by the API. <code>apiKey</code> is the key name in JSON objects and <code>niceName</code> is the name to use on the graphs</li><li><b>definedRanges</b> &#8211; MediaTemple also supports some default intervals that can be requested with these URLs, e.g. <a
href="http://bits.meloncholy.com/mt-stats/api/5min">this URL</a> will serve up the last 5 minutes&#8217; data. These are not used by the front end</li><li><b>currentUrl</b> &#8211; API server URL from which to get the current stats. <code>%SERVICEID</code> and <code>%APIKEY</code> are replaced with your service ID and API key</li><li><b>historyUrl</b> &#8211; API server URL to request stats going back for the past X seconds, e.g. <a
href="http://bits.meloncholy.com/mt-stats/api/300">this URL</a> will also give the past 5 minutes&#8217; data</li><li><b>rangeUrl</b> &#8211; API server URL to get stats covering a specified time range</li></ul><pre>
{
	"serviceId": 000000,
	"apiKey": "XXXXXXXX",
	"mode": "production",
	"rootPath": "/api/",
	"interval": 15000,
	"ranges": [
		{ "range": 3600, "resolution": 15, "step": 3600 },
		{ "range": 43200, "resolution": 120, "step": 28800 },
		{ "range": 86400, "resolution": 240, "step": 57600 },
		{ "range": 604800, "resolution": 1800, "step": 432000 }
	],
	"metrics": [
		{ "apiKey": "cpu", "niceName": "CPU %" },
		{ "apiKey": "memory", "niceName": "Memory %" },
		{ "apiKey": "load1Min", "niceName": "Load 1 min" },
		{ "apiKey": "load5Min", "niceName": "Load 5 min" },
		{ "apiKey": "load15Min", "niceName": "Load 15 min" },
		{ "apiKey": "processes", "niceName": "Processes" },
		{ "apiKey": "diskSpace", "niceName": "Disk space" },
		{ "apiKey": "kbytesIn", "niceName": "kb in / sec" },
		{ "apiKey": "kbytesOut", "niceName": "kb out / sec" },
		{ "apiKey": "packetsIn", "niceName": "Packets in / sec" },
		{ "apiKey": "packetsOut", "niceName": "Packets out / sec" }
	],
	"definedRanges": ["5min", "15min", "30min", "1hour", "1day", "1week", "1month", "3month", "1year"],
	"currentUrl": "https://api.mediatemple.net/api/v1/stats/%SERVICEID.json?apikey=%APIKEY",
	"historyUrl": "https://api.mediatemple.net/api/v1/stats/%SERVICEID/%RANGE.json?apikey=%APIKEY",
	"rangeUrl": "https://api.mediatemple.net/api/v1/stats/%SERVICEID.json?start=%START&#038;end=%END&#038;&#038;resolution=%RESOLUTION&#038;apikey=%APIKEY"
}
</pre><h2 id="dependencies">Dependencies</h2><p>If NPM doesn&#8217;t do it for you, you&#8217;ll need to get copies of</p><ul><li><a
href="https://github.com/visionmedia/express">Express</a></li><li><a
href="https://github.com/visionmedia/jade">Jade</a></li><li><a
href="https://github.com/pgte/konphyg">Konphyg</a></li><li><a
href="https://github.com/cloudhead/less.js">Less</a></li><li><a
href="https://github.com/meloncholy/mt-stats">(mt) Stats</a></li></ul><h2>Problems and stuff</h2><p><b>It doesn&#8217;t work at all! I&#8217;ve pressed new and nothing happens!</b></p><p>IE and Opera aren&#8217;t supported (at least up to IE 9) &#8211; as I said, I wrote this principally to learn about D3 and for me, so supporting older browsers was not on my to do list. The biggest problem here is probably D3, which uses the browser&#8217;s SVG capabilities to draw graphs and doesn&#8217;t work with IE and Opera. (I know that IE (as of version 9) and Opera have some SVG support, but D3 doesn&#8217;t seem to like it, or at least not for the graphs I used. I haven&#8217;t looked into this further.) If this bothers you, I&#8217;d be more than happy to integrate a polyfill if you write one. :)</p><p><b>Why do the graphs jump sometimes?</b></p><p>The client requests an update every 15 seconds (the maximum resolution offered by MediaTemple) and it relies on this to slide the graphs along and create a smooth viewing experience. Unfortunately the updates from MediaTemple are not always that reliable: they can have a timestamp that&#8217;s off by a couple of seconds or, sometimes, by 30+ seconds. In these cases, the graphs will unfortunately jump as they recalibrate to the new timestamp.</p><p><b>What&#8217;s with the ugly loops in my graphs?</b></p><p>Particularly after loading the page, MediaTemple&#8217;s API will sometimes supply an update that&#8217;s timestamped 30-45s after the previous one, rather than the 15s the app was expecting. The <a
href="http://bl.ocks.org/1016220">line tension</a> I&#8217;m using means that the graphs sometimes show little loops in these cases. If this bugs you, you can adjust it in <code>mt-stats.graph.js</code>.</p><p><b>Urg! My graphs just raced of the screen and vanished!</b></p><p>The graphs sometimes seem to get their knickers in a twist and fly almost entirely off the screen before new data points are added. I think this is because the API server sometimes returns very few data points (2 or 3) when the range is set to 5 mins, though I have seen it happen at other times too. But I&#8217;m not sure what&#8217;s going on here. Sorry.</p><p><b>The axes are weird / have disappeared</b></p><p>Yes, they seem to do that sometimes. This can happen if you&#8217;re using a log scale and the graph range is currently small and not zero-based (e.g. 88.5-88.6). In those circumstances, D3 finds it difficult to add suitable tick marks. But it happens at other times too, and in those cases I&#8217;m not sure if it&#8217;s my fault (probably) or a bug in D3. Refreshing the page could help, but if not I apologise.</p><p><b>I don&#8217;t want to run Node.js</b></p><p>I like Node so I used it here. That said, the back end is basically a specialist proxy server, and translating it into another language should be pretty easy if you want to use PHP, Ruby or something else. (Again, please <a
href="http://meloncholy.com/contact">let me know</a>!)</p><p><b>It only works on MediaTemple</b></p><p>That&#8217;s true. But if you use another host that provides server stats in a JSON wrapper then translating it should be fairly trivial. MediaTemple sends status updates in these formats:</p><p><i>Ranged request</i></p><pre>
{"statsList":{"timeStamp":1343138160,"resolution":15,"serviceId":00000,"stats":[
{"timeStamp":1343138160,"cpu":12.99,"memory":83.24,"load1Min":0.0,"load5Min":0.0,"load15Min":0.0,"processes":48,"diskSpace":43.88,"kbytesIn":1.93,"kbytesOut":21.07,"packetsIn":11.4,"packetsOut":20.27,"state":1},
{"timeStamp":1343138175,"cpu":0.01,"memory":83.79,"load1Min":0.0,"load5Min":0.0,"load15Min":0.0,"processes":48,"diskSpace":43.88,"kbytesIn":0.85,"kbytesOut":33.61,"packetsIn":16.07,"packetsOut":22.6,"state":1},
...
]}}
</pre><p><i>Current status request</i></p><pre>
{"stats":{"timeStamp":1345595595,"cpu":1.76,"memory":103.39,"load1Min":0.0,"load5Min":0.02,"load15Min":0.0,"processes":72,"diskSpace":49.2,"kbytesIn":57.0,"kbytesOut":6.2,"packetsIn":72.0,"packetsOut":86.0,"state":1}}
</pre><p>I think the only thing you&#8217;d have to do to the front end is change this line in <code>mt-stats.data.js</code> to extract the stats from whatever object your server sends them in</p><pre>
for (var i = 0, len = data.statsList.stats.length; i < len; i++) {
</pre><p>You'll have to do a bit more fiddling around in <code>mt-stats/index.js</code> and <code>mt-stats/config/mt-stats.json</code> as that uses MediaTemple URLs and checks for specific return messages, but hopefully translating wouldn't be too much work.</p><h2>En fin</h2><p>Hope this is useful / interesting to someone other than me. If you end up using it, please <a
href="http://meloncholy.com/contact/">let me know</a>!</p><p><a
href="https://github.com/meloncholy/mt-stats-viewer" class="source-link">Source on Github</a> <a
href="https://github.com/meloncholy/mt-stats" class="source-link">Source on Github</a><br
/> <i>(You need both, but the first will get the second via NPM.)</i></p><h2>Legal fun</h2><p>Copyright &copy; 2012 Andrew Weeks <a
href="http://meloncholy.com">http://meloncholy.com</a></p><p>(mt) Stats is licensed under the <a
href="http://meloncholy.com/licence">MIT licence</a>.</p> ]]></content:encoded> <wfw:commentRss>http://meloncholy.com/blog/using-d3-for-realtime-webserver-stats/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://meloncholy.com/blog/using-d3-for-realtime-webserver-stats/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=using-d3-for-realtime-webserver-stats</feedburner:origLink></item> <item><title>Strange returns in JavaScript</title><link>http://feedproxy.google.com/~r/meloncholy/~3/QEefxz98HMA/</link> <comments>http://meloncholy.com/blog/strange-returns-in-javascript/#comments</comments> <pubDate>Sat, 21 Jul 2012 17:44:08 +0000</pubDate> <dc:creator>Andrew</dc:creator> <category><![CDATA[Comment]]></category> <category><![CDATA[JavaScript]]></category> <guid isPermaLink="false">http://meloncholy.com/?p=360</guid> <description><![CDATA[Via Stack Overflow, I&#8217;ve just found this from Neil Fraser. What gets returned? try { return true; } finally { return false; } The result is consistent in Java, JavaScript and Python. I wrote this snippet on a whiteboard here &#8230; <a
class="more-link" href="http://meloncholy.com/blog/strange-returns-in-javascript/">Keep&#160;reading&#160;<span
class="meta-nav">&#62;</span></a>]]></description> <content:encoded><![CDATA[<p>Via <a
href="http://stackoverflow.com/a/2005435">Stack Overflow</a>, I&#8217;ve just found this from <a
href="http://neil.fraser.name/news/2009/10/27/">Neil Fraser</a>. What gets returned?</p><pre>
try {
	return true;
} finally {
	return false;
}
</pre><p><span
id="more-360"></span></p><blockquote><p> The result is consistent in Java, JavaScript and Python. I wrote this snippet on a whiteboard here at Google. For the next couple of weeks engineers passing by would abruptly stop, transfixed by the paradox. <a
href="http://xkcd.com/356/">Nerd sniping</a>. I now use it during hiring interviews, if the candidate suffers a similar breakdown, I know we&#8217;ve got a keeper.</p></blockquote><p>If you haven&#8217;t tried it yet, the answer is <code>false</code>. Which makes sense I guess (haven&#8217;t dug into the <a
href="http://es5.github.com/">specs</a>, but I&#8217;m sure it&#8217;s all there).</p><p>But what actually happens in the <code>try</code>? Interestingly the statement is executed, but the function just doesn&#8217;t return. Consider</p><pre>
var a = 2;
try {
	return a++;
} finally {
	return a++;
}
</pre><p>The answer here is <code>3</code>.</p> ]]></content:encoded> <wfw:commentRss>http://meloncholy.com/blog/strange-returns-in-javascript/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://meloncholy.com/blog/strange-returns-in-javascript/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=strange-returns-in-javascript</feedburner:origLink></item> <item><title>Sending a regex via JSON in JavaScript</title><link>http://feedproxy.google.com/~r/meloncholy/~3/Q1-WyzqU6Es/</link> <comments>http://meloncholy.com/blog/sending-a-regex-via-json-in-javascript/#comments</comments> <pubDate>Sun, 15 Jul 2012 17:09:38 +0000</pubDate> <dc:creator>Andrew</dc:creator> <category><![CDATA[Code]]></category> <category><![CDATA[JavaScript]]></category> <guid isPermaLink="false">http://meloncholy.com/?p=324</guid> <description><![CDATA[I recently wrote some code to change post titles into slugs (permalinks). Blogs like WordPress clamp down quite hard on the symbols allowed in a post slug, e.g. swapping spaces for dashes and stripping most ASCII non-alphanumeric characters. Most of &#8230; <a
class="more-link" href="http://meloncholy.com/blog/sending-a-regex-via-json-in-javascript/">Keep&#160;reading&#160;<span
class="meta-nav">&#62;</span></a>]]></description> <content:encoded><![CDATA[<p>I recently wrote some code to change post titles into slugs (permalinks). Blogs like WordPress clamp down quite hard on the symbols allowed in a post slug, e.g. swapping spaces for dashes and stripping most ASCII non-alphanumeric characters. Most of these transformations can be tackled with some simple regular expressions (and one <code>toLowerCase</code>), but removing diacritics from letters requires a bit more work.</p><p>Luckily I found a <a
href="http://lehelk.com/2011/05/06/script-to-remove-diacritics/">nice function</a> to help me (updated <a
href="https://gist.github.com/3117741">here</a>). The function contains a big array of basic glyphs (and digraphs, trigraphs, etc.) and regexes of the letter with diacritical marks / Unicode di- or trigraph, e.g.</p><pre>
{ "base": "b", "letters": /[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g },
</pre><p>I needed this array on the server (for storing stuff in the database) and on the client (to give snappy performance), but, as it&#8217;s quite large, I wanted to wait until the page had loaded before I requested it, which meant using JSON. <span
id="more-324"></span></p><h2>Sending a regex via JSON</h2><p>Unfortunately <a
href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/stringify">stringifying</a> a <code>RegExp</code> <a
href="http://ecmascript5.blogspot.co.uk/2012/01/jsonstringify.html">gives <code>{}</code></a>, which is not quite what I had in mind.</p><p>If an object has a <code>toJSON</code> function then <code>stringify</code> will use that instead of doing its default conversion, so I added a function that loops through the array and, if it comes across a regex, converts it to a string.</p><p>(I&#8217;ve assumed the array to send is called <code>bigArray</code> and that <code>bigArrayJSON</code> is cached outside this function.)</p><pre>
bigArray.toJSON = function () {
	var entry;
	if (bigArrayJSON.length !== 0) return bigArrayJSON;
	for (var i = 0, len = bigArray.length; i < len; i++) {
		entry = {};
		Object.keys(bigArray[i]).forEach(function (key) {
			entry[key] = bigArray[i][key] instanceof RegExp ? bigArray[i][key].toString() : bigArray[i][key];
		});
		bigArrayJSON.push(entry);
	}
	return bigArrayJSON;
};
</pre><h2>Converting a regex string back into a real regex</h2><p>Now all I needed to do was convert the regex strings back into regexes, and it turned out I could do this with a one-liner.</p><p>The regex strings sent from the server will have the form <code>/pattern/flags</code>, which can be split up using <code>/^\/(.*)\/(.*)/</code> into <code>[pattern, flags]</code> (with a quick slice to restrict it to the parenthesised matches only) &ndash; almost perfect for making a new regex. Unfortunately the <code>RegExp</code> constructor expects two parameters, not an array, but apply can handle this.</p><pre>
function saveBigArray(data) {
	bigArray = data.bigArray;
	for (var i = 0, len = bigArray.length; i < len; i++) {
		// Convert string regex to real regex. Use apply as exec returns array.
		bigArray[i].letters = RegExp.apply(undefined, /^\/(.*)\/(.*)/.exec(bigArray[i].letters).slice(1));
	}
}
</pre><p>I'm a bit uncomfortable with the lack of new when calling RegExp. Testing suggests that it works just fine &ndash; <code>RegExp("aa?b", "g")</code> returns an identical new regex as <code>new RegExp("aa?b", "g")</code> &ndash; but I'm not happy with this. Any ideas on how to do a new with <code>apply</code>?</p><p>(It's possible to use <code>eval</code> on the string regex too, but I didn't want to do that. :)</p> ]]></content:encoded> <wfw:commentRss>http://meloncholy.com/blog/sending-a-regex-via-json-in-javascript/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://meloncholy.com/blog/sending-a-regex-via-json-in-javascript/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=sending-a-regex-via-json-in-javascript</feedburner:origLink></item> <item><title>MixCloud RSS Converter in Node.js</title><link>http://feedproxy.google.com/~r/meloncholy/~3/7DeoPt9zfPA/</link> <comments>http://meloncholy.com/blog/mixcloud-rss-converter-in-node-js/#comments</comments> <pubDate>Tue, 03 Jul 2012 22:40:12 +0000</pubDate> <dc:creator>Andrew</dc:creator> <category><![CDATA[Code]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Node.js]]></category> <guid isPermaLink="false">http://meloncholy.com/?p=302</guid> <description><![CDATA[Continuing my love affair with Node.js, I made a MixCloud to RSS converter last weekend. Just paste in the MixCloud account URL or name you want and it&#8217;ll give you an RSS URL in return. MixCloud RSS Converter The source &#8230; <a
class="more-link" href="http://meloncholy.com/blog/mixcloud-rss-converter-in-node-js/">Keep&#160;reading&#160;<span
class="meta-nav">&#62;</span></a>]]></description> <content:encoded><![CDATA[<p>Continuing my love affair with Node.js, I made a MixCloud to RSS converter last weekend. Just paste in the MixCloud account URL or name you want and it&#8217;ll give you an RSS URL in return.</p><p><a
href="http://bits.meloncholy.com/mixcloud-rss" class="source-link">MixCloud RSS Converter</a></p><p>The source is also <a
href="https://github.com/meloncholy/mixcloud-rss">on GitHub</a> if you want it.</p><h2>The bit with some reminiscing you can probably skip</h2><p>While at <a
href="http://www.york.ac.uk">York</a>, I was involved with (and ran) <a
href="http://www.breakzdjs.com/">Breakz</a> for quite a few years. Breakz is a music society that stages drum &amp; bass, dubstep and electronic music events, and has brought some pretty major acts to York, including Chase &amp; Status, Benga, Skream, Brookes Brothers and Ed Rush. <span
id="more-302"></span></p><p>One of the things I did, around 5 years ago, was move the website to WordPress. I ended up jumping in after the domain expired last week, and the site needed some more TLC generally. As I had some free time, I swapped it over to a new (cheaper and faster-so-far) host and changed the theme for one based on the one I did for <a
href="http://meloncholy.com/portfolio/premier-tri/">Premier Tri</a>.</p><p>I added a lot of Facebook links as everything happens there now, but I wanted to put some music back on the site too. (Once upon a time, the site hosted MP3s composed by some of the DJs and links to a few online radio stations, but this bit had long since bitten the dust.) Of course there&#8217;s been an explosion in online music over the past five years, and there are a huge number of sites out there for artists to host their tunes and network with others.</p><p>Two of the most popular, with this crowd at least, are SoundCloud and MixCloud, where most of the DJs have accounts. I needed to get the tunes from there and onto the site with as little work (on their part) as possible, which basically meant syndicating RSS feeds via a WordPress plugin.</p><h2>The bit about which you care</h2><p>Neither SoundCloud nor MixCloud offer RSS feeds, but fortunately Adam Schlitt&#8217;s already written <a
href="http://picklemonkey.net/cloudflipper/cloudflipper.php">a converter</a> for SoundCloud. Unfortunately I was out of luck for MixCloud (they <a
href="http://feedback.mixcloud.com/forums/3043-general/suggestions/1922671-loss-of-user-mixes-rss">used to have feeds</a>, but they don&#8217;t seem to work now). So I decided to write one.</p><p>MixCloud does have <a
href="http://www.mixcloud.com/developers/documentation/">an API</a>, so I was able to use this to get a recent uploads stream and then query their oEmbed API to get an embedded music player for each post, which I then added to an RSS feed. To make it pretty, I also made a frontend using the ubiquitous Twitter Bootstrap.</p><p>I really am standing on the shoulders of giants here as this required almost no code on my part (maybe 300 lines) thanks to the <a
href="https://github.com/astro/node-oembed/">node-oembed</a> and <a
href="https://github.com/dylang/node-rss">node-rss</a> modules from Stephan Maka and Dylan Greene, and of course Express, Jade and Node.js.</p><p>If you use it and if you can, please set the update interval to something fairly big. The app doesn&#8217;t cache anything, and every request could query MixCloud&#8217;s API around 20 times. Actually I probably shouldn&#8217;t worry about MixCloud as they no doubt have very meaty servers, but I don&#8217;t, so please think of me too. :)</p><p>Here&#8217;s the <a
href="http://www.breakzdjs.com/category/music/">Breakz music page</a>, which syndicates feeds from a number of MixCloud (and SoundCloud) accounts.</p><p><a
href="http://bits.meloncholy.com/mixcloud-rss" class="source-link">MixCloud RSS Converter</a></p><h2>The code</h2><p>If you&#8217;re interested in getting your own copy, I&#8217;ve put the source up <a
href="https://github.com/meloncholy/mixcloud-rss">on GitHub</a> too. As there&#8217;s not a lot to it, hopefully it will (almost) just work. After adjusting the settings you should be good to go.</p><h2>Settings</h2><p>Please rename <code>/config/app-sample.json</code> to <code>app.json</code>.</p><ul><li><strong>mode</strong> &#8212; <em>production</em> loads minimized files and includes Analytics, while <em>development</em> doesn&#8217;t.</li><li><strong>localPort</strong> &#8212; The local port to use.</li><li><strong>url</strong> &#8212; Public URL for the site.</li><li><strong>title</strong> &#8212; Name of the site. Used in the browser title bar and elsewhere.</li><li><strong>author</strong> &#8212; Not really used, actually. Just appears in the meta header.</li><li><strong>description</strong> &#8212; Meta description content.</li><li><strong>rssDescription</strong> &#8212; Short description of the feed. <em>%user</em> will be swapped for the current account&#8217;s name.</li><li><strong>cloudcastStreamUrl</strong> &#8212; MixCloud API URL for getting profiles.</li><li><strong>cloudCastOembedUrl</strong> &#8212; MixCloud API URL for getting Cloudcasts.</li><li><strong>jQueryCdnUrl</strong> &#8212; Yup. Only used in <em>production</em> mode.</li><li><strong>gaAccount</strong> &#8212; Your Google Analytics UA number.</li><li><strong>gaDomain</strong> &#8212; Your Analytics domain.</li></ul><p>Example</p><pre>
{
	"mode": "development",
	"localPort": 3000,
	"url": "http://bits.meloncholy.com/mixcloud-rss/",
	"title": "MixCloud to RSS converter",
	"author": "Andrew Weeks",
	"description": "Get an RSS feed for your (or anyone else's) Mixcloud mixes and podcasts.",
	"rssDescription": "Visit %user's profile on MixCloud. Listen to their recent and most popular Cloudcasts. It's free and really easy to use. MixCloud to RSS thingy hacked together by Andrew Weeks. http://meloncholy.com",
	"cloudcastStreamUrl": "http://api.mixcloud.com/%user/cloudcasts/",
	"cloudcastOembedUrl": "http://www.mixcloud.com/oembed/?url=http%3A//www.mixcloud.com%cloudcast&#038;format=json",
	"jQueryCdnUrl": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js",
	"gaAccount": "UA-XXXXXXXX-X",
	"gaDomain": "meloncholy.com"
}
</pre><h2>Dependencies</h2><p>In case NPM doesn&#8217;t help you out for some reason, you&#8217;ll also need</p><ul><li><a
href="https://github.com/visionmedia/express">Express</a></li><li><a
href="https://github.com/visionmedia/jade">Jade</a></li><li><a
href="https://github.com/pgte/konphyg">Konphyg</a></li><li><a
href="https://github.com/astro/node-oembed/">node-oembed</a></li><li><a
href="https://github.com/dylang/node-rss">node-rss</a></li></ul><p><a
href="https://github.com/meloncholy/mixcloud-rss" class="source-link">Source on GitHub</a></p><h2>Legal fun</h2><p>Copyright &copy; 2012 Andrew Weeks http://meloncholy.com</p><p>MixCloud RSS Converter is licensed under the <a
href="http://meloncholy.com/licence/">MIT licence</a>.</p> ]]></content:encoded> <wfw:commentRss>http://meloncholy.com/blog/mixcloud-rss-converter-in-node-js/feed/</wfw:commentRss> <slash:comments>2</slash:comments> <feedburner:origLink>http://meloncholy.com/blog/mixcloud-rss-converter-in-node-js/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=mixcloud-rss-converter-in-node-js</feedburner:origLink></item> <item><title>Node.js Video Gallery</title><link>http://feedproxy.google.com/~r/meloncholy/~3/P3AwxIB6JyE/</link> <comments>http://meloncholy.com/blog/node-js-video-gallery/#comments</comments> <pubDate>Mon, 30 Apr 2012 16:28:49 +0000</pubDate> <dc:creator>Andrew</dc:creator> <category><![CDATA[Code]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Node.js]]></category> <guid isPermaLink="false">http://meloncholy.com/?p=267</guid> <description><![CDATA[A few months ago, I wrote a Metro-style video gallery with Ajax, infinite scrolling and other fun stuff. I&#8217;ve now rewritten it in Node.js, which was surprisingly cathartic after coding the PHP original. There&#8217;s a load of bumf about the &#8230; <a
class="more-link" href="http://meloncholy.com/blog/node-js-video-gallery/">Keep&#160;reading&#160;<span
class="meta-nav">&#62;</span></a>]]></description> <content:encoded><![CDATA[<p>A few months ago, I wrote a Metro-style video gallery with Ajax, infinite scrolling and other fun stuff. I&#8217;ve now rewritten it in Node.js, which was surprisingly cathartic after coding the PHP original.</p><p>There&#8217;s a load of bumf about the <a
href="http://meloncholy.com/portfolio/video-gallery/">original PHP version here</a> (mostly relevant) and some supplementary info about <a
href="http://meloncholy.com/portfolio/node-video-gallery/">this version here</a>, though probably the best thing to do is to try the <a
href="http://bits.meloncholy.com/node-video-gallery/">demo site</a>. Yay!</p><p><a
href="https://github.com/meloncholy/node-video-gallery" class="source-link">Source on GitHub</a><span
id="more-267"></span></p><h2>Nota bene</h2><p>The front end code is (almost) identical to that in the PHP version. It could do with updating a bit, e.g. to use the HTML5 history API rather than <code>#!</code> and moving the jQuery events over to <code>on</code>. But other than that I think it works fine.</p><p>I redid an existing project so I could focus on how Node, and not the app itself, works. It was definitely a learning process for me, and I&#8217;m sure I could have done things better (and would probably do some bits better if I were starting again now). So if you have any feedback on how I could improve things, I&#8217;d love to hear.</p><p>With those health warnings out of the way&hellip;</p><h2>Settings</h2><p>You&#8217;ll need to set up a MySQL database first (see <a
href="#sample-content">sample content</a>). Then there are some configuration things to do</p><p><code>/config/app.json</code> (Please rename <code>app-sample.json</code>.)</p><ul><li><strong>localPort</strong> &#8211; Local port to use</li><li><strong>sessionSecret</strong> &#8211; Session secret for Express</li><li><strong>siteUrl</strong> &#8211; Public URL for your site</li><li><strong>mode</strong> &#8211; <em>development</em> or <em>production</em>. <em>development</em> gives more error messages, while <em>production</em> uses minimized, combined public JS files that load a lot faster.</li><li><strong>batchSize</strong> &#8211; Number of videos to load at once in infinite scroll.</li><li><strong>videoCount</strong> &#8211; Approximate number of videos in the database. Just used for bragging rights in a few places.</li><li><strong>cookiePath</strong> &#8211; Cookie path to use. Unless you want to restrict it to a subfolder, I&#8217;d suggest using <code>/</code>.</li><li><strong>siteTitle</strong> &#8211; Name of the site. Used in browser title bar and elsewhere.</li><li><strong>siteTitlePost</strong> &#8211; Text to stick in the title bar after the video name (where appropriate).</li><li><strong>siteDescription</strong> &#8211; Meta description content.</li><li><strong>maxRelatedVideos</strong> &#8211; If we haven&#8217;t come via a search (via a link or refresh), show this many related videos at the side of a video.</li><li><strong>maxSimilarVideos</strong> &#8211; Show this many similar videos at the bottom of a video. See video.js for more. This number should obviously work for the largest video size offered.</li><li><strong>prettyLink</strong> &#8211; Prepend this to each video&#8217;s URL for SEO, so if this were <code>amazing-stunts</code>, the URLs would be something like <code>http://example.com/#!amazing-stunts/name-of-this-video</code>.</li><li><strong>jQueryCdnUrl</strong> &#8211; In <em>production</em> mode, load jQuery from here.</li><li><strong>gaAccount</strong> &#8211; Your Google Analytics UA number.</li><li><strong>gaDomain</strong> &#8211; Your Analytis domain.</ul><p>Example</p><pre>
{
	"localPort": 3000,
	"sessionSecret": "session secret",
	"siteUrl": "http://bits.meloncholy.com/node-video-gallery/",
	"mode": "development",
	"batchSize": 20,
	"videoCount": 300,
	"cookiePath": "/",
	"siteTitle": "Video Gallery",
	"siteTitlePost": " | Video Gallery",
	"siteDescription": "This Video Gallery is a web app with lots of jQuery and Node goodness. Yum.",
	"maxRelatedVideos": 19,
	"maxSimilarVideos": 7,
	"prettyLink": "video",
	"jQueryCdnUrl": "http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js",
	"gaAccount": "UA-XXXXXXXX-X",
	"gaDomain": "meloncholy.com"
}
</pre><p><code>/config/database.json</code></p><ul><li><strong>host</strong> &#8211; Database host</li><li><strong>database</strong> &#8211; Database name</li><li><strong>user</strong> &#8211; Database user</li><li><strong>password</strong> &#8211; Database password</ul><p>Example</p><pre>
{
	"host": "localhost",
	"database": "database",
	"user": "user",
	"password": "password"
}
</pre><h2>Dependencies</h2><p>To get it to work, you&#8217;ll also need</p><ul><li><a
href="https://github.com/visionmedia/express">Express</a></li><li><a
href="https://github.com/visionmedia/jade">Jade</a></li><li><a
href="https://github.com/pgte/konphyg">Konphyg</a></li><li><a
href="https://github.com/meloncholy/vid-streamer">VidStreamer.js</a></li></ul><h2 id="sample-content">Sample content</h2><p>If you want to get going quickly, you can use the sample videos, photos and database from the <a
href="https://github.com/meloncholy/video-gallery">PHP Video Gallery</a> on GitHub (placeholder and database folders resp.).</p><p>And try something like this for <code>vidStreamer.json</code> (in <code>/node_modules/vid-streamer/config</code>) to get it to serve up the sample content.</p><pre>
{
	"mode": "development",
	"forceDownload": false,
	"random": true,
	"rootFolder": "/path/to/express/public/",
	"rootPath": "node-video-gallery/",
	"server": "VidStreamer.js/0.1"
}
</pre><h2>Thanks</h2><p>The download includes Longtail Video&#8217;s <a
href="http://www.longtailvideo.com/">JW Player</a>, released under Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) licence. Commercial licences are also available. You&#8217;re of course welcome to swap the player for another one if you don&#8217;t like it, though you&#8217;ll need to change some of the source files that set up parameters to pass to the player.</p><p>The sample video footage comprises some short video clips and still images of these video clips by <a
href="http://www.theycallusanimals.com/">Catrin Hedstr&ouml;m</a> and released under a Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) licence. It&#8217;s gorgeous footage too &#8211; do check it out!</p><p><a
href="https://github.com/meloncholy/node-video-gallery" class="source-link">Source on GitHub</a></p><h2>Legal fun</h2><p>Copyright &copy; 2012 Andrew Weeks http://meloncholy.com</p><p>Video Gallery is licensed under the <a
href="http://meloncholy.com/licence/">MIT licence</a>.</p><p>Includes some code written by others; see source for credits and licence details (though it&#8217;s all permissive).</p> ]]></content:encoded> <wfw:commentRss>http://meloncholy.com/blog/node-js-video-gallery/feed/</wfw:commentRss> <slash:comments>9</slash:comments> <feedburner:origLink>http://meloncholy.com/blog/node-js-video-gallery/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=node-js-video-gallery</feedburner:origLink></item> <item><title>VidStreamer.js – a simple Node.js video streamer</title><link>http://feedproxy.google.com/~r/meloncholy/~3/e_hnDvfaYlA/</link> <comments>http://meloncholy.com/blog/vidstreamer-js-a-simple-node-js-video-streamer/#comments</comments> <pubDate>Thu, 19 Apr 2012 16:19:27 +0000</pubDate> <dc:creator>Andrew</dc:creator> <category><![CDATA[Code]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Node.js]]></category> <guid isPermaLink="false">http://meloncholy.com/?p=252</guid> <description><![CDATA[I&#8217;ve been really keen to learn Node.js for a while now, so I decided to translate the video gallery I wrote a while ago into Node (nearly done!). At the back end, the video gallery uses xmoovStream Server (site currently &#8230; <a
class="more-link" href="http://meloncholy.com/blog/vidstreamer-js-a-simple-node-js-video-streamer/">Keep&#160;reading&#160;<span
class="meta-nav">&#62;</span></a>]]></description> <content:encoded><![CDATA[<p>I&#8217;ve been really keen to learn Node.js for a while now, so I decided to translate the <a
href="http://meloncholy.com/portfolio/video-gallery/">video gallery</a> I wrote a while ago into Node (<strike>nearly</strike> <a
href="http://meloncholy.com/blog/node-js-video-gallery/">done</a>!). At the back end, the video gallery uses <a
href="http://stream.xmoov.com/">xmoovStream Server</a> (site currently dead) to stream the videos to the popular JW Player (I used Flash videos as I wanted to support bitrate switching).</p><p>As using a LAMP stack to support a Node app seemed wrong, I wrote a simple JavaScript video streamer. Most of the requirements came from looking at how xmoovStream Server works.</p><ul><li>Supports http pseudostreaming</li><li>Streams Flash video and can resume streams</li><li>Streams other video and file formats and can resume these streams too</li><li>Works with JW Player&#8217;s bitrate switching</li></ul><p><a
href="https://github.com/meloncholy/vid-streamer" class="source-link">Source on GitHub</a> <span
id="more-252"></span></p><h2>Using VidStreamer</h2><p>To make a standalone video streamer, try something like this</p><pre>
var http = require("http");
var vidStreamer = require("vid-streamer");
var app = http.createServer(vidStreamer);
app.listen(3000);
console.log("VidStreamer.js up and running on port 3000");
</pre><p>And here&#8217;s an example of including it in an Express app</p><pre>
var app = require("express").createServer();
var vidStreamer = require("vid-streamer");
app.get("/videos/", vidStreamer);
app.listen(3000);
</pre><h2>Settings</h2><p>There are a few things to twiddle in <code>/config/vidStreamer.json</code>. (Please rename <code>vidStreamer-sample.json</code>.)</p><ul><li><strong>mode</strong> &#8211; Not actually used right now. :)<li><strong>forceDownload</strong> &#8211; Tell the browser to show a save file dialog.<li><strong>random</strong> &#8211; Show a random file instead of the one named (see below).<li><strong>rootFolder</strong> &#8211; The root folder from which files are returned. Accessing subfolders is possible by putting them in the path.<li><strong>rootPath</strong> &#8211; The path assumed to match up with the root folder.<li><strong>server</strong> &#8211; Server string returned in the header.</ul><p>Standalone example</p><pre>
{
	"mode": "development",
	"forceDownload": false,
	"random": false,
	"rootFolder": "/path/to/videos/",
	"rootPath": "videos/",
	"server": "VidStreamer.js/0.1"
}
</pre><p>Example for Express. (Note that rootPath should be relative to the root URL of your Express app.)</p><pre>
{
	"mode": "development",
	"forceDownload": false,
	"random": false,
	"rootFolder": "/path/to/express/public/",
	"rootPath": "",
	"server": "VidStreamer.js/0.1"
}
</pre><h2>Serving random files</h2><p>You can use VidStreamer to serve up random files instead of the actual file requested. This can be useful if you&#8217;re demoing an app that&#8217;s supposed to have hundreds of videos but you don&#8217;t want to go to the trouble of making them all.</p><ul><li>Which file to return is calculated from the requested file name, so the same one will be returned each time for the same name. This is useful if you want to switch between video bitrates for example.</li><li>The file returned will be of the same type (same extension) as the requested file.</li><li>The file returned will be from the same folder as the requested file.</li></ul><h2>Thanks</h2><p>I hadn&#8217;t really thought about how to write video streamer before, so <a
href="http://delog.wordpress.com/2011/04/25/stream-webm-file-to-chrome-using-node-js/">Devendra Tewari&#8217;s post</a> and of course the xmoovStream Server source were very useful to me.</p><p><a
href="https://github.com/meloncholy/vid-streamer" class="source-link">Source on GitHub</a></p><h2>Legal fun</h2><p>Copyright &copy; 2012 Andrew Weeks http://meloncholy.com</p><p>VidStreamer.js is licensed under the <a
href="http://meloncholy.com/licence/">MIT licence</a>.</p> ]]></content:encoded> <wfw:commentRss>http://meloncholy.com/blog/vidstreamer-js-a-simple-node-js-video-streamer/feed/</wfw:commentRss> <slash:comments>2</slash:comments> <feedburner:origLink>http://meloncholy.com/blog/vidstreamer-js-a-simple-node-js-video-streamer/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=vidstreamer-js-a-simple-node-js-video-streamer</feedburner:origLink></item> <item><title>Aaarg!</title><link>http://feedproxy.google.com/~r/meloncholy/~3/L56AUKIpw28/</link> <comments>http://meloncholy.com/blog/aaarg-web-pages-that-suck/#comments</comments> <pubDate>Thu, 26 Jan 2012 20:07:50 +0000</pubDate> <dc:creator>Andrew</dc:creator> <category><![CDATA[Comment]]></category> <guid isPermaLink="false">http://meloncholy.com/?p=234</guid> <description><![CDATA[A few years ago, I came across a couple of sites on Web Pages That Suck. (Both of which, I&#8217;m delighted to say, are still in more or less their original states five years after that post was published.) While &#8230; <a
class="more-link" href="http://meloncholy.com/blog/aaarg-web-pages-that-suck/">Keep&#160;reading&#160;<span
class="meta-nav">&#62;</span></a>]]></description> <content:encoded><![CDATA[<p>A few years ago, I came across a couple of sites on <a
href="http://www.webpagesthatsuck.com/10-worst-web-pages-featured-on-web-pages-that-suck-in-2006.html">Web Pages That Suck</a>. (<a
href="http://www.hrodc.com/index.htm">Both</a> of <a
href="http://www.dokimos.org/ajff/">which</a>, I&#8217;m delighted to say, are still in more or less their original states five years after that post was published.)</p><div
id="attachment_237" class="wp-caption alignnone" style="width: 570px"><a
href="http://meloncholy.com/wordpress/wp-content/uploads/hrodc-postgraduate-training-courses.jpg"><img
src="http://meloncholy.com/wordpress/wp-content/uploads/hrodc-postgraduate-training-courses-thumb.jpg" alt="" title="HRODC postgraduate training courses" width="560" height="300" class="size-full wp-image-237" /></a><p
class="wp-caption-text">HRODC postgraduate training courses</p></div><div
id="attachment_239" class="wp-caption alignnone" style="width: 570px"><a
href="http://meloncholy.com/wordpress/wp-content/uploads/accept-jesus-forever-forgiven.jpg"><img
src="http://meloncholy.com/wordpress/wp-content/uploads/accept-jesus-forever-forgiven-thumb.jpg" alt="" title="ACCEPT JESUS, FOREVER FORGIVEN!" width="560" height="300" class="size-full wp-image-239" /></a><p
class="wp-caption-text">ACCEPT JESUS, FOREVER FORGIVEN!</p></div><p>While both are, of course, fine examples of important web design maxims, surely their impact would be much greater if they could be finessed into one great web page? After all, who could resist combining the eloquent brevity of a 1.2MB page full of tables with the understated finesse of animated rainbow gifs and autoplaying music?</p><p>Turns out I couldn&#8217;t. And, having just rediscovered the result, I thought I&#8217;d stick it up here.</p><p><a
href="http://bits.meloncholy.com/aaarg/">Aaarg!</a></p><p><em>To enjoy the full experience, including the music and a bird that follows your cursor around, please view in an old version of IE.</em></p> ]]></content:encoded> <wfw:commentRss>http://meloncholy.com/blog/aaarg-web-pages-that-suck/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://meloncholy.com/blog/aaarg-web-pages-that-suck/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=aaarg-web-pages-that-suck</feedburner:origLink></item> <item><title>Serving up random-but-consistent sample videos</title><link>http://feedproxy.google.com/~r/meloncholy/~3/5co83InafZU/</link> <comments>http://meloncholy.com/blog/serving-up-random-but-consistent-sample-videos/#comments</comments> <pubDate>Tue, 03 Jan 2012 16:01:28 +0000</pubDate> <dc:creator>Andrew</dc:creator> <category><![CDATA[.htaccess]]></category> <category><![CDATA[Code]]></category> <category><![CDATA[PHP]]></category> <guid isPermaLink="false">http://meloncholy.com/?p=181</guid> <description><![CDATA[Need to serve up some sample images or videos that are apparently random, but remain consistent each time you show them? Download source Update: this is also part of the Node.js Video Streamer I made. Though not in PHP, obviously. &#8230; <a
class="more-link" href="http://meloncholy.com/blog/serving-up-random-but-consistent-sample-videos/">Keep&#160;reading&#160;<span
class="meta-nav">&#62;</span></a>]]></description> <content:encoded><![CDATA[<p>Need to serve up some sample images or videos that are apparently random, but remain consistent each time you show them?</p><p><a
href="https://github.com/meloncholy/consistent-random" class="source-link">Download source</a></p><p><em><strong>Update:</strong> this is also part of the <a
href="http://meloncholy.com/blog/vidstreamer-js-a-simple-node-js-video-streamer/">Node.js Video Streamer</a> I made. Though not in PHP, obviously.</em></p><p>I recently uploaded a <a
href="http://meloncholy.com/portfolio/video-gallery/">Video Gallery</a> web app I&#8217;d made (<a
href="http://bits.meloncholy.com/video-gallery/">demo</a>, <a
href="https://github.com/meloncholy/video-gallery">code download</a>). Unfortunately I don&#8217;t have the real videos and pictures that it was supposed to use, so I thought I&#8217;d cut some clips from the silent-but-gorgeous <a
href="http://www.theycallusanimals.com/">They Call Us Animals</a> music video footage. But the Video Gallery has about 300 different videos in the database, and I really didn&#8217;t want to make 900 sample clips (each one in 720p, 480p and 360p). So I made about 10, which seems enough to give a little variety.</p><p>But I couldn&#8217;t just serve up any video. Using the HTTP pseudostreaming support in <a
href="http://www.longtailvideo.com/players/">JW Player</a>, the site lets users change video sizes while they&#8217;re watching, and the player will switch to a different resolution source file if one is available to improve the viewing experience. It looks good, but the effect was rather spoiled when the video selector returned a higher resolution version of a completely different clip.</p><p>So I wrote a little PHP script that would serve up an apparently random sample video consistently. <span
id="more-181"></span></p><h2>Using the code</h2><p>First set <code>$folder</code> to point at the folder where the files you want to serve are located. It&#8217;s currently set up to use the folder where the file is uploaded.</p><p>The file selector will serve up jpegs, pngs and flv files from the box. If you want to allow others, add their file extensions and MIME types to the <code>$ext_list</code> array. There are examples for CSS, PDF and HTML files in the source. (On a side note, please don&#8217;t add PHP to the list and use <code>$folder = '.'</code> unless you don&#8217;t mind it returning itself sometimes.)</p><p>To get a random-but-consistent file, link to it as any other file</p><pre>
&lt;a href="http://example.com/rotate.php?file=kittens.flv"&gt;link&lt;/a&gt;
</pre><pre>
&lt;img src="http://example.com/rotate.php?file=kittens.jpg" /&gt;
</pre><p>The file returned will be of that same type (flv or jpg above). It will not be kittens.flv, but you will get the same one every time you ask for kittens.flv.</p><h2>The code</h2><p>After performing a few checks on the file type and allowed extensions, I used <a
href="http://php.net/manual/en/function.crc32.php"><code>crc32</code></a> to convert the supplied filename into an integer and ran it through the modulus operator to get a number in the range of the file count in the current folder. Then it reads the chosen file and we&#8217;re done. And that&#8217;s it.</p><pre>
$ext_list = array();
$ext_list['gif'] = 'image/gif';
$ext_list['jpg'] = 'image/jpeg';
$ext_list['jpeg'] = 'image/jpeg';
$ext_list['png'] = 'image/png';
$ext_list['flv'] = 'video/x-flv';
$file = null;
$vid = null;
$vids = null;
$content_type = null;
if (!isset($_GET['file'])) return;
$file = pathinfo($_GET['file']);
// Just in case there's other stuff in the folder too (like this file)
if (!array_key_exists($file['extension'], $ext_list)) return;
if (substr($folder, -1) != '/') $folder .= '/';
// Select a file based on the given extension.
$vids = glob($folder . '*.' . $file['extension']);
$vid = $vids[abs(crc32($file['basename'])) % count($vids)];
$content_type = 'Content-type: ' . $ext_list[$file['extension']];
header ($content_type);
readfile($vid);
</pre><h2>Fun with .htaccess</h2><p>Supplying random-but-consistent videos like this changes their path, as it&#8217;s now necessary to include (at least) <code>consistent-random.php?file=</code>. Not ideal, but a few lines added to .htaccess can point the original paths at the new placeholder files.</p><pre>
RewriteBase /video-gallery/
RewriteRule ^images/thumb/([-_a-zA-Z0-9\.]+)$ placeholder/images/thumb/consistent-random.php?file=$1 [L]
RewriteRule ^images/full/([-_a-zA-Z0-9\.]+)$ placeholder/images/full/consistent-random.php?file=$1 [L]
RewriteRule ^videos/([-_a-zA-Z0-9\.]+.flv)$ placeholder/videos/consistent-random.php?file=$1 [L]
</pre><p>And we&#8217;re done! The Video Gallery now thinks it&#8217;s supplying the videos and images as originally intended, not the placeholder ones it&#8217;s actually showing.</p><p><a
href="https://github.com/meloncholy/consistent-random" class="source-link">Download source</a></p> ]]></content:encoded> <wfw:commentRss>http://meloncholy.com/blog/serving-up-random-but-consistent-sample-videos/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://meloncholy.com/blog/serving-up-random-but-consistent-sample-videos/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=serving-up-random-but-consistent-sample-videos</feedburner:origLink></item> </channel> </rss>
