Archives for posts with tag: web development

I started playing with AppCache today after working at better offline support for LunchTable. Though information is well-dispersed on the topic (especially when it comes to using Google Fonts), I eventually sorted it.

Step 1. Add a cache manifest file

For more details, MDN is awesome.

My landing page contains <html manifest="manifest.appcache">, and manifest.appcache contains (abridged):

CACHE MANIFEST
# v1
NETWORK:
*

CACHE:
css/main.css
fonts/fontawesome-webfont.eot?v=4.0.3
fonts/fontawesome-webfont.eot?#iefix&v=4.0.3
fonts/fontawesome-webfont.woff?v=4.0.3
fonts/fontawesome-webfont.ttf?v=4.0.3
fonts/fontawesome-webfont.svg?v=4.0.3
js/myjs.js

The * under NETWORK: ensures that everything we don’t explicitly set here isn’t cached, because the next time we refresh it actually won’t load (see?).

Also note that we include query strings in the manifest; the CSS file references them this way, so we ensure they match here, because the browser cares.

Step 1.1. Serve the manifest file correctly

Your .appcache file should be served with the text/cache-manifest MIME type. On Apache, I added this to my universal server configuration:

AddType text/cache-manifest .appcache

Step 2. Host all previously remote files

Browsers also seem to care about caching files only from the application domain. I stopped using the FontAwesome CDN without a problem, but Google Fonts provided another hurdle.

Step 2.1. Host your Google Fonts

Based on some comments on this 2010 post, I found a simple script to download the font you want, and added a user agent for iPhone, though it doesn’t seem to do anything different right now (I was hoping to get SVGs), and needs testing.

family='Noto+Sans'; for url in $( { for agent in 'Mozilla/5.0 (X11; Linux i686; rv:6.0) Gecko/20100101 Firefox/6.0' 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1' 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)' 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_4 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25' ; do curl -A "$agent" -s "http://fonts.googleapis.com/css?family=$family" | grep -oiE 'http://[a-z0-9/._-]+'; done } | sort -u ) ; do extn=${url##*.} ; file=$(echo "$family"| tr +[:upper:] _[:lower:]); echo $url $file.$extn; curl -s "$url" -o "$file.$extn"; done

Simply replace Noto+Sans with the font you want, and add your newly downloaded fonts with something like this CSS in place of your old one-liner:

@font-face {
    font-family: 'Noto Sans';
    font-style: normal;
    font-weight: normal;
    src: url('/fonts/noto_sans.eot');
    src: url('/fonts/noto_sans.eot?#iefix') format('embedded-opentype'),
         url('/fonts/noto_sans.woff') format('woff'),
         url('/fonts/noto_sans.ttf') format('truetype');
}

Step 3. Test

To see what resources you’re caching on your site (as well as others), visit:

Chrome: chrome://appcache-internals
Firefox: about:cache?device=offline

Step 4. Read more

This is just the start, and there are a lot of additional concerns this kind of caching can add to your web application. But hopefully this will get you started. Besides LunchTable, forecast.io is another great app that uses this (look how fast it loads the second time!). $ logout

Advertisements

Recently LunchTable received an influx of spam registrations: marked by similar-looking nonsensical usernames, “real names” that didn’t match their gender, and email addresses on adult sites. I didn’t want to use CAPTCHA to mitigate this, because I personally die a little inside at each CAPTCHA encounter, so I took another route.

With a little help from StackOverflow (can’t find the link now, will update), I found a clever solution that banks on spam scripts not executing Javascript. Less than 12 hours of being live captured and prevented two spam registrations. I start by adding a hidden input field to my registration form and filling it with default text:

<input type="hidden" id="robot" name="beep-beep-boop" value="iamspam" />

Next, I add a little Javascript to change this value to a numeric value, and subsequently increment it every second (this will come in handy later):

var botZapper = function() {
    if (document.getElementById("robot")) {
        a = document.getElementById("robot");
        if (isNaN(a.value) == true) {
            a.value = 0;
        } else {
            a.value = parseInt(a.value) + 1;
        }
    }
    setTimeout(botZapper, 1000);
}
botZapper();

Finally, on the server side, I do a simply check (in PHP):

$botTest = $_POST['beep-beep-boop'];
if ( $botTest == "iamspam" || !is_numeric($botTest) || $botTest < 10) {
    // This appears to be spam!
    header("Location: /");
    exit;
}
// ...database INSERT code untouched by bots...

This checks if any of the conditions are true, indicating a bot:

  • First, if the value hasn’t changed; meaning the user didn’t have Javascript enabled.
  • If the submitted value is other textual information we didn’t expect.
  • If JS was enabled, but the form was submitted within 10 seconds.

If these tests fail, I mercilessly redirect the bot to the home page with no “fail” message! My scorn is certainly felt.

Your time threshold will definitely vary depending on the length of your form, and you will need to accommodate users without Javascript enabled at all. However, at the time of this sentence’s writing, I’ve captured four attempted spammings from China, all who never updated the hidden input field (failing at the first test).

$ logout

I’ve made a lot of from-scratch websites in the years Chrome has been around (and before that, Firefox). In the old days—around IE 7 or so—I remember hoping to support old versions of Internet Explorer. I would test thoroughly and spend hours on workarounds for the archaic browsers. But not anymore.

With Chrome and Firefox on a quick update schedule, it’s obvious that they are doing web browsers right: quick iterations of browsers that keep up with the web that they provide the lens to. Not faux-modern browsers.

So I no longer address older browsers in the websites I create with anything more than a message telling users to upgrade. One site I’ve made, LunchTable, will simply let you know your browser is out of date before you can log in if you’re using IE (made easy through conditional statements), since only IE 9 has some CSS3 features I use, and IE 10 finally supports WebSockets for chat. Other browsers get a lazier approach that works well: feature detection. Before a user can chat, I check if WebSockets are supported— no need for me to research and test exhaustively, or update the tests when new browser versions come out.

The company I work for has a web editor that utilizes a ton of browser APIs (like FileReader) to create a seamless “editor” that works entirely on the client-side. While Opera happens to break for mysterious reasons and IE apparently doesn’t know every CSS2 or 3 selector, every other browser can check for the needed functionality and alert the user about certain functions that just won’t work with their current browser. This allows, for example, Safari 5.1 users to still log in and use most features of the application, but when they can’t upload images, we expect they won’t be surprised.

This approach puts more of a burden on the end user, which my old CS professor used to argue vehemently against (that the developer should be trimming input data and formatting it correctly, for example). But it is only the burden of keeping your software up to date— a responsibility of any computer user. And in the case of IE, this approach encourages users to exercise their freedom of choice in the web browser market. Truly, a win for everyone.

On the last from-scratch CMS I built (for Radford University’s EMS), I utilized jWYSIWYG for the content editor. It was easy to implement, but had a few bugs (including with the content manager). Before that, I used TinyMCE and its MCFileManager to power Dean J. Baer‘s site, which both worked great, but the file manager required licensing fees. Currently I’m looking at these open source options:

The deciding factor here will mostly be how easy it is to add more robust media features.

I like the idea of on-the-page, limited section editing that Mercury provides, though I’m not a fan of the OS X aesthetic. And I also like what looks to be a simple, clean interface and backend for CLEditor. But this is all conjecture right now, so it’s time to dig in.

$ logout

I learned about LESS when I started working for the company I do now. And I can no longer live without it, if anything, for simple nesting of rules. The increased intuitiveness and readability you get from doing stylesheets this way is amazing. The Mixins are freakin’ great, too. Seriously, who wants to look up all the vendor-specific prefixes for all the latest CSS abilities every time?

As you might guess, I’ve switched gears to frontend development.

$ logout

This is my first time using HTML5 Boilerplate. I noticed a lot of people have been talking about it, so I decided to give it a go myself. First impressions (a good judge in the web world):

  • I’ve never liked templates, and this one doesn’t change my mind
  • Like any pre-existing code base, though minimal, you have to learn it before you can begin actually creating things (i.e. creating is no longer fun)
  • At best, I’m getting IE support out of this (and I’m a screw-IE kind of web developer)

I thought since this project is for others and not me, it should be professional and support the myriad microformats and Apple icons and such. But I’m currently doing more work tweaking prefabricated names and changing relative URLs to absolute and thinking about how nothing is how I want it to be.

And I’ve tricked myself into thinking I need a package like this. The work and detail there is great; I already learned things by looking through the code and checking some referenced links. But I can implement these basic HTML5 compatibility features with the minimum necessary developments I’ve kept up with and mostly, as a personal preference, I need full sovereignty over my work. So I’ll be starting from scratch.

$ logout