muonlab

random .NET and web development musings

If your index page is slow to render, you may find you experience a flicker when calling:

navigator.splashscreen.hide();

i.e. the splash screen hides, you get a blank page, then your content appears.

You can remedy this by deferring the hiding of the slash screen until a few animation frames after you’ve finished drawing.

You may be able to achieve this using a setTimeout, however it will depend on what the rest of your code is doing.
A better approach (for writing your whole app anyway – regardless of this problem) is to use asynchronous DOM I/O, using something like fastdom.

Then you can simply do:

fastdom.defer(2, function () {
    navigator.splashscreen.hide();
});

This of course assumes that you’ve done all your other DOM I/O asynchronously (in the first animation frame). You may need to defer more than 2 frames depending on your setup.

Boundarizer is an excellent script from Paul Lewis that highlights layout boundaries in your page.

I’ve taken that script and bundled it into a Chrome extension to make its use far easier.

Install the extension

This is currently a beta version, feedback and pull requests welcome :)

Checkout the source on GitHub.

I have lots of calls to jQuery’s focus() method in my sites:

$('input:first').focus();

However on mobile devices you might not want to be calling this, because focus() doesnt open the keyboard, but does focus the element. Under certain cirumstances you might want to prevent the focus from happening to give a better user experience.

You can do this with the following code:

$.event.special.focus = {
	trigger: function (e) {
		e.preventDefault();
		return true;
	}
};

You will only want to apply this when your responsive site is being displayed on a mobile device.

thanks to pushOK on StackOverflow for this jQuery insight.

Check out this blog post:

http://www.pretentiousname.com/timesync/

Essentially you need to create a scheduled task that runs

W32tm.exe /resync

As often as you deem necessary (I have chosen hourly).

Here is an exported scheduled task that you can use:

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2013-11-28T09:19:59.2378</Date>
    <Author>OPS-02\Administrator</Author>
  </RegistrationInfo>
  <Triggers>
    <CalendarTrigger>
      <Repetition>
        <Interval>PT1H</Interval>
        <StopAtDurationEnd>false</StopAtDurationEnd>
      </Repetition>
      <StartBoundary>2013-11-28T09:17:40.4914</StartBoundary>
      <Enabled>true</Enabled>
      <ScheduleByDay>
        <DaysInterval>1</DaysInterval>
      </ScheduleByDay>
    </CalendarTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author">
      <UserId>S-1-5-19</UserId>
      <RunLevel>HighestAvailable</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>true</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>true</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession>
    <UseUnifiedSchedulingEngine>false</UseUnifiedSchedulingEngine>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>P3D</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>%windir%\system32\sc.exe</Command>
      <Arguments>start w32time task_started</Arguments>
    </Exec>
    <Exec>
      <Command>%windir%\system32\w32tm.exe</Command>
      <Arguments>/resync</Arguments>
    </Exec>
  </Actions>
</Task>

This shall be a dumping ground that I keep updated with useful resources for optimising web sites.

Tools

Blogs

Videos

Tweeps to Follow

Three simple steps:

openssl pkcs12 -in mycert.pfx -out mycert.txt -nodes

Then, to generate your encrypted private key

openssl rsa -in mycert.txt -text -out mycert.key

And your certificate:

openssl x509 -inform PEM -in mycert.txt -out mycert.cer

Bosh.

The other day something happened that caused the insert part of me running the SQL Azure Migration Wizard to abort, leaving me with a bunch of local .dat files and no data on my destination server.

To upload the data, you need to run the following command:

bcp database.dbo.tablename in dbo.tablename.dat -n -U username -P password -S remote.server.address.com -b 200 -h"TABLOCK"

You can play with various values for -b, the batch size. I found 200 worked reasonably although I didn’t investigate too much.

Here is a great post on troubleshooting your AWS ELB.

The point that caught me out for about 10 hours today was that if you have your ELB configured for multiple Availability Zones, it doesnt matter if your assigned instance list doesn’t contain any instances from some of the AZs, it will still route traffic to those zones, which will get lost and result in a 503 (or 504/324).

So, DONT assign AZs that dont have any in-service instances running.

You want your site to issue far-future cache expiry values for resources like CSS and JS to reduce bandwidth usage and decrease page load speed.

However, when you release new code, you want everyone to receive this a.s.a.p.
But how do you achieve this when they all have cached versions that are cache-valid for a week or more?

Here’s what I do.

Create yourself a class such as this:

public static class Cacher
{
	public static readonly string Value;

	static Cacher()
	{
		Value = DateTime.UtcNow.ToString("yyMMddHHmmssfff");
	}
}

Then, change your script and css tags from:

<link rel="Stylesheet" type="text/css" href="/assets/css/all.css" />

to:

<link rel="Stylesheet" type="text/css" href="/assets/css/<%: Cacher.Value %>/all.css" />

You can then use a mod_rewrite/asapi_rewrite rule to remove the value:

RewriteRule ^assets/css/[^/]+/all.css /assets/css/all.css [L,NC]

The reason you want the value in the path and not in the query string is that some caches refuse to cache content on URIs which include querys, regardless of the cache-control headers.

Alternatively, you could make the value be the current assembly version. It depends on your use-case.

Do you want to pull GA data into your site’s admin area? Here’s how to do it as simply as possible.

  1. Read this: https://developers.google.com/analytics/devguides/reporting/core/v3/reference
  2. Then this: https://developers.google.com/accounts/docs/OAuth2WebServer#refresh
  3. Then this: https://developers.google.com/analytics/devguides/reporting/core/dimsmets

Right, now go here: https://code.google.com/apis/console/ and create yourself an App, and a Client ID for a Web Application, so you end up with something less blurry than this:

Next, replace the XXXXXX in the below with the Client ID from above:


https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=XXXXXX&redirect_uri=https%3A%2F%2Flocalhost&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fanalytics.readonly&access_type=offline

and visit this in your browser, click “grant access/allow”.

You’ll end up on:

http://localhost/?code=yyyyyy

“yyyyyy” is your “Code”, keep this.

Next, open up Fiddler and issue this request:

POST https://accounts.google.com/o/oauth2/token HTTP/1.1
Host: accounts.google.com
Content-Type: application/x-www-form-urlencoded
Content-Length: ???

code=YYYYY&client_id=XXXXXX&client_secret=ZZZZZ&redirect_uri=https%3A%2F%2Flocalhost&grant_type=authorization_code

Replace YYYYY with your Code, XXXXX with your Client Id and ZZZZZ with your client secret.

Bang, get a response like this:

{
  "access_token" : "PPPPPPPPP",
  "token_type" : "Bearer",
  "expires_in" : 3600,
  "refresh_token" : "QQQQQQQQ"
}

Store the access_token and refresh_token, you’ll need these.

Now, make yourself a request!

replace DDDDD with the IDs of the profile you want to report on, find that here

GET https://www.googleapis.com/analytics/v3/data/ga?ids=ga:DDDDDDD&metrics=ga:visits&start-date=2012-06-01&end-date=2012-06-25 HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Authorization: Bearer PPPPPPPP

Then, when your token expires, request a new on like this:

POST https://accounts.google.com/o/oauth2/token HTTP/1.1
Host: accounts.google.com
Content-Type: application/x-www-form-urlencoded
Content-Length: ???

refresh_token=QQQQQQQQ&client_id=XXXXXX&client_secret=ZZZZZ&grant_type=refresh_token

Next, send me beer for writing the only tutorial on the ENTIRE INTERNET that explains this process concisely.