Monday, November 30, 2015

3rd Annual Fairfield County Beer Advent Calendar (2015)

This year with Bill Farrell and Steve Spanos.

We have a few extras this year:
Friday, December 26, 2015: Evil Cousin - Heretic Brewing Company
Saturday, December 27, 2015: TrIPL - Jack's Abby Brewing
Sunday, December 27, 2015: Doubletyme DIPA - Crop Bistro & Brewery

Thursday, May 7, 2015

I Just Want to Run my Watch App in the Simulator!

I spent the better part of a day and a half trying to add a Watch app to an existing iOS 7 project and run it in the simulator, but ran into two primary problems. First, the repeated appearance of the SPErrorInvalidBundleNoGizmoBinaryMessage error and second, the “Waiting to attach” message in the Xcode Debug Navigator. This seems to be happening to lots of others as well. Let me outline what worked for me in the hopes that it saves you from spending as much time as I did on this.

Note that this pertains to using Xcode 6.3 with an iOS project having a deployment target of iOS 7.0. If your situation is different, this may not help you at all.
  1. Select the project target of your iOS app. Go to the General tab. Make note of the version and build values there.  Select your WatchKit Extension target and your WatchKit App target and set them to be the exact same values as the iOS app. If the version is 1.2, set the version for all three targets to be 1.2, If the build is 32, set the build for all three targets to be 32.
  2. Select the project target of your iOS app. Go to the General tab. Make note of the team setting. Select your WatchKit Extension target and your WatchKit App target and set them to be the exact same value as the iOS app.
  3. Select the project target of your WatchKit App – NOT the iOS app, NOT the WatchKit Extension. Got to the Build Settings tab. Find the iOS Deployment Target setting. Set that to be iOS 8.2. I’m not clear on the reasons, but it CANNOT be iOS 8.3. The extension can be set to 8.3, but not the app.
  4. Select the project target of your WatchKit Extension and then your WatchKit App. Both of mine showed warnings for my provisioning profile. In my previous attempts to get to this point, I had created new App IDs using the bundle identifiers Xcode created in the Member Center. Xcode then created provisioning profiles on its own. I would suggest you create the App IDs, got to the Build Settings tab and set your Provisioning Profile to be Automatic, quit Xcode, re-launch Xcode, open the project, build, and hope that this voodoo dance gets you what you need. You may need to refresh provisioning profiles Xcode has, but I did not. 
Now, at this point I could run the iPhone 6 8.3 simulator and see the time and charging icon in the top-right corner of the Watch simulator. BUT, if I switched to one of the 8.2 simulators, specifically the iPhone5s 8.2 simulator in my case, THEN I started to see the “Waiting to attach” message in the Xcode Debug Navigator, where it sits indefinitely.

To recover from this problem, sometimes I had to reset the simulator and do a deep clean (Cmd + Shift + Option + K) and sometimes I just needed to switch back to the iPhone 6 8.3 simulator.

I honestly don’t know what new issues will come up as I try to test on a real device, but this at least got me to the simulator.


Wednesday, November 26, 2014

2nd Annual Fairfield County Beer Advent Calendar (2014)

Sunday November 30: Narragansett Bohemian Pilsner
Monday December 1: Heavy Seas Peg Leg Imperial Stout
Tuesday December 2:  Evil Twin Low Life
Wednesday December 3: Two Roads Road 2 Ruin Double IPA
Thursday December 4: Gasthaus & Gosebrauerei Berliner Style Weisse
Friday December 5: Otter Creek Wolaver's Oatmeal Stout
Saturday December 6: Dixie Blackened Voodoo Lager

Sunday December 7: Victory Golden Monkey
Monday December 8: Traveler Illusive Traveler Shandy
Tuesday December 9: Samuel Smith's Imperial Stout
Wednesday December 10: Kcco Black Lager
Thursday December 11: Saint Arnold Santo
Friday December 12: Rogue Hazelnut Brown Nectar
Saturday December 13: Shipyard Blue Fin Stout

Sunday December 14: Flying Dog Old Scratch Amber Lager
Monday December 15: Geary's Hampshire Special Ale
Tuesday December 16: Harpoon White Unfiltered Wheat Beer
Wednesday December 17: Thornbridge Saint Petersburg Imperial Russian Stout
Thursday December 18: Schneider Weisse Aventinus Wheat Doppelbock
Friday December 19: Ayinger Brauweisse
Saturday December 20: Two Roads Unorthodox Russian Imperial Stout

Sunday December 21: Ballast Point Sculpin IPA
Monday December 22: Maine Beer Company King Titus Porter
Tuesday December 23: North Coast Brother Thelonious
Wednesday December 24: Samuel Adams Thirteenth Hour Stout

This year with Bill Farrell!

Source: Total Wine of Norwalk, CT, Whole Foods of Fairfield, CT, and Mo's Wine & Spirits of Fairfield, CT

Tuesday, October 28, 2014

The Order of Adapters in Lawnchair

If you are using Lawnchair and you want to explicitly define the order in which adapters are used, then you must open the unminified file and make sure they appear IN REVERSE ORDER THAT YOU WANT THEM in the source.

I have no idea if this is a bug new to version 0.6.1, but you can inspect Lawnchair.adapters in your dev tools of choice to see the order and run tests to confirm they are what you expect -- and I strongly recommend you do this.

For me, I have them pasted in my Lawnchair file as window name, Web SQL (a separate download on the site), then Local Storage, which puts them in the order for actual use: Local Storage, Web SQL, then window name.

Saturday, June 7, 2014

jQuery show() and hide() Don't Seem to Work with jQuery Mobile

That's not entirely true. Chances are you're running into this type of situation.

I was trying to hide a set of ui-block-* elements in a JQM grid when the user tapped a button. On the same set of ui-block-* elements, my media query for an iPhone was setting display: block !important. When the user would tap the button the elements would animate to hide but then suddenly re-appear.

This was not a problem on an iPad or in a desktop version of Chrome. But on the iPhone (iOS 6 and 7 at least), the !important directive was winning -- was dictating the final display.

That !important directive was unnecessary in my case, so when I removed it the calls to hide() and show() began working as expected.

Quick and Dirty Forms Auth to Test Something Else

Recently I needed to test how an Application Cache would operate, if at all, when resources where protected by ASP.NET forms authentication within a ASP.NET MVC application. I didn't need a full forms auth implementation, just something quick. I've done most of this before, but had nearly forgotten the details.

The Web.config:

    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" timeout="2880">
        <credentials passwordFormat="Clear">
          <user name="admin" password="password"/>
        </credentials>
      </forms>
    </authentication>

The AccountController with the [Authorize] attribute:

        [AllowAnonymous]
        public ActionResult Login(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            return View();
        }

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Login(LoginModel model, string returnUrl)
        {
            if (ModelState.IsValid && FormsAuthentication.Authenticate(model.UserName, model.Password))
            {
                FormsAuthentication.SetAuthCookie(model.UserName, false);
                return RedirectToLocal(returnUrl);
            }
 
            ModelState.AddModelError("", "The user name or password provided is incorrect.");

            return View(model);
        }

        private ActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }

            return RedirectToAction("Index", "Home");
        }

The LoginModel class:

    public class LoginModel
    {
        [Required]
        [Display(Name = "User name")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }
    }

The Login.cshtml file:

@model OfflinePartial.Models.LoginModel
@{
    ViewBag.Title = "Login";
}
<h2>Login</h2>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)
    <p>
        @Html.LabelFor(m => m.UserName)
        @Html.TextBoxFor(m => m.UserName)
        @Html.ValidationMessageFor(m => m.UserName)
    </p>
    <p>
        @Html.LabelFor(m => m.Password)
        @Html.PasswordFor(m => m.Password)
        @Html.ValidationMessageFor(m => m.Password)
    </p>
    <p><input type="submit" value="Log in" /></p>
}

Saturday, February 15, 2014

First attempt at a Web Worker for a Lunr.js index

Most of the applications I've been building lately are internal mobile web apps using jQuery Mobile and they function offline using the Application Cache plus a bit of Local Storage. The latest one also has a search requirement using some textual data even if the user is offline. I decided to give Lunr.js a try and it works great. The one problem I've had is that it takes a couple of seconds to index the 400+ reports I'm trying to handle on an iPad 2 running iOS 7. It's not awful, but every time that loading spinner hangs I squirm.

Enter Web Workers.

This seemed like a perfect opportunity to try out a Web Worker. The search page has a couple of other features, so I don't want the user to be forced to wait for indexing to complete to use the page and, of course, I don't want to lock up the UI at all. Index creation only has to happen once, and moving it off to a background thread until complete seemed to make sense. The only issue to keep in mind, and it comes into play in my scenario, is that data is copied from the main thread to the worker thread. I have noticed a bit of a UI hitch when that fires, but it's been a huge improvement.

Here's a stripped down example with comments. You will need to run this from a web server. You cannot run it using local file:// access as far as I know.

The HTML:

<!DOCTYPE HTML>
<html>
<head>
 <title>Web Worker with Lunr.js</title>
</head>
<body>

 <p>Building...</p>

 <script type="text/javascript" src="javascript/lib/underscore-min.js"></script>
 <script type="text/javascript" src="javascript/lib/lunr.min.js"></script>
 <script type="text/javascript" src="javascript/lib/jquery-1.11.0.min.js"></script>

 <script type="text/javascript" src="javascript/SearchIndexWorker.js"></script>
 <script type="text/javascript" src="javascript/SearchMobileModule.js"></script>
 <script type="text/javascript" src="javascript/app.js"></script>

</body>
</html>

A small app.js file to bootstrap the process:

// You'll need to run this from a web server.

var app = app || {};

// I have two search modules: mobile and desktop.
app.isMobileDevice = true;

//  We'll use local data for this example. This is a small set.
//  The benefit comes when you have several hundred of these.
var data = {
    reports: [
        { 'reportId': 1, 'reportTitle': "Jane Doe visited Company ABC to review business renewal." },
        { 'reportId': 2, 'reportTitle': "John Smith visited XYZ, Inc. to review sales over lunch." }
    ]
};

// You'll have to decide when to init the index.
// We currently do it if the user visits the reporting section of the app.
$(function() {
 app.search.mobile.init(data.reports, $('p'));
})

I have two different search modules, one for desktop users which communicates with the server to retrieve the full set of data and one for mobile users which is limited to the past 6 months of data and works offline.

(function (app, $, _, lunr) {

    // Private
    
    var reportsPointer = [];
    
    var index;

    // Public
    
    var search = {};
    
    search.setIndex = function (serializedIndex) {
        // Source: http://www.garysieling.com/blog/building-a-full-text-index-in-javascript
        index = lunr.Index.load(serializedIndex); 
    };

    search.init = function (reports, $uiNotice) {
        console.log('Attempting to init the Lunr.js index.');
        
        // We're going to hold a pointer to the reports.
        reportsPointer = reports;

        // Create the Web Worker.
        // You may see a browser error in Chrome or Safari while testing that says:
        //   'Uncaught ReferenceError: importScripts is not defined'
        // Not sure why that happens, but it works.
        var worker = new Worker("javascript/SearchIndexWorker.js");
        
        // Here we're adding a listener for messages coming back from the worker to us. 
        worker.addEventListener('message', function (evt) {
            // The evt.data property has the response from the worker.
            // The Web Worker cannot modify a global, so it passes back the index for us to use.
            search.setIndex(JSON.parse(evt.data)); 

            // We'll update the UI here for now.
            $uiNotice.html('Ready! Open the console and try something like: app.search.mobile.query("Jane")');
            console.log('The Lunr.js index is ready.');
            
            // Memory footprint will be large with all that data copied around. This kills the worker.
            worker.terminate(); 
        }, false);
        
        // Sends a message to the worker and passes it *a copy* of the data it needs. 
        // I'm sending a string to be consistent across browsers.
        worker.postMessage(JSON.stringify({ reports: reports }));
    };

    search.query = function (query) {
        if (!query) return [];

        // We're going to keep things simple and handle all the results here.
        // Searching with Lunr.js isn't really the point here.
        var lunrResults = index.search(decodeURIComponent(query));
        _.each(lunrResults, function(el, idx, list) {
            $('<pre>').text(JSON.stringify(_.findWhere(reportsPointer, { reportId: parseInt(el.ref, 10) }))).appendTo('p');
        });
    };

    app.search = app.search || {};
    app.search.mobile = search;

}(window.app = window.app || {}, jQuery, _, lunr));

Finally, the Web Worker itself.

// This is the Web Worker script. No DOM, window, or document access at all.

// Import the scripts we'll need in this worker.
importScripts('lib/lunr.min.js', 'lib/underscore-min.js');

var index = lunr(function () {
    this.field('reportTitle');
    this.ref('reportId');
});

var buildIndex = function (reports) {
    if (!reports || reports.length === 0) return;

    for (var i = 0; i < reports.length; i++) {
        index.add(reports[i], false); // Don't emit any Lunr events.
    }
};

// This is a listener on the worker for incoming messages.
self.addEventListener('message', function (evt) {
    // evt.data has the data passed to this worker.
    var data = JSON.parse(evt.data);

    if (data.reports) {
        buildIndex(data.reports);
    }

    // Now we send a message back to the script that created this worker.
    self.postMessage(JSON.stringify(index.toJSON()));

    // Memory footprint can be large with a lot of data copied around. This kills the worker.
    self.close(); 
}, false);

I hope someone else finds this useful!

Saturday, November 16, 2013

1st Annual Fairfield County Beer Advent Calendar (2013)

Sunday December 1: De Struise, Svea IPA 4
Monday December 2: BrewDog, 5 A.M. Saint Red Ale 3.5
Tuesday December 3: Founders, Pale Ale 3.5
Wednesday December 4: Lagunitas, Maximus IPA 3.5
Thursday December 5: Captain Lawrence, Winter Ale 3
Friday December 6: Emelisse, Double IPA 3.5
Saturday December 7: Great Divide, Colette Farmhouse Ale 2.5

Sunday December 8: Flying Dog, In-Heat Wheat 3.5
Monday December 9: Dogfish Head, 60 Minute IPA 2.5
Tuesday December 10: Paulaner, Salvator Doppel Bock 3.5
Wednesday December 11: BrewDog, Libertine Black Ale 4
Thursday December 12: North Coast, Old Stock Ale 2013 3.5
Friday December 13: Heavy Seas, Peg leg Imperial Stout 4
Saturday December 14: Great Divide, Yeti Imperial Stout 3

Sunday December 15: Caulier, Blonde Belgian Ale 2.5
Monday December 16: Weisses, Tap 7 Original 3.5
Tuesday December 17: Lagunitas, Censored Rich Copper Ale 3
Wednesday December 18: Petrus, Aged Pale 0
Thursday December 19: Ithaca, Dark Vine Black IPA 3.5
Friday December 20: La Rulles, Triple 3.5
Saturday December 21: Flying Dog, Gonzo Imperial Porter 4

Sunday December 22: Troegs, Mad Elf Ale 1
Monday December 23: North Coast, Old Rasputin Russian Imperial Stout 4
Tuesday December 24: De Struise, Pannepot 2011 4.5

Total cost: $96.05
Source: DeCicco's of Brewster, NY

Saturday, October 19, 2013

Granola Bars


  • 2 cups rolled oats
  • 1/4 cup wheat germ
  • 3/4 cup sunflower seeds
  • 1 cup of peanuts, crushed
  • 1/3 cup brown sugar
  • 1/2 cup honey
  • 4 tbsp unsalted butter
  • 2 tsp vanilla extract
  • 1/2 tsp kosher salt
  • 8 oz  dried fruit (raisins, dried cranberries, etc)
Preheat the oven to 400.

Mix the oats, wheat germ, sunflower seeds, and peanuts on a baking sheet and bake for about 10 minutes, stirring every few minutes. I usually put the wheat germ in after the first time I stir because it seems to brown faster than everything else and the flavor becomes too strong for my tastes.

Prepare a glass baking dish or similar container about 11x13 by lining it with wax paper. You may want to spray it lightly with Pam or oil.

Put the brown sugar, honey, butter, vanilla extract, salt into a small saucepan and warm on simmer until it melts together, stirring constantly.

Put the baked mixed together with the liquid mix in a large bowl and stir well. Pour that mixture into the lined glass baking dish and press hard somehow to compress it together. I use a 9x9 glass baking dish. Let it sit 2-3 hours to cool and cut into bars.

Tinker with the recipe to your liking. This originally contained more wheat germ and brown sugar, but I toned it down over time.

Sunday, August 11, 2013

Convert JavaScript Epoch Milliseconds to C# DateTime

// Passed into the service; this example is UTC 1/1/2012, so it'll end up being 5:00am Eastern
string millisecondsFromEpochJavaScript = "1325394000000";

// Start at 1/1/1970 since that is considered the epoch date.
// Multiply by 10000 to convert milliseconds to "ticks", which are 100 nanoseconds.
DateTime converted = new DateTime(1970, 1, 1).AddTicks(Convert.ToInt64(millisecondsFromEpochJavaScript) * 10000);

Wednesday, May 8, 2013

JSON Date UTC Formatting for iOS to Web API Communication

Noob, but I have an iOS app POSTing data to a .NET Web API service and I want to keep the date/time data in UTC, so here's what I'm doing when I serialize my JSON.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]]; // Keeps time in UTC.
NSDate *myDate = [NSDate date];
NSString *myDateString = [dateFormatter stringFromDate:myDate];

Thursday, October 11, 2012

IIS7, JSON Compression, and You

How do you know if your requests are being compressed?

Fiddler. Select resource > Response Section > Transformer tab. Also, the Headers tab should have a Transport section listing Content-Encoding: gzip.

How do I enable it in IIS 7?

First, just get Dynamic and Static Compression running by going to the IIS level or the site level, selecting the Compression feature, and checking the boxes. You may need to install it if you don’t see these options.

How come my JSON requests aren’t being compressed?

The JSON MIME type isn’t compressed by default. That MIME type is application/json.

Great. So how do I add it?

You can use the appcmd.exe or you can edit the file directly. C:\Windows\System32\inetsrv\Config\applicationHost.config is locked by the system fairly tightly, but I did the following. Make a backup. From the Start menu, right click Notepad and start as administrator. Use File > Open to go to the config file. Go to the <httpCompression> node. In both <dynamicTypes> and <staticTypes>, add both <add mimeType="application/json" enabled="true" /> and <add mimeType="application/json; charset=utf-8" enabled="true" />. Note the charset in the second item. That has to match what is sent from the server, exactly. Again, I pulled that from Fiddler by inspecting one of the responses' headers and checking the Content-Type value. I restarted at the IIS level to get the changes recognized.

Saturday, September 8, 2012

Vegetable Casserole

We had a spurt of production from our vegetable garden and we weren't sure what to do with it. We found a vegetable casserole recipe, modified it, and were extremely happy with the results.

You'll need the following.

  • 9 x 9 Pyrex dish
  • Frying pan
  • Eggplant
  • Peppers
  • Tomatoes
  • Onion
  • Shredded mozzarella cheese
  • Breadcrumbs
  • Salt, pepper, and cooking oil (I used a mix of vegetable and olive oil)
  • 1 cup of tomato sauce
Here are the steps.
  1. Slice enough eggplant to cover the bottom of the Pyrex dish and fry it in the cooking oil. I cut the slices nice and thick so they wouldn't fall apart after cooking. You'll need two layers, one for the bottom and one for the top.
  2. Cover the bottom of the Pyrex dish with the eggplant in a neat layer.
  3. Slice the peppers and spread them on top of the eggplant. I used multiple kinds of peppers, including jalapeño. Note that the peppers are not cooked.
  4. Add salt and pepper to taste on the peppers. Sprinkle a little mozzarella cheese on them as well.
  5. Slice the tomatoes and spread them on top of the peppers in a neat layer. Again, keep the slices a bit thick. Note that the tomatoes are not cooked.
  6. Carmelize the onion. Spread the carmelized onion on top of the tomatoes.
  7. Add another layer of fried eggplant on top of the onion in a neat layer.
  8. Sprinkle a little mozzarella cheese on top of the eggplant. 
  9. Sprinkle a little breadcrumb on top of the mozzarella.
Preheat the oven to 375 and bake for about 25 minutes or until the top is brown and you see a little bubbling on the sides. Let it sit for about 5 minutes and cut into quarters. Have some bread with this on the side.
As an aside, avoid raw onion in this. It's too overpowering.

Saturday, March 3, 2012

Loading and Refreshing the Application Cache of Multiple Sites

THE PROBLEM:

We have multiple web applications that are offline-capable using the HTML5 Application Cache feature. We want to give users the ability to go to one location to load / refresh / update / synchronize all applications and avoid the need to go to each and every application individually. Details of interest: we are only targeting the iPad 2 (or higher) and each application is not guaranteed to be on the same domain or sub-domain.

THE SOLUTION:

The HTML5 Cross-Document Messaging feature + an iframe + jQuery. Certainly feels little goofy, but it works.

THE DETAILS:

At least at this stage, there doesn't appear to be a way to determine whether the Application Cache of a specific site is up-to-date without loading that site. Said another way, we don't know if a site's Application Cache contains stale data until we load that site, and as soon as that site is loaded, it will begin updating, if necessary. The user must then wait until the browser is done fetching and storing the resources for the cache to actually be ready for offline use.

Therefore, if we could come up with a way to visit each site for the user, we could guarantee the newest manifest file would be checked and the cache updated.

There are several pieces to this puzzle.

1) One site, which we'll call "the loader," with the list of URLs for the target sites in a JavaScript array. I injected that list into the page server side, but they don't need to be. These are the individual web applications using the Application Cache so they can be available offline.

2) An iframe on the loader, which can be visible or hidden.

3) The individual web applications will each need a function to listen for requests.
function crossDocumentListener(event) {
//NOTE: There's no security in place here! Educate yourself before using!
if (event) {
var response = "origin:" + event.origin + ";request:" + event.data + ";location:" + window.location + ";response:" + window.applicationCache.status;
event.source.postMessage(response, '*');
}
}
window.addEventListener("message", crossDocumentListener, false);

I'm returning the origin, the original request data, the site's location, and the application cache status. The latter is what I want most.

4) A listener on the loader to get the responses. It is set to message the site loaded in the iframe every 2 seconds.

function responseListener(event) {
var response = event.data;
displayResponseVisible(event);

// 0 = uncached, 2 = checking, 3 = downloading
if(response.indexOf('response:0') > -1 || response.indexOf('response:2') > -1 || response.indexOf('response:3') > -1) {
if(!intervalId) { // We only set it once.
intervalId = setInterval(
function() {
getStatus();
}
, 2000);
}
}
// 1 = idle, 4 = updateready, 5 = obsolete
else {
if(intervalId) {
clearInterval(intervalId);
intervalId = undefined;
}

if(nextUrlIndex == -1) {
// The array is empty. We're done.
$('#message').prepend(completeMessage);
}
else{
// There are more URLs in the array to process so load the next one.
$.when(loadUrl(nextUrlIndex)).then(function() { getStatus(); });
}
}
}
window.addEventListener('message', responseListener, false);

5) More code to glue it altogether. Here are the key bits.

function getStatus() {
document.getElementById("app").contentWindow.postMessage('status', '*');
}

function loadUrl(arrayIndex) {
// Set the URL on the iframe, triggering it to load.
var iframe = $('#app');
$(iframe).attr('src', urls[arrayIndex]);

// Set the nextUrlIndex appropriately.
if(arrayIndex + 1 == urls.length) nextUrlIndex = -1;
else nextUrlIndex = arrayIndex + 1;

// http://www.elijahmanor.com/2011/02/jquerydeferred-to-tell-when-certain.html
var deferred = $.Deferred();
iframe.load(deferred.resolve);

return deferred.promise();
}

$(document).ready(function() {
$('#loader').click(function(event) {
$.when(loadUrl(nextUrlIndex)).then(function() { getStatus(); });
event.preventDefault();
});
});

I'm not crazy about the design of this, but with the glue code in place the details can be refined. As I refine it I'll try to update this post. If you have another way to approach this that you believe is somehow an improvement, I'd love to hear it.

Resources:

Sunday, February 5, 2012

iOS, Private Browsing, and the HTML5 Application Cache

This problem makes sense, but it caught me off-guard during debugging.

If you have a web application that uses the HTML5 Application Cache feature, the cache will not function properly with iOS Safari's Private Browsing setting on. The various JavaScript snippets that help with debugging will catch the most general error, which doesn't tell you much.

So if you have an app that seems to be caching properly in your desktop browser but not your mobile device, be sure to check your privacy settings!

Monday, December 26, 2011

Crabmeat Cream Sauce

We made this with lobster ravioli for a special occasion. The original recipe was for about one pound of pasta and in parentheses I've added my changes 3 pounds.
  • 2 ounces of unsalted butter (6 ounces for 3 lbs.)
  • 1 tablespoon of chopped shallots (1 large bulb for 3 lbs.)
  • 4 ounces of chunk crabmeat (8 ounces for 3 lbs.)
  • 2 ounces of Cognac (I substituted Grand Marnier because I had it on hand and used only 2 ounces for 3 lbs. because of its strong flavor)
  • 5 ounces of tomato sauce (15 ounces for 3 lbs.)
  • 10 ounces of heavy cream (30 ounces for 3 lbs.)
  • Salt to taste
In a pan or pot large enough to contain all the liquid, melt the butter and then sauté the shallots on medium heat until they are translucent.

Add the crabmeat and sauté for 2-3 minutes more.

Remove from the heat, add the Cognac, then put it back on the burner. PLEASE BE CAREFUL! The Cognac might ignite and result in a large flame. Don't burn your face off. Sauté for 2-3 minutes more.

Add the tomato sauce, cream, and salt. Cook until it reduces about halfway and becomes thicker.

Add the sauce to the cooked ravioli and let them sit for 1-2 minutes to absorb some of the sauce. Serve immediately.

Peach Cobbler in a Cast Iron Skillet

This is insanely good, especially if you like cast-iron cooking.
  • 6 tablespoons of unsalted butter
  • 1 cup of sugar
  • 1 cup of flour
  • 2 teaspoons of baking powder
  • 1/4 teaspoon of salt
  • 1 cup + 1 tablespoon of whole milk
  • 1 can of sliced peaches with their liquid (I have been substituting 1 jar of Trader Joe's peach halves, minus 2 halves, with about half the liquid in the jar)
Pre-heat the oven to 350 with a 10" or 12" cast iron skillet inside. After the oven is pre-heated, let it continue to heat for another 8-10 minutes.

Mix sugar, flour, baking powder, salt, and milk together into a batter.

Take the skillet out of the oven and melt the butter in it. I find this easier if I cut the butter into 1-tablespoon blocks. Pour the batter into the skillet. Spread the peaches on top of the batter. Pour the liquid on top of that. If you're using a 12" skillet, don't worry if the batter doesn't spread all the way out to the edges--it will rise to about 3/4 of an inch.

Bake at 350 for 30-40 minutes.

Monday, November 7, 2011

Testing Geolocation Locally with Chrome and URIs with file://

If you need to test geolocation in Chrome using local files -- where your URL is going to start with file:// -- you need to throw a switch when you start Chrome.

--allow-file-access-from-files

If you're a Launchy user like me, you can do this by starting to type Chrome, hitting Tab, then pasting in that switch.

This issue is documented and tracked here: http://code.google.com/p/chromium-os/issues/detail?id=13009

Sunday, November 6, 2011

VirtualBox on Mac OS X Lion Running Windows XP and Accessing a USB Drive

File this one under "odd."

While running VirtualBox 4.1.4 on Mac OS X Lion as the host and Windows XP 3 as the guest, I was having some trouble getting my Western Digital Elements external hard drive recognized on the guest. The external was formatted for Windows and all my data was added from a Windows machine, so I could read it from the Mac, but I couldn't write to it. The Mac would recognize the external, but not the Windows guest. If I clicked on the USB icon at the bottom of the VirtualBox window, the external drive would be greyed out.

Oddly enough, if I started the VM first, had the Windows guest window selected, and THEN plugged in the external, XP would recognize it immediately. The host would not recognize it at all.

While the external was plugged into the Mac with my first attempts, I did add a filter for it under Ports > USB, but I'm not sure if that was really part of the solution.

Whatever. It's working!

Friday, October 28, 2011

Moving Thunderbird from a PC to a Mac

This was pure awesome sauce when I heard I could do it.

I had been using Thunderbird on a PC for about two years before needing to move over to a Mac, OS X Lion, to be specific. Thankfully, you can move your Thunderbird profile from a PC to a Mac with hardly any fuss at all.

1) Open up a terminal window. You should be in your user directory by default. If not, get there. The user Library directory is hidden now in Lion, so you have to unhide it with the following command. Once you run this, you can see it in Finder: chflags nohidden ~/Library

2) Exit Thunderbird if you have it running. In Finder go to Users/[user]/Library/Thunderbird/Profiles/[existing default profile]/. Copy the contents of your Windows profile directory into the existing default profile directory on your Mac.

Open Thunderbird and you're done! I had all my emails, my email server settings still worked, and all was well with the world.

As always, make sure you backup anything you're copying over.

Sources: