All posts by James

JavaScript ECMAScript 6 slugify

I wanted to slugify a URL using Javascript.  There are lots of implementations out there, but they aren’t very nice.  I have taken inspiration from the Django implementation to create the following.  It uses String.prototype.normalize(), which is ES6, so it won’t work where you don’t have ES6.  I’m using Node.js, so I’m happy with this, and it’s very neat.  
function slugify(value) {
// Compatibly-decompose and remove combining characters.
value = value.normalize('NFKD').replace(/[\u0300-\u036F]/g, '');
// Remove all non-word characters, leaving spaces and dashes. Trim and convert to lower case.
value = value.replace(/[^\w\s\-]+/g, '').trim().toLowerCase();
// Replace groups of spaces and dashes with a single dash.
return value.replace(/[-\s]+/g, '-');
The following runs some test cases through it.  They are designed for a different function, so most fail, but it shows how it works.  

OCR PDF on Windows

This turns out to be fairly easy.  
  1. Install Tesseract
  2. Convert your PDF to TIFF (Print > Fax > check option print to file and save as TIFF)
    OR install Ghostscript, and convert using the command line (I installed the 64 bit version):
    gswin64c -r300x300 -o out.tif -sDEVICE=tiffg4 in.pdf
  3. Convert the PDF: `tesseract input.tif out -l deu pdf` (skip the `-l deu` bit if it’s not in German; out is the name of the output file, the extension, e.g. pdf, will be appended)

Done.  You can even get Google to translate the output.

Show only unread or flagged or todays mail in Outlook

I use this filter so that I can manage my inbox in outlook. It means that once I’ve read a message, it isn’t shown unless I flag it. I also have a filter that keeps todays mail shown too.

Adding the “Change View” option to the quick launch bar makes changing between views less painful. Go to the “View” ribbon and then right click “change view” and “add to quick access toolbar”:
Add to quick access toolbarSelect “Manage Views”:
Manage ViewsSelect New, and give it a name, hit ok:
New ViewSelect “Filter”, and enter one of these:
a)  Only unread and flagged:
(“urn:schemas:httpmail:read” = 0) OR (“” > 1)
b)  Unread, flagged and todays messages:
(“urn:schemas:httpmail:read” = 0) OR (“” > 1) OR (“urn:schemas:httpmail:datereceived” >= ‘Today’)
Screenshot_842Hit OK a few times and then select you view.  Sorted.  

User script to add information to transfers page

I have no clue about football, yet I’m in a fantasy league. I’ve written this user script to pull injury information from ‘’ and the likely lineups from ‘’. Injury information is shown with an ‘Injured’ badge in the status column. Players that are expected to play in the next match are highlighted by adding a black border and black text to the player’s position symbol. Requires Tampermonkey fpldraft

Running a Canon CanoScan FS 2710 on Windows 10

…Isn’t easy.  

I have an Adaptec AHA-2940 SCSI controller, which doesn’t have any drivers available on their support site for x64 systems.  I found them on a forum somewhere (perhaps not overly trust worthy): AdaptecAic78xx.  They have been taken from a Windows 7 x64 system apparently.

These aren’t signed, so you have to disable driver signing:

  1. Hit the start button, power and then shift-click restart.  
  2. Select “Troubleshoot”->“Advanced options”->“Windows Startup Settings”->restart.  
  3. When the system starts up you get a selection of options.  Option 7 disables driver signing requirements.  

Then you can go to device manager, update the driver, go to “have disk” and select the folder extracted from the zip linked above.

The next bit isn’t all that intelligent, but works.  Go buy VueScan, it seems to have drivers for every scanner under the sun.  

Spotify web client minimized mode bookmarklet

I was playing around with casting spotify from Chrome to Chromecast, and disliked the cluttered interface here’s a bookmarklet that can be dragged to the bookmarks bar.  When clicked, it toggles the interface to show a more minimal look.  



Here’s the code:
if (typeof spotTgShown == 'undefined') {
var spotTgShown = true;

function spotTg() {
if (spotTgShown){
spotTgShown = false;
} else {
spotTgShown = true;

function spotSm() {
$('now-playing').setStyle('right', 'auto').setStyle('left', '0');
$('main-nav').setStyle('visibility', 'hidden');
$('main').setStyle('visibility', 'hidden');
$('suggest-area').setStyle('visibility', 'hidden');
$('now-playing-widgets').setStyle('visibility', 'hidden');
$('now-playing').setStyle('width', '100%');
$$('body').setStyle('overflow', 'hidden');
$('wrapper').setStyle('min-width', '0px');

function spotLg() {
$('now-playing').setStyle('left', 'auto').setStyle('right', '0');
$('main-nav').setStyle('visibility', 'visible');
$('main').setStyle('visibility', 'visible');
$('suggest-area').setStyle('visibility', 'visible');
$('now-playing-widgets').setStyle('visibility', 'visible');
$('now-playing').setStyle('width', '');
$$('body').setStyle('overflow', '')
$('wrapper').setStyle('min-width', '');

Cast audio only from Google Chrome to Chromecast

This is a not particularly satisfying work around, but it seems to work.

My issue was that the Google Cast extension and GPU processing in Chrome were hammering my CPU and the audio from Spotify was choppy.  If only there was a way to have audio only.  Well there is, kind of.  Take your tab into a new window and reduce the window size to nothing.  This caused my CPU usage to drop dramatically, and the audio streams nicely now.  My TV just displays a thin grey line accross the center of the screen, which I assume is the single pixel of my window or something.  I’m pretty happy with that.  

Changing tracks is a pain in the ass…

Samsung TV average selling prices by day on eBay

I always assumed that everyone bought everything on eBay on a Sunday, as they had nothing better to do. Realising this was utterly unfounded, I decided to do a little (not hugely scientific) research. I gathered the average selling price for the last 200 Samsung TVs to sell on eBay for various sizes. Here’s a chart of the results.


s  you can see, it doesn’t seem to make any difference what day the item finishes.  More expensive items seem to have more volatility though.  Also, larger TVs are more expensive.  

The reason I chose only Samsung was to limit the number of results, in an attempt to gather data over a longer time period (without expending to much effort).  I used this bookmarklet to gather the data.  

Average selling price for completed eBay items on each day of the week

Here’s a bookmarklet to let you know what the average price of items shown in eBay was on each day of the week.

  1. Drag eBayDayAvg to your address bar.
  2. Navigate to a completed and sold page on eBay.
  3. Click on the bookmarklet.

It’s probably best to run it on a page with the max results as high as possible, and try it on a few pages.  Also limit the page to display the most relevant items.


Here’s the code:

msg = '';
days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
items = {0:[],1:[],2:[],3:[],4:[],5:[],6:[]};
$('.sresult ').each(function() {
 title = $(this).find('.lvtitle').text();
 if (!(title.includes('fault') || title.includes('spare') || title.includes('issue') || title.includes('damage'))){
 price = parseFloat($(this).find('.lvprice').text().replace('£', ''));
 date = new Date(Date.parse($(this).find('.timeleft').text() + ' ' + new Date().getFullYear()));
for(item in items) {
 if (items[item].length > 0) {
 msg += days[item] + ': ' + items[item].reduce(function(a, b){return a+b;}) / items[item].length + '\r\n'


Convert LatLng to Point and Point to LatLng in Google Maps API v3

Having googled this, I found code to go one direction, but not the other.  Here’s both.

function latLngToPoint(latLng) {
 var topRight = map.getProjection().fromLatLngToPoint(map.getBounds().getNorthEast());
 var bottomLeft = map.getProjection().fromLatLngToPoint(map.getBounds().getSouthWest());
 var scale = Math.pow(2, map.getZoom());
 var worldPoint = map.getProjection().fromLatLngToPoint(latLng);
 return new google.maps.Point((worldPoint.x - bottomLeft.x) * scale, (worldPoint.y - topRight.y) * scale);

function PointTolatLng(point) {
 var topRight = map.getProjection().fromLatLngToPoint(map.getBounds().getNorthEast());
 var bottomLeft = map.getProjection().fromLatLngToPoint(map.getBounds().getSouthWest());
 var scale = Math.pow(2, map.getZoom());
 return map.getProjection().fromPointToLatLng(new google.maps.Point((point.x / scale) + bottomLeft.x, (point.y / scale) + topRight.y));