Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

Web developers were used to using cookies to store data in the user’s browser. You could then set how long you wanted the cookie to last. It could automatically be removed when the browser was closed or it could last longer. Overtime, cookies got a bad rap. There was also a relatively small size limitation.

HTML5 started a move away from cookies and to web storage. There are two types of web storage, sessionStorage and localStorage. Both of them use the same API signature, meaning that once you have an example of localStorage working, you also know how to access sessionStorage.

If they are both accessed the same way, then what is the difference? LocalStorage persists across browser sessions. You can think of it as persistent storage. LocalStorage is per origin Origin is defined as the protocol (e.g. http, https, etc), hostname and port number (e.g. 80, 443, 8080). This means that all of the pages from your website that are HTTPS can see the data set by another window, but the HTTP pages cannot see the data. This also means that if you are using multiple host names, you can’t share data across your domains.

SessionStorage has a shorter lifespan. It is intended for as long as the browser window is open. When the browser window is closed, this data is removed. Whereas localStorage is origin specific, sessionStorage is origin-window specific. Meaning, that even for the same domain, etc, the data is specific to the window that wrote it. This is something that cookies couldn’t support on their own. Any data that is written in sessionStorage is only visible by the window that wrote it.

Let’s look at how to use the web storage API. LocalStorage and sessionStorage are both accessed by global variables that are named after the type of storage, localStorage and sessionStorage. Meaning, that they are on the current window object (e.g. window.localStorage and window.sessionStorage). My examples will use localStorage, but the examples are the same for sessionStorage. In general, browsers allow up to 5MB to be stored. Much larger than the cookie size limit.

Does this browser support web storage

How do we determine if a browser supports localStorage? I saw two different ways. With that said, most browsers support web storage. IE8, FF 3.5, Chrome 4, Safari 4, Android 2.0, Chrome on Android 44.

        /**
         * Does this browser support HTML5 Local Storage.
         * @returns {boolean}
         */
        function supportsHtml5Storage() {
            try {
                return 'localStorage' in window && window['localStorage'] != null;
            } catch (e) {
                return false;
            }
        }

        /**
         * An alternative way to determine if a browser supports local storage.
         * @returns {boolean}
         */
        function supportsHtml5StorageAlternate() {
            return typeof(Storage) !== "undefined";
        }

Modernizer also has a method to determine if localStorage is supported.

// In FF4, if disabled, window.localStorage should === null.

// Normally, we could not test that directly and need to do a
//   `('localStorage' in window) && ` test first because otherwise Firefox will
//   throw bugzil.la/365772 if cookies are disabled

// Also in iOS5 & Safari Private Browsing mode, attempting to use localStorage.setItem
// will throw the exception:
//   QUOTA_EXCEEDED_ERRROR DOM Exception 22.
// Peculiarly, getItem and removeItem calls do not throw.

// Because we are forced to try/catch this, we'll go aggressive.

// Just FWIW: IE8 Compat mode supports these features completely:
//   www.quirksmode.org/dom/html5.html
// But IE8 doesn't support either with local files

Modernizr.addTest('localstorage', function() {
    var mod = 'modernizr';
    try {
        localStorage.setItem(mod, mod);
        localStorage.removeItem(mod);
        return true;
    } catch(e) {
        return false;
    }
});

How to write data

The data is stored in a key-value pair map. The value of the data is stored as a string. If you need it to be an integer, etc you will need to convert it to the value type that you need. The data is written by calling setItem. The example shows setting a value that is a string and an integer. Regardless, the value ends up as a string.

        /**
         * Sets a couple of items to local storage.
         */
        function setLocalStorageItem() {
            localStorage.setItem("foo", "bar");
            localStorage.setItem("max", 5);
        }

How to read data

Data is retrieved by calling the getItem method. Remember that the data comes back as a string and needs to be converted. Below are both examples. If the data was ‘5’, then retrieving the value of ‘5’ and adding 1 to it will result in ’51’ if you don’t convert the value to an integer. The value will be 6 if you convert it to an int, then add.

        /**
         * Gets an item from local storage.
         */
        function getLocalStorageAsString() {
            return localStorage.getItem("max") + 1;
        }

        /**
         * Gets an item from local storage.
         */
        function getLocalStorageAsNumber() {
            // local storage items are stored as a string
            return parseInt(localStorage.getItem("max")) + 1;
        }

How to remove data

Like most maps, there is the ability to remove an item from the map. However, unlike some other implementations, calling removeItem doesn’t return a value to indicate if it existed before it was removed.

        /**
         * Removes an item from local storage
         */
        function removeLocalStorage() {
            // removeItem does NOT return a value like Java does
            localStorage.removeItem("foo");
        }

There is also the option to remove all of the data that is in storage. This is accomplished by calling clear. I could not get this to work on Firefox 40.0.2.

        /**
         * Clear the contents of local storage.
         */
        function clearLocalStorage() {
            // clear did not work on FireFox 40.0.2
            localStorage.clear();
        }

How to query web storage for it’s contents

There is also the ability to get the number of items being stored in the local storage.

        /**
         * Gets the number of items in Local Storage.
         * @returns {number}
         */
        function getLocalStorageLength() {
            return localStorage.length;
        }

Once you have the number of items in a map, you can iterate over the items, get the key, then use the key to get the value.

        /**
         * Displays the contents of local storage.
         * @returns {string}
         */
        function displayLocalStorageContents() {
            var contents = "";
            var key = "";
            var value = "";
            for (var i = 0; i < localStorage.length; i++) {
                key = localStorage.key(i);
                value= localStorage.getItem(key);
                contents += "index=" + i + "\tkey=" + key + "\nvalue=" + value + "\n"
            }
            return contents;
        }

How to listen for storage events

Sometimes you will have the need to know that the storage has been changed. This will be especially useful if you are trying to coordinate the activities of multiple windows. You register an event listener by calling addEventListener, then implementing the a method to respond to the storage events. I didn’t see consistent behavior on this. I could call setItem, getItem and removeItem. I never got it to fire. I was able to make it fire in Chrome by manually changing the storage value. As of right now, the various browser implementations don’t seem to allow this to be of much use.

        /**
         * Register a local storage event listener.
         */
        function registerEventListener() {
            if (window.addEventListener) {
                addEventListener("storage", handleStorageEvent, false);
            }
        }

        /**
         * Handle the storage event.
         */
        function handleStorageEvent(storageEvent) {
            if (!storageEvent) {
                storageEvent = window.event;
            }
            alert("key=" + storageEvent.key +
                  " oldValue=" + storageEvent.oldValue +
                  " newValue=" + storageEvent.newValue +
                  " url=" + storageEvent.url);

        }

The entire file of examples

Here is the entire html file.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Java Ninja Local Storage prototype</title>
</head>
<body>
    <button onclick="alert(supportsHtml5Storage())">Local Storage Supported</button>
    <button onclick="alert(supportsHtml5StorageAlternate())">Local Storage Supported (alt)</button>
    <button onclick="registerEventListener()">Local Storage register listener</button>
    <button onclick="alert(getLocalStorageLength())">LocalStorage length</button>
    <button onclick="alert(displayLocalStorageContents())">LocalStorage contents</button>
    <button onclick="setLocalStorageItem()">LocalStorage set</button>
    <button onclick="alert(getLocalStorageAsString())">LocalStorage get as string</button>
    <button onclick="alert(getLocalStorageAsNumber())">LocalStorage get as number</button>
    <button onclick="removeLocalStorage()">LocalStorage remove foo</button>
    <button onclick="clearLocalStorage()">LocalStorage clear</button>

    <script>
        /**
         * Does this browser support HTML5 Local Storage.
         * @returns {boolean}
         */
        function supportsHtml5Storage() {
            try {
                return 'localStorage' in window && window['localStorage'] != null;
            } catch (e) {
                return false;
            }
        }

        /**
         * An alternative way to determine if a browser supports local storage.
         * @returns {boolean}
         */
        function supportsHtml5StorageAlternate() {
            return typeof(Storage) !== "undefined";
        }

        /**
         * Register a local storage event listener.
         */
        function registerEventListener() {
            if (window.addEventListener) {
                addEventListener("storage", handleStorageEvent, false);
            }
        }

        /**
         * Handle the storage event.
         */
        function handleStorageEvent(storageEvent) {
            if (!storageEvent) {
                storageEvent = window.event;
            }
            alert("key=" + storageEvent.key +
                  " oldValue=" + storageEvent.oldValue +
                  " newValue=" + storageEvent.newValue +
                  " url=" + storageEvent.url);

        }

        /**
         * Gets the number of items in Local Storage.
         * @returns {number}
         */
        function getLocalStorageLength() {
            return localStorage.length;
        }

        /**
         * Displays the contents of local storage.
         * @returns {string}
         */
        function displayLocalStorageContents() {
            var contents = "";
            var key = "";
            var value = "";
            for (var i = 0; i < localStorage.length; i++) {
                key = localStorage.key(i);
                value= localStorage.getItem(key);
                contents += "index=" + i + "\tkey=" + key + "\nvalue=" + value + "\n"
            }
            return contents;
        }

        /**
         * Sets a couple of items to local storage.
         */
        function setLocalStorageItem() {
            localStorage.setItem("foo", "bar");
            localStorage.setItem("max", 5);
        }

        /**
         * Gets an item from local storage.
         */
        function getLocalStorageAsString() {
            return localStorage.getItem("max") + 1;
        }

        /**
         * Gets an item from local storage.
         */
        function getLocalStorageAsNumber() {
            // local storage items are stored as a string
            return parseInt(localStorage.getItem("max")) + 1;
        }

        /**
         * Removes an item from local storage
         */
        function removeLocalStorage() {
            // removeItem does NOT return a value like Java does
            localStorage.removeItem("foo");
        }

        /**
         * Clear the contents of local storage.
         */
        function clearLocalStorage() {
            // clear did not work on FireFox 40.0.2
            localStorage.clear();
        }
    </script>
</body>
</html>

August 24th, 2015

Posted In: HTML, HTML5, java ninja, Javaninja, javascript

Tags: ,

Leave a Comment

HTML5 is making geolocation much, much easier to do. It is now a standard API and it’s pretty easy to do.

    The Geolocation API is supported by the following browser versions and up:

  • Chrome 5.0
  • Firefox 3.5
  • Android 2.0
  • Iphone 3.0
  • IE 9.0+
  • Safari 5.0
  • Opera 10.6

The basic functionality to get the user’s current geolocation is accessed by:

<script>
     navigator.geolocation.getCurrentPosition(success, error, positionOptions);
</script>

The error handler and the options are optional. My signature is as follows:

<script>
     navigator.geolocation.getCurrentPosition(
          handleGeolocationSuccess,
          handleGeolocationError,
          positionOptions
);
</script>

It is also possible watch a user’s position. If you watch a user’s position, then you don’t have to poll it. The browser will notify you when the user’s location changes.

This is how the watch functionality is accessed:

<script>
     var id = navigator.geolocation.watchPosition(success, error, positionOptions);
</script>

The id is needed to stop the watch functionality.

<script>
     navigator.geolocation.clearWatch(id);
</script>

The positionOptions object is used to specify options to the geolocation API. The definition of the positionOptions object is as follows:

  • enableHighAccuracy – boolean. Require high accuracy (e.g. GPS).
  • timeout – milliseconds. How long should the code wait till it determines that it can’t get a location
  • maximumAge – the maximum age of a previous location that can be used. Use the last position that the browser has as long as that position is less than N milliseconds old. Set to 0 to require the current position immediately.

An example of the positions object is:

{enableHighAccuracy: true, timeout: 5000, maximumAge: 60000}

The full example is below:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Java Ninja Geolocation prototype</title>
</head>
<body>
    <button onclick="getCurrentGeolocation()">Locate Me!!!</button>
    <button onclick="watchGeolocation()">Watch Me!!!</button>
    <button id="stopWatchingGeolocation" style="display: none" onclick="stopWatchingGeolocation()">
        Stop watching Me!!!
    </button>

    <script>
        /** Variable to make seconds more readable. */
        var seconds = 1000;  // 1000 milliseconds = 1 second

        /** Positions Object to contain all of the settings that we want for the geolocation.getCurrentPosition call. */
        var positionOptions = {
            /** require high accuracy (e.g. GPS). */
            enableHighAccuracy: true,
            timeout: 5 * seconds,
            /**
             * Use a position that is up to a minute old.
             * Set to 0 to ask for the current position right now.  Makes it faster and less expensive for the user if
             * they are getting repeated requests.
             */
            maximumAge: 60 * seconds
        };

        /** The geolocation watch id.  This is used to stop the watch. */
        var watchGeolocationId = 0;

        /**
         * Get the user's current geolocation.
         */
        function getCurrentGeolocation() {
            navigator.geolocation.getCurrentPosition(
                    handleGeolocationSuccess,
                    handleGeolocationError,
                    positionOptions
            );
        }

        /**
         * Watch the user's geolocation.
         */
        function watchGeolocation() {
            watchGeolocationId = navigator.geolocation.watchPosition(
                    handleGeolocationSuccess,
                    handleGeolocationError,
                    {enableHighAccuracy: true, timeout: Infinity, maximumAge: 0}
            );
            // Used old school so the example wouldn't have to pull in Jquery
            document.getElementById("stopWatchingGeolocation").style.display = "block";
        }

        /**
         * Stop watching the user's geolocation.
         */
        function stopWatchingGeolocation() {
            navigator.geolocation.clearWatch(watchGeolocationId);
            // Used old school so the example wouldn't have to pull in Jquery
            document.getElementById("stopWatchingGeolocation").style.display = "none";
        }

        /*
         * Geolocation handler functions
         */


        /**
         * Success handler for geolocation.getCurrentPosition.
         * @param position
         */
        function handleGeolocationSuccess(position) {
            var latitude = position.coords.latitude;
            var longitude = position.coords.longitude;
            var accuracy = position.coords.accuracy;

            alert('latitude=' + latitude + "\nlongitude=" + longitude + "\naccuracy=" + accuracy + " meters");
        }

        /**
         * Error handler for geolocation.getCurrentPosition.
         * @param err
         */
        function handleGeolocationError(err) {
            if (err.code == 1) {
                alert('PERMISSION_DENIED - User declined to provide geolocation data');
            } else if (err.code == 2) {
                alert('POSITION_UNAVAILABLE - Network is down or GPS satellites are unavailable')
            } else if (err.code == 3) {
                alert('TIMEOUT - Unable to determine location within the allotted time')
            }
        }
    </script>
</body>
</html>

July 9th, 2015

Posted In: Geolocation, HTML, HTML5

Tags: , ,

Leave a Comment

Bootstrap 3 tooltips are fairly easy to use. They also offer excellent cross-browser support while also being responsive.

If you are already using Bootstrap 3, then it is fairly easy to start using the tooltips. The first step is to add the HTML markup.

<a href="#"
     data-toggle="tooltip"
     title="My tooltip text">Tooltip target</a>

That is fairly straight forward. The title contains the text that will be displayed in the tooltip. data-toggle is a custom attribute and is used by the required javascript to trigger the markup as a tooltip.

The required javascript is

<script>
    $(function () {
        $('[data-toggle="tooltip"]').tooltip();
    });
</script>

That’s all it takes for a basic tooltip. Sometimes you need to customize the functionality. The default behavior is to trigger the tooltip on click. Sometimes you need other triggers, hover for example. This can be accomplished by adding data-trigger.

<a href="#"
     data-toggle="tooltip"
     data-trigger="hover click"
     title="My tooltip text">Tooltip target</a>

If you want to style the text, all you need to do is add normal class attribute. In my case, I made a css style especially for this purpose.

<a href="#"
     class="tooltip_text"
     data-toggle="tooltip"
     title="My tooltip text">Tooltip target</a>

By default, HTML markup is not displayed as HTML when it is included as part of the tooltip text. This can be enabled by adding data-html.

<a href="#"
     data-html="true"
     data-toggle="tooltip"
     title="My tooltip text">Tooltip target</a>

We did encounter a problem with using an <a> as the HTML element. The page will attempt to scroll to the anchor when clicked. We overcame this by using a tag.

<span data-toggle="tooltip"
      title="My tooltip text">
  Tooltip target
</span>

The default placement for the tooltips is above the trigger. Use data-placement to specify left, right or bottom.

<a href="#"
     data-placement="bottom"
     data-toggle="tooltip"
     title="My tooltip text">Tooltip target</a>

June 10th, 2015

Posted In: Bootstrap, CSS, HTML

Tags: , , , , ,

Leave a Comment

It has been a while since I wrote a form. I kept posting data even though the Jquery Validation fired and was not valid. I forgot that you have to check for a valid form. I also learned that you can also add a submit handler to the validation call. Here is what the completed validation and ajax call looked like. Notice that in this form, I disable the submit button belonging to the form just before the ajax request is sent.

$(document).ready(function () {
    $("#expungementForm").validate({
        submitHandler: function(form) {
            $.ajax({
                cache: false,
                type: "POST",
                url: form.action,
                data: $(form).serialize(),
                beforeSend: function (jqXHR, settings) {
                    $('input[type="submit"]', form).attr('disabled', true);
                },
                success: function (data) {
                    $('#expungeResult').html(data)
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    $('#expungeResult').html(textStatus).append("...").append(errorThrown).css('color', 'red')
                }});
            return false;
        }
    });
});

June 13th, 2012

Posted In: HTML, javascript, JQuery, json

Tags: , , , , ,

Leave a Comment

Here is what I started with, a hidden div containing data:

<div id="executive-dashboard-census-by-unit" style="display:none; padding-top:1.5em;">
    <executiveTags:census-by-unit census="${census}" now="${now}"/>
</div>

The data turned out to be too large for the amount of room that I was given to display in. Here is a good write-up, but his plugin did not work for me.
remy sharp’s b:log -> Fixing IE overflow problem

I resorted to adding the overflow:hidden to the style for the area.

January 11th, 2011

Posted In: HTML, IE

Leave a Comment

LinkedIn Auto Publish Powered By : XYZScripts.com