Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

1048 lines (753 sloc) 66.684 kb

Class: HashNav

Click here to jump to the method index.

Implements

Requires

  • MooTools 1.3 (or higher; successfully tested again: MooTools 1.4.0-5)

  • HashNav

    • Core: Core, All Types, Browser, All Class, All Slick (dependency of Element & DOMReady), Element & Element.Event, DOMReady
    • provided: [String.QueryStringImproved.js, Object.compare.js]

Note that, along with including the provided script(s) in your webpage (don't worry, they're microscopic :D), each module that you would like to utilize the funcitonality of needs to be included AFTER HashNav.js. Choosing to forgo the inclusion of some "essential" modules (such as HashNav.History) might cause the HashNav class to function in an unexpected manner. Check out the comments included within each module file for more specific requirements.

Properties

  • Singleton (cannot be reinitialized)
  • Modular

Syntax

var hashNav = new HashNav([options]);

Arguments

  1. Options (object, optional) See below.

Options

  • interval - (integer: defaults to 200) This value determines how long the pause (in milliseconds) between function calls for the polling method will be. Ignored when the native onHashchange event is present.

  • prefix - (string: defaults to "!/") Determines the string that will be looked for after the pound/hash sign (#) in URIs. If the prefix is not found, the HashNav object will refuse to recognize the hash change as legal and will ignore it, as will most observers (see observer triggers below).

  • defaultHome - (string: defaults to "home") Your website's "meta-homepage." When visitors navigate to your page using a Relative Hash, the HashNav object will assume the user navigated to the defaultHome page. Check out Relative Hashes for more information on this integral setting.

  • queryMakeFalse - (boolean: defaults to false) When query strings are transcoded to key/value pairs by the ampersand parser, a blank parameter (param=) will become a blank string (param:"") by default. When queryMakeFalse is set to true, however, a blank parameter will be become a literal false (param:false). See How Hashes Are Parsed for more information, and how this applies to the slash parser.

  • externalConstants - (array) Determines the key the observe() DOM element method will use to store its pertinent data ("NAVOBJOBSDATA") along with the cookie prefix used when the serialize() method is called ("NAVOBJSERDATA").

  • cookieOptions - (object) An object containing key/value pairs representing the default options passed to MooTools's Cookie object. Note that encode should always be false!

  • ignoreVersionCheck - (boolean: defaults to false) When attempting to restore a session using unserialize(), version checking is used to prevent session data generated by older versions of the HashNav object from being recreated due to potential inconsistencies future updates may introduce into the serialization process. This version check can be skipped (at your own peril) by setting this option to true, which is definitely not recommended.

  • cookieDataHardLimits - (array) An array containing two integers. The first value (default: 2000) determines that maximum amount of characters allowed per cookie when one calls the serialize() method. The second value (default: 6) determines the maximum amount of cookies that can be created before the function aborts (returning false).

Events

navchange

Fired on the window object when the HashNav object recognizes a hash change.

Signature

onNavchange(storedHashData)

Arguments

  1. storedHashData - (object) The hash data-object representing the most recent URI hash change. Note that said object is structurally equivalent to the object returned when one calls getStoredHashData().

Public Method Index

history.get, history.truncate, history.clear

startPolling, stopPolling, poll

registerObserver, registeredObserver, unregisterObserver, unregisterObservers

getCurrent, getStoredHash, getStoredHashData

get, set, unset, has

isNative, isLegalHash

navigateTo, triggerEvent

serialize, unserialize, deserialize

Element.observe, Element.observing, Element.unobserve

Fx.scrl, Fx.scrlTo


Public Method: history.get

Grabs a stored hash data object from history and returns it. Requires the HashNav.History module.

Syntax

hashNav.history.get(entry);

Arguments

  1. entry - (mixed) History index to grab. Accepts positive numbers (0 being the first index to history.legnth-1 being the last); negative numbers (-1 being the last index to history.length being the first); and the all keyword, which would return the whole history array.

Returns

  • (object) The specified history object.
  • (array) An array consisting of all the history objects.
  • (boolean) false when out of bounds or the history object does not otherwise exist.

Notes

  • history.get(-1) returns the most recent hash URI data, and is equivalent to calling getStoredHashData().

See Also


Public Method: history.truncate

Truncates the history array (starting from the front [index 0] and working towards the back [index history.length-1]). Usually invoked before calling serialize(). Requires the HashNav.History module.

Syntax

hashNav.history.truncate(count);

Arguments

  1. count - (integer) How many history entries (starting from [index 0]) to delete. Does not accept negative numbers. Specifying a number greater than the history array's current length will cause this function to clear said array.

Returns

  • (boolean) false when nothing was truncated, otherwise true.

See Also


Public Method: history.clear

Clears the internal history array. Requires the HashNav.History module.

Syntax

hashNav.history.clear();

Returns

  • (boolean) true every time, unless there was some sort of internal error.

See Also


Public Method: startPolling

Starts the internal polling routine if it is not already started. Note that this method only needs to be called if internal polling has been stopped using stopPolling().

Syntax

hashNav.startPolling();

Returns

  • (boolean) true if the polling function was started successfully, otherwise false.

Notes

  • startPolling() will always fail and return false when native onHashchange event functionality is detected within the browser.


Public Method: stopPolling

Stops the internal polling routine if it is not already stopped. Call the startPolling() method to restart the polling routine.

Syntax

hashNav.stopPolling();

Returns

  • (boolean) true if the polling function was stopped successfully, otherwise false.


Public Method: poll

Used by the HashNav object to interrogate the window.location object and assess the current state of the hash URI. Does not need to be called manually.

Syntax

hashNav.poll();


Public Method: registerObserver

Registers an observer with the HashNav object by setting up a "middle-man" to mediate between your observing functions and the window object in a cross-browser fashion.

Syntax

hashNav.registerObserver(name, trigger, fn[, args[, bind[, scrlto]]]);

Arguments

  1. name - (string) The observer's name. This value will be used to unregister the observer later. Without it, the observer cannot be unregistered.
    • Note that observers can in fact have the same name without conflict. This is useful for grouping related observers together under one label. Do note, however, that when unregisterObserver() is called on a name that is tied to multiple observers, all of the observers under that label will be unregistered.
  2. trigger - (object) This object is important. So important, in fact, that it gets its own section below.
  3. fn - (function) Function to be called when the observer's trigger is satisfied by the current hash URI. This function should accept getStoredHashData() as the first argument -- usually denoted e for event -- followed by any custom arguments.
  4. args - (mixed, optional) Custom arguments passed to the observer function when triggered. Can either be a single argument or an array of arguments.
    • Warning: If your single argument is an array, wrap it within an array literal to prevent incorrect processing.
  5. bind - (object, optional) Object or element to bind the this keyword to within the observer function. Defaults to the current this.
  6. scrlto - (mixed, optional) An element to scroll to in an aesthetically pleasing manner when the observer's trigger is satisfied by the current hash URI. Requires the HashNav.Fx module.

Returns

  • (boolean) true if the observer was registered successfully, otherwise false.


Observer Triggers

An observer's trigger object is what is used to dictate if the observer should care about a hash URI change or not. Here's an example of a trigger obect:

{ page: 'home2', params: {object:1, object2:true, magic:'happening', object3:'~'} }

An observer registered with the above trigger would call its observing function when the hash URI looked something like the following: #!/home2/object/1/object2/true/magic/happening


Trigger Syntax

The syntax for trigger objects may seem a little esoteric at first, but using them is actually quite easy (maybe even intuitive) when you get the hang of it.

Expounding on our above example,

{ page: 'home2', params: {object:1, object2:true, magic:'happening', object3:'~'} }

we see that we have:

  • A page key, which maps to the page/state designator in a hash URI.
    • page: 'home2' means the observer will only activate when the hash URI is on home2.
  • A params key, which maps to the hash URI's query string (everything after the current parser's main separator (e.g. /), which is basically the object returned by calling getStoredHash()).
    • params: {object:1, object2:true, magic:'happening', object3:'~'} means the observer will only activate when the hash URI has object/1/object2/true/magic/happening contained somewhere in its query string (in any order). Note that the parameter object3 is missing. If said parameter were present, the trigger would not activate.

Pretty simple, 'eh? Just remember that the page key is required to exist within your trigger objects. If it is missing, your observer will crash. params, however, is not required. This means you don't have to use "trigger params" if you don't want to :)

Now let's get serious.


Advanced Trigger Syntax

Now that we've got a grasp on simple triggers, let's spice things up a little.

The page key accepts more than just a simple page/state designator string. You can also feed it:

  • A blank string '', which is interpreted to mean the defaultHome page.
    • ex. page:''
  • The boolean literal true, which is interpreted as "match any change to the page/state designator." Note that changes to the page's params will NOT activate these types of triggers unless the designator itself also changes.
    • ex. page:true
  • The boolean literal false, which is interpreted as "match any change to the hash URI"
    • ex. page:false
    • WARNING: This matches any change in the hash URI, even changes that are not recognized as legal by the HashNav object! Any change to the hash will trigger this observer!

The params key is just as special. Each parameter you specify within the params object can be fed:

  • A blank string '', which is interpreted as "if the parameter is present with any value (even an empty or non-existent one)"
    • ex. params:{someparam:''}
  • The tilde mark '~', which, when found alone, is parsed as a special keyword (much like all in most of HashNav's other methods) and interpreted as "if this parameter is NOT present"
    • ex. params:{someparam:'~'}
  • The boolean literal true, which is interpreted as "if the parameter is an orphan"
    • ex. params:{someparam:true}
  • The boolean literal false, which is interpreted as "if the parameter is empty"
    • ex. params:{someparam:false}

But we're still not finished!


Qualifiers and Wildcards

Trigger objects also have an optional third key called qualifiers, which applies special "group logic" to the trigger based on the "qualifying" keys that are passed in.

Those keys are as follows:

  • exclusive - (boolean, defaults to false) When true, the trigger will only activate its observing function when all of the parameters in the params object are present exclusively, meaning there are no other parameters present except those listed.

    • ex. { page: 'somepage', params: { someparam: somevalue }, qualifiers: { exclusive: true } }
  • minparams - (integer, defaults to 0) Specifies the minimum (inclusive) number of parameters necessary to catch the observer's attention. Cannot be used with the exclusive qualifier.

    • ex. { page: 'somepage', params: { someparam: somevalue }, qualifiers: { minparams: 5 } }
  • maxparams - (integer, defaults to Infinity) Specifies the maximum (inclusive) number of parameters allowed before an observer ignores a hash URI change. Cannot be used with the exclusive qualifier.

    • ex. { page: 'somepage', params: { someparam: somevalue }, qualifiers: { wildstrict: true } }
  • strict - (boolean, defaults to false) When true, the trigger processes its parameters using strict (===) comparison as apposed to normal comparison (==).

    • ex. { page: 'somepage', params: { someparam: somevalue }, qualifiers: { strict: true } }
  • wildstrict - (boolean, defaults to false) When true, the trigger processes its wildcard parameters using strict (===) comparison as apposed to normal comparison (==).

    • ex. { page: 'somepage', params: { someparam: somevalue }, qualifiers: { wildstrict: true } }
  • explicitChange - (boolean, defaults to false) Allows the trigger to differentiate between an actual change in parameter data and a typical hash change (where the underlying data may not have been altered, such as a reordering of parameters) in the hash URI.

    • ex. { page: 'somepage', params: { someparam: somevalue }, qualifiers: { explicitChange: true } }

(more qualifiers will be added as they become necessary)

There are also these cool little things called wildcards, represented, of course, by the asterisk *, and can appear as a parameter within the params object. When present within the params object, a wildcard may take one of the following forms:

  • *:'~', which is interpreted as "no parameters are allowed" (the same as supplying a trigger with an empty params object in conjunction with the exclusive qualifier)
    • ex. params:{'*':'~'}
  • *:'', which is interpreted as "any parameter is allowed" (aka, there must be at least one parameter present in the hash URI)
    • ex. params:{'*':''}
  • *:true, which is interpreted as "any orphan parameter is allowed" (aka, there must be at least one orphan parameter present in the hash URI)
    • ex. params:{'*':true}
  • *:false, which is interpreted as "any empty parameter is allowed" (aka, there must be at least one empty parameter present in the hash URI)
    • ex. params:{'*':false}
  • *:string, which is interpreted as "all parameters present must equal this string" (aka, all present parameters must equal this string and there must be at least one parameter present in the hash URI)
    • ex. params:{'*':'hello world'}


Notes
  • You must include HashNav_T-QualifierLogic.js and HashNav_T-WildcardLogic.js in your webpage if you plan on using any advanced trigger functionality!
  • Due to the way objects work, only one wildcard may appear per trigger. To include more than one would cause registerObserver() to behave in an undefined manner.
  • wildcards do not have to appear alone (ex. params:{ '*':false, someparam1:1, somepara2:2 } is logically and syntactically correct).
  • All trigger objects are optimized when created. Check out the demo page's "Trigger Demystifier" for an in-depth look at how this is done.
  • Trigger functions are automatically supplied with an "event" object that matches the structure of getStoredHashData().
  • Esoteric syntax { page: false, params: { '*':'' }, qualifiers: { exclusive: true } } has been deprecated. Use minparams and maxparams instead.

Congratulations, you're now a trigger master!

Examples

// Grab the HashNav Class's instance
var hashNav = new HashNav();

// Register an observer
hashNav.registerObserver('observer', { page: 'page3', params: {} }, function(e){ if(e[0]) console.log('event triggered:', e, arguments); }, [1, 2, 3, 4], null, 'header');

// The observer will be alerted when the hash URI looks something like   #!/page3   with any number of params!


SUPER Advanced Trigger Syntax

// Grab the HashNav Class's instance
var hashNav = new HashNav();

// Register an observer
hashNav.registerObserver('superduper', { page: false, params: { test: ['data1', 'data2'] } }, function(e){ alert('hi!'); });

// The observer will be alerted when the hash URI looks something like   #!/page99&&test=data1&test=data2   
// Do you see what I did there? No, it's not a typo! Even better:

// Register another observer
hashNav.registerObserver('superduper', { page: false, params: { '*': ['data1', 'data2', false, 'data5', ''] } }, function(e){ alert('hi!'); }); // Woah!

// Note that these "folding parameters," aka "nested parameters" or "nested objects," only work with the [ampersand parser](#Parsers "Jump to it!").


Public Method: registeredObserver

Checks the internal observer stack for the supplied name.

Syntax

myElement.registeredObserver(name);

Returns

  • (boolean) true if the observer name was found or false if it was not.

Examples

// Create a observer
hashNav.registerObserver('myObserver', ... );

// This...
hashNav.registeredObserver('myObserver'); // Will return true

// And finally, unregister the observer...
hashNav.unregisterObserver('myObserver'); // Easy right?

// Aaaand....
hashNav.registeredObserver('myObserver'); // Will return false!!


Public Method: unregisterObserver

Removes the specified observer from HashNav's internal observer stack, and removes all associated observer functionality.

Syntax

hashNav.unregisterObserver(name);

Arguments

  1. name - (string) The name of the observer or group of observers to unregister.

Returns

  • (boolean) true if the observer was successfully unregistered, otherwise false (if the observer name doesn't exist or an internal issue was encountered).

Notes

  • WARNING: DO NOT use window.removeEvents() to remove observer event handlers! That's what this method is for.


Public Method: unregisterObservers

Unregisters multiple observers (and can utilize the all keyword). Requires the HashNav.unregisterObservers module.

Syntax

hashNav.unregisterObservers(name1[, name2[, name3[, ...]]]);

Arguments

  1. name - (string) The name of the observer or group of observers to unregister. If the all keyword is passed in, all observers will be unregistered (rendering any argument following all useless).

Examples

// Unregisters observer1, Xunnamius, Linkin, and Park
hashNav.unregisterObservers('observer1', 'Xunnamius', 'Linkin', 'Park');

See Also


Public Method: navigateTo

Navigates the browser to the specified URI or history entry.

Syntax

// First Mode
hashNav.navigateTo(location[, forced]);
hashNav.navigateTo(historyIndex[, forced]);
hashNav.navigateTo(params[, forced]);

// Second Mode
hashNav.navigateTo(page, params[, forced]);

// Third Mode
hashNav.navigateTo(prefix, page, params[, forced]);

Arguments

  • First Mode

    1. location - (mixed) The target destination.
      • If this is a number (and trackHistory is true), the browser will navigate to the history entry that corresponds with the supplied parameter by passing location as an argument to history.get(). Note that supplying a number that is "out of bounds" will have this method simply return false with no further action taken.
      • If this is an object made up of key/value pairs representing the desired URI query string (similar to what you'd get if getStoredHash() were called), the browser will navigate to the specified parameters (without changing the page/state designator).
      • If this is a string
        • and you're using the slash parser
          • and the string starts with #, the current hash URI will be replaced with location (ex. '#!/home/param/1')
          • and the string starts with /, the current hash URI will be replaced with your prefix initialization option + location (ex. '/param/1')
          • and none of the above rules are matched, location will be appended to the end of the current query string (ex. 'param/1')
        • and you're using the ampersand parser
          • and the string starts with #, the current hash URI will be replaced with location (ex. '#!/home&&param=1')
          • and the string starts with &, the browser will append location to the end of the current query string (ex. '&param=1')
          • and none of the above rules are matched, the browser will navigate to your prefix initialization option + location (ex. '#!/' + 'home&&param=1')
    2. forced - (boolean, optional: defaults to false) If triggerEvent() should be called after navigation. Setting this to true is usually unnecessary, and may cause the navchange event to fire twice if used incorrectly.
  • Second Mode

    1. page - (string) The page/state designator to navigate to. (ex. 'home')
    2. params - (mixed) The query string to apply to the new hash URI. This can either be a literal query string or an object comprised of key/value pairs. (ex. 'hello/world/hello2/2')
    3. forced - (boolean, optional: defaults to false) If triggerEvent() should be called after navigation. Setting this to true is usually unnecessary, and may cause the navchange event to fire twice if used incorrectly.
  • Third Mode

    1. prefix - (string) The hash prefix to navigate to. (ex. '#')
    2. page - (string) The page/state designator to navigate to. (ex. 'home')
    3. params - (mixed) The query string to apply to the new hash URI. This can either be a literal query string or an object comprised of key/value pairs. (ex. 'hello/world/hello2/2')
    4. forced - (boolean, optional: defaults to false) If triggerEvent() should be called after navigation. Setting this to true is usually unnecessary, and may cause the navchange event to fire twice if used incorrectly.

Returns

  • (boolean) true if navigation completed successfully or false on failure.

Notes

  • The only time you'd need to use forced would be if you were navigating to the same unchanging hash URI again and again, and wanted your observers to take notice.
  • In an attempt to mimic the way browser history naturally works, using forced will NOT create a new history entry! It only calls history.triggerEvent().
  • Just because you can navigate to it doesn't make it a legal hash URI!

Examples

// Current parser = HashNav.parsers.slash
// Current URI = #!/home

hashNav.navigateTo('#Illegal');                         // #Illegal
hashNav.navigateTo('Legal');                            // #!//Legal
hashNav.navigateTo('/Legal');                           // #!/Legal
hashNav.navigateTo('Legal');                            // #!/Legal/Legal
hashNav.navigateTo('/newpage/hello/world');             // #!/newpage/hello/world
hashNav.navigateTo('Legal/goodbye/world');              // #!/newpage/Legal/goodbye/world
hashNav.navigateTo(-1);                                 // URI remains the same but no observers are triggered (because nothing has changed!)
hashNav.navigateTo(-1, true);                           // URI remains the same and any curious observers are triggered (forced!)
hashNav.navigateTo({ param2:2 });                       // #!/newpage/param2/2
hashNav.navigateTo('somepage', { param2:2 });           // #!/somepage/param2/2
hashNav.navigateTo('#', 'IllegalURI', { param2:2 });    // #IllegalURI/param2/2
hashNav.navigateTo('#!/', 'home', { param:1 });         // #!/home/param/1
hashNav.navigateTo('#!/', 'home', { param:1 });         // URI remains the same but no observers are triggered (because nothing has changed!)
hashNav.navigateTo('#!/', 'home', { param:1 }, true);   // URI remains the same and any curious observers are triggered (forced!)

// etc...

See Also


Public Method: buildURI

Used to generate HashNav-compatible URIs. Accepts the exact same input as navigateTo(), sans mode 1 history indexes.

Syntax

hashNav.buildURI( ... );

Returns

  • (string) The generated URI.

See Also


Public Method: getCurrent

Returns the current page/state designator (as recognized by the HashNav instance).

Syntax

hashNav.getCurrent();

Returns

  • (string) The current page/state designator. May be empty ('') if no hash has been navigated to yet.

Examples

//URI = #!/home/param/1/param2/2

hashNav.getCurrent(); // Returns 'home'


Public Method: getStoredHash

Returns an object containing key/value pairs representing a parsed version of the currently stored hash URI's query string.

Syntax

hashNav.getStoredHash();

Returns

  • (object) A subset of the internal storedHash object.

Examples

//URI = #!/home/param/1/param2/2

hashNav.getStoredHash(); // Returns { param:1, param2:2 }

See Also


Public Method: getStoredHashData

Returns an object containing pertinent data on the currently recognized hash URI (including the parameters returned by getStoredHash()).

Syntax

hashNav.getStoredHashData();

Returns

  • (object) The entire internal storedHash object.

Examples

//URI = #!/home/param/1/param2/false/param3/true/6/7

hashNav.getStoredHashData();

Returns:


storedHash:
[
   '#!/home/param/1/param2/false/param3/true/6/7',
   {
      page: 'home',
      pathString: 'param/1/param2/false/param3/true/6/7',
      pathParsed:
      {
         6: '7',
         param: '1',
         param2: '',
         param3: true
      }
   }
]

See Also


Public Method: get

Grabs the value of a specified parameter.

Syntax

hashNav.get(parameter1[, parameter2[, parameter3]]);

Arguments

  1. parameter - (string) The name of a hash URI parameter. If all is passed in, the results would be the same as calling getStoredHash().

Returns

  • (mixed) Value of the specified parameter or null if the parameter does not exist.
  • (object) When more than one parameter is specified, an object is returned instead, housing the results in convenient key/value pairs.


Public Method: set

Sets the value of the specified parameter (merges current parameter object with supplied input using Object.merge()).

Syntax

hashNav.set(parameter, value);
hashNav.set(paramobject);

Arguments

  • First Mode

    1. parameter - (string) The name of a hash URI parameter.
    2. value - (mixed) The value to assign the parameter.
  • Second Mode

    1. paramobject - (object) An object of key/value pairs similar in structure to what is returned by calling getStoredHash().

Notes

  • Calling set() changes the hash URI and as such will trigger a navchange event (similar to navigateTo()).
  • Although it isn't recommended, you can use array literal notation to give a single parameter multiple values (stored within an array) when using the ampersand parser. ex. { param1: 1, param2: [1, 2, 3, 4] }


Public Method: unset

Unsets the specified parameter.

Syntax

hashNav.unset(parameter1[, parameter2[, parameter3]]);

Arguments

  1. parameter - (string) The name of a hash URI parameter. If all is passed in, all parameters will be unset.

Notes

  • Calling unset() changes the hash URI and as such will trigger a navchange event (similar to navigateTo()).


Public Method: has

Checks if a specific parameter or parameters are present in the hash URI query string.

Syntax

hashNav.has(parameter1[, parameter2[, parameter3]]);

Arguments

  1. parameter - (string) The name of a hash URI parameter. If all is fed in as an argument, this method will return false if there are currently no parameters, and true in every other legal case.

Returns

  • (boolean) If only one parameter was passed in, the result will be true/false.
  • (boolean) If all was passed in, the result will be true if there are parameters present (and the hash URI is recognized as legal) or false if there are not.
  • (array-like object) If more than one parameter is specified, an array-like object of the arguments you supplied that are present as parameters is returned.


Public Method: isNative

Determines if the onHashchange event is native to the browser (true) or emulated with polling (false).

Syntax

hashNav.isNative();

Returns

  • (boolean) true if the browser supports the onHashchange event, otherwise false.


Public Method: isLegalHash

Checks if the specified hash (or the internally stored hash) URI is considered legal.

Syntax

hashNav.isLegalHash([hash]);

Arguments

  1. hash - (string, optional: defaults to the current hash URI) The hash to test against.

Returns

  • (boolean) true if the provided URI is legal, otherwise false.

See Also


Public Method: serialize

Capture the current hash URI and related browser session data. Check the notes below for some important specifics. Requires deserialize() as well as the HashNav.serialize module.

Syntax

hashNav.serialize([cookieName, [ nowrite]]);

Arguments

  1. cookieName - (string, optional: defaults to externalConstants[1]) The prefix to attach to all data cookies.
  2. nowrite - (boolean, optional: defaults to false) If true, this method will return the serialized data within a JSON object instead of writing the data to cookies.

Returns

  • (boolean) true if the whole serialization process succeeded and all cookies were written (if nowrite is false), else false.
  • (object) if nowrite is true, JSON object representing the serialized data.

Notes

  • Serialize() stores a variable amount of cookies (up to a specific limit) on your visitor's local machine: '_history', '_options', '_state', and '_version' (all prefixed by externalConstants[1]).
  • Note that Observers and their relationships to the window object and various other DOM elements are not included in the serialization process for obvious reasons.
  • Also note that, when using nowrite, the JSON object that is returned is not structured like getStoredHash(). Make sure you read this object correctly.
  • WARNING: Using cookies to store your serialized data is very dangerous. Most browsers and servers are not configured by default to handle massive amounts of data being stored in cookies. There are, of course, measures within the HashNav object put in place to mitigate the massive amounts of data that one may attempt to store by cookie using serialize (such as pseudo-pointers used in place of history entries in the internal history array, which dramatically reduces the history array's size), but even then you may experience data overflow if the user has been browsing around your website and you happen to use hash URIs that contain massive amounts of data in them. Some helpful limits:
    • It is recommended that you only serialize your HashNav data into cookies if your website contains around 35 (or less) unique massive hash URIs. Note that I said unique! Since URIs are cached and compressed internally, developers should only worry about unique URIs. Once a URI has been stored, it will never be stored (in full) again.
    • Make sure your server is configured to accept whatever amount of data you're trying to send to it without choking (I'm looking at you, Apache).
    • Do not increase the default limits set in place by the cookieDataHardLimits option object unless you know what you're doing. Know that if this method is returning false and you can't figure out why, it's probably because you hit the default limits.
    • If you do have a particularly large amount of unique hash URIs, you may consider using history.truncate() to truncate the internal history array before serialization.
  • Instead of using cookies to store your serialized data, which can be dangerous for large websites, developers have two options that are, in my opinion, MUCH better:
    • Use emerging JavaScript 2.0 (or HTML5, whatever the kids call it) technologies to store large amounts of data locally. Personally, I use http://www.jstorage.info/ (without overloading the dollar sign, thank you very much).
    • Store session data on the server instead. Super easy if you're developing using PHP.


Public Method: unserialize

Rebuild (unserialize) serialized HashNav session data. Requires the HashNav.unserialize module.

Syntax

hashNav.unserialize([restoreParadigm[, fireEventOnNav[, cookieName[, secure[, customdata]]]]]);

Arguments

  1. restoreParadigm - (string, optional: defaults to true) If true, the unserialized data will replace the current data session.
  2. fireEventOnNav - (boolean, optional: defaults to true) If restoreParadigm is true, this boolean will be passed to navigateTo() in place of the forced parameter.
  3. cookieName - (string, optional: defaults to externalConstants[1]) The prefix attached to the data cookies.
  4. secure - (boolean, optional: defaults to false) Whether unserialized data (excluding custom data) should be decoded using JSON's "secure" mode (may screw some things up, recommended false).
  5. customdata - (JSON object, optional) Custom data to unserialize. The customdata object's structure should follow the pattern of the JSON object returned by calling Serialize() with the nowrite parameter set to true.

Returns

  • (boolean) true if the whole unserialization process succeeded, else false.
  • (object) If restoreParadigm is false, an object consisting of the unserialized data is returned.

Notes

  • Attempting to share serialized data between different (even slightly different) versions of HashNav class instances will result in this function returning false unless overridden.


Public Method: deserialize

Destroys any cookies created with serialize(). Requires unserialize() as well as the HashNav.deserialize module.

Syntax

hashNav.deserialize([cookieName]);

Arguments

  1. cookieName - (string, optional: defaults to externalConstants[1]) The prefix attached to the data cookies.
  2. cookieOptions - (string, optional: defaults to cookieOptions) The options used initially to create the data cookies.

Returns

  • (boolean) true if the whole deserialization process succeeded, else false.

Notes

  • Since the original 'cookieOptions' used to create the cookies is also used to destroy them (automatically), there should be no trouble deleting cookies (unlike in previous versions)


Public Method: triggerEvent

Triggers a navchange event on the window, which triggers any active observers. Do not use window.fireEvent('navchange') directly or your observers may die.

Syntax

hashNav.triggerEvent(customHashData);

Arguments

  1. customHashData - (object, optional) A custom data object that will be passed down to any interested observers without generating a registry within HashNav's history. The structure of your customHashData objects should match that of getStoredHashData() exactly or you risk crashing your observers.

Returns

  • (boolean) true if the event fired successfully, else false.

Notes

  • This method will refuse to run if the hash URI is completely empty.
  • Use the triggerEvent() method whenever you register a new observer (or after you're finished registering all your observers or initializing a page -- much more efficient, an example is below). This is required because most modern browsers fire their native onHashchange event before the HashNav object is allowed to fully initialize. So when your visitors land on your site using a hash URI and you don't call triggerEvent(), it'll be as if the hash never changed! To better understand the problem, try the below example code out on your own page.
  • This stipulation also applies to DOM element observers as well.
  • WARNING: Calling triggerEvent() is nothing to scoff at! In fact, it's pretty dangerous, and can lead to some serious bugs if used incorrectly (most often, it'll cause observers to fire multiple times -- even ones that wouldn't normally fire under the circumstances -- which you might or might not desire). Be careful!

Examples

//First Time Visitor: URI = #!/home/slideshow/slide5

hashNav.registerObserver('test1', { page: 'home' }, function(e){ console.log('event triggered:', e, arguments); }, [1, 2, 3, 4], null, 'header');
hashNav.registerObserver('test2', { page: '' }, function(e){ console.log('MatchDefaultHome!'); });

// NOTHING HAPPENS?!?!

// Until we call...
hashNav.triggerEvent();

// Yay it worked! Try navigating to your page (from an external site or new tab) with the above line commented out, and see what happens.


Public Property (supplied by the HashNav.Fx module): scrl

One of the few utilities that is publicly exposed. This property is used to house the internal Fx.Scroll(window) instance of the Fx.Scroll Class (initialized just like that, yes). Being public, you can modify the scrolling effect to your heart's content (by calling setOptions on your HashNav instance's scrl property). Requires the HashNav.Fx module.


Public Method (supplied by the HashNav.Fx module): scrlTo

Calls toElement() on the Fx.Scroll instance housed within the scrl property. Requires the HashNav.Fx module.

Syntax

hashNav.scrlTo(elementID);

Arguments

  1. elementID - (mixed) May either be a literal DOM element or a DOM element's ID.


DOM Method Index

Element Method: observe

Calls registerObserver() on a DOM element, allowing said element to observe the hash URI and trigger a function if specific conditions are met. Note that this method passes registerObserver() the current object's ID (or Class or Name or TagName) as the name argument. Requires the HashNav.DOM module.

Syntax

myElement.observe(trigger, fn[, args[, scrollToElement]]);

Arguments

  1. trigger - (object) Trigger object. See Observer Triggers.
  2. fn - (function) Function to be called when the observer's trigger is satisfied by the current hash URI. This function should accept getStoredHashData() as the first argument -- usually denoted e for event -- followed by any custom arguments (the function is bound to the current DOM object).
  3. args - (mixed, optional) Custom arguments passed to the observer function when triggered. Can either be a single argument or an array of arguments.
    • Warning: If your single argument is an array, wrap it within another array literal to prevent incorrect processing.
  4. scrollToElement - (mixed, optional) An ID string/DOM object that will be scrolled to using Fx.Scroll.toElement(). If scrollToElement is true, the observing DOM element will be scrolled to instead. Requires the HashNav.Fx module.

Returns

  • (element) The DOM element in question.

Examples

// Create a new Element and insert it into the document
var myElement = new Element('div', { id: 'myFirstElement' });
myElement.inject(document.body);

// Tell the element to observe the hash URI for our specific changes
$('myFirstElement').observe({ page: 'home2', params: {object:1, object2:true, magic:'happening', object3:'~'} }, function(e){ alert('Hello World!'); console.log(arguments); }, [1,2,3], true);

// Will trigger when hash URI = #!/home2/object/1/object2/true/magic/happening


Element Method: unobserve

Calls unregisterObserver() on an observing DOM element. Note that this method passes unregisterObserver() the current object's ID (or Class or Name or TagName) as the name argument. Requires the HashNav.DOM module.

Syntax

myElement.unobserve();

Returns

  • (element) The DOM element in question.

Examples

// Create a new Element and insert it into the document
var myElement = new Element('div', { id: 'myFirstElement' });
myElement.inject(document.body);

// Tell the element to observe the hash URI
$('myFirstElement').observe( ... );

// Will trigger when hash URI = #!/home2/object/1/object2/true/magic/happening

// And finally, remove the observer, which will stop the element from observing hash URI changes!
$('myFirstElement').unobserve(); // Easy right?


Element Method: observing

Returns the DOM element's observer status. Requires the HashNav.DOM module.

Syntax

myElement.observing();

Returns

  • (boolean) true if the element is currently observing the hash URI or false if it is not.

Examples

// Create a new Element and insert it into the document
var myElement = new Element('div', { id: 'myFirstElement' }); // By the way, the observer's name (as acknowledged by the HashNav object) will be: myFirstElement
myElement.inject(document.body);

// Tell the element to observe the hash URI
$('myFirstElement').observe( ... );

// This...
$('myFirstElement').observing(); // Will return true

// And finally, remove the observer, which will stop the element from observing hash URI changes!
$('myFirstElement').unobserve(); // Easy right?

// This...
$('myFirstElement').observing(); // Will return false!!


Additional Features

How Hashes Are Parsed

Let's start off with a perfectly fine and "legal" example of a hash URI: #!/somepage/someparam/something/somethingelse/too

Wasn't so bad, was it? So, to construct a hash URI that the HashNav object will recognize as legal, the URI will have to conform to these rules:

  • Begin with your prefix (!/ by default)

    • #!/home/param/1
  • Contain a legal (no spaces) page name/state designator

    • #!/home/param/1
  • If the hash URI contains any parameters, they need to occur after the state designator, be delimited by / if more than one parameter is passed, and separated from the state designator by a / (if you're using the ampersand parser, that'd be & and && respectively)

    • #!/home/param/1
    • #!/home/param/1/param2/2
    • non-existent in #!/home

Basically: #!/ somepage / someparam/something / somethingelse/too

Do note that empty parameters (param= / param/false, depending on the parser) and orphan parameters (param with no = following it / param/true, depending on the parser) are also legal, allowed, and encouraged.

Ergo, #!/ somepage/true / someparam/false / somethingelse/true is just as legal as its predecessor above.

Note how true and false are interpreted as literals by the slash parser.

Now, the quintessential core facet of the HashNav object is its ability to parse a hash URI and store it in a logical and meaningful manner.

In order to do this efficiently, the HashNav object employs a simple sorting algorithm to parse a legal hash into an accessible key/value object. This object is then passed into every observer's function (as the first argument -- usually denoted e for event -- followed by any custom arguments) when a change is detected within a hash URI. This object is also accessible by calling getStoredHashData() or getStoredHash(), which returns a subset of the former. The object itself is constructed as follows:

storedHash:
[
   '',
   {
      page: '',
      pathString: '',
      pathParsed: null
   }
]

where

  • storedHash[0] returns the full hash URI unmodified (including leading pound/hash #)
  • storedHash[1] returns an object containing the hash URI parsed into useful tidbits
  • storedHash[1]['page'] returns a string containing the current page/state designation
  • storedHash[1]['pathString'] returns the query string of the hash URI (everything after -- but not including -- the parser's main separator [e.g. /]) as a string
  • storedHash[1]['pathParsed'] returns an object of key/value pairs representing the hash URI's query string in a more accessible and developer-friendly form. This is what is returned when one calls getStoredHash().

An example of a real storedHash object in action (using the ampersand parser):

storedHash:
[
   '#!/home&&param=1&param2=&param3&param4=4&param4=5&6=7',
   {
      page: 'home',
      pathString: 'param=1&param2=&param3&param4=4&param4=5&6=7',
      pathParsed:
      {
         6: '7',
         param: '1',
         param2: '',
         param3: true,
         param4: ['4', '5']
      }
   }
]

Notes

  • If queryMakeFalse was set to true, param2 would be set to false instead of an empty string
  • When a URI is recognized as illegal and a trigger is set to { page: false }, parameters within that trigger are ignored.
  • Although it is generally a hallmark of bad design, nesting parameters inside of other parameters and parameter-array notation (e.g. param1[0]=foo&param1[1]=bar) is partially supported by HashNav when using the ampersand parser.

Relative Hashes

Assuming you've read How Hashes Are Parsed above, relative hashes are basically fully realized hash URIs that are missing their page/state designator. For example:

#!/home/param/1  //I'm an absolute hash!
#!//param/1   //I'm a relative hash! Ooh!

Wait, home is missing! How can that be? Well, the HashNav object is smart enough to interpret the missing designator as the current page (much like relative URLs in traditional web design), and process the hash URI as if it had included the aforesaid designator all along. For example:

#!//param/1   //I'm a relative hash! Ooh!

becomes:

#!/contact/param/1

when the current page is "contact", and becomes:

#!/about/param/1

when the current page is "about". You can nab the current page string by calling getCurrent().

By now you've got to be asking yourself "what if my user navigates to my website using a relative hash within their URL from a location that is not governed by the HashNav object?" Valid question, with an equally valid answer! If your vistor is landing on your site utilizing a relative hash (or whenever the current page is read as empty), the HashNav object assumes the target to be the defaultHome option set at instantiation. For example:

#!//param/1   //I'm a relative hash! Ooh!

becomes:

#!/home/param/1

when the current page is unknown or the user is using a relative hash when first landing on your site. This carries with it some heavy implications:

  • Use relative hashes sparingly or not at all outside of your defaultHome page (simply "home" by default).
    • If you do, prepare for the certainty that some user somewhere will use your relative hash somewhere other than that one page.
  • Make sure that your different pages don't use the same parameter names to do different things!
  • Make sure your home page is equip to deal with (or promptly ignore) erroneous parameters from other pages.

As you can see, relative hashes are certainly powerful, and definitely have their shining moments, but are dangerous when used willy-nilly. Watch out!

History Tracking

To enable history tracking, make sure to include the HashNav.History module in your webpages.

When enabled, HashNav will take the current hash data object (via getStoredHashData()) and push it onto a private "history" array. This means all history entries are really just stored hash data objects representing mini "snapshots" in time. Using the history methods or navigateTo(), one can turn back the clock and restore any previous state in the web application's history. However, be aware that when a hash change occurs that the parser refuses to acknowledge as legal, it will not be logged internally (but it will be logged in the browser).

Do note that History Tracking allows some of the more powerful parameter filtering capabilities of the registerObserver() and observe() methods to work. Disabling history tracking will cripple both methods' parameter filtering capabilities, so beware.

HashNav URI Parsers

New in version 1.3, along with the ability to create custom hash parsers, is the inclusion by default of the "slash parser" over the "ampersand parser" (previous default), which allows hash URIs to mimic the style of the Zend Framework, or Github (look at your URL bar), etc.

To use something other than the default slash parser, such as the ampersand parser or a custom parser, you must initialize HashNav like so:

var hashnav = new HashNav({ parser: new HashNav.parsers.ampersand() });

That's it, you're all done! You can now play with URIs that use '&&', '&', and '=' in lieu of '/'.

Know that all available parsers are stored under HashNav.parsers, which is also where any custom parsers should go as well.

Using The slash parser

For those of you coming from version 1.3, using the slash parser over the ampersand parser comes with a few caveats. Mainly:

  • Relative URIs #!//look/like/this
  • Nested objects are IN NO WAY SUPPORTED (and will be turned into strings by way of toString())
  • Orphaned parameters need to be followed by "true" (e.g. #!/IAmAn/orphan/true)
  • Empty parameters need to be followed by "false" (e.g. #!/IAm/empty/false)
  • This, of course, means you must use the strings "true" and "false" carefully, as they are interpreted as literals
  • options.queryMakeFalse still applies. If it is true, any blank parameters (ones that appear on the end when params.length is odd) will be evaluated as false

Both the default slash parser and ampersand parser can be found under HashNav.parsers.

To load a parser after another parser has already been loaded, or the page has finished loading (yes, parsers can be loaded onto your HashNav instance dynamically at runtime), you could do something like the following:

var hashnav = new HashNav();
hashnav.options.parser = new HashNav.parsers.ampersand();
hashnav.options.parser.setInstance(hashnav);

PRO TIP: you may supply the slash parser with a string (defaults to '/') that will be used as the delimiter. Similar functionality can be achieved with the ampersand parser by passing in an object in the form of { main: '&&', pair: '&', field: '=' }.

Creating Your Own Parser

If you ever find yourself in need of an alternate URI scheme for your project, you're always free to write your own!

Don't worry, it's actually pretty easy (I wrote the slash parser in 15 minutes).

Step 1: Come up with your URI scheme

Since everything is stored internally as key-value pairs, you'll have to base your parser around this.

The general scheme goes a little like this: <prefix><state><symbol><params> where <param> is equivalent to a virtually infinite amount of <key><symbol><value> pairs. If the number of key-value pairs is odd, then the last param (always evaluated as a key in this case) will be interpreted as having an empty string as its value.

So my scheme is basically that of a file system or regular basic URL, if my <symbol> is '/'.

Step 2: Extend the HashNav.parsers.GeneralHashURIParser abstract class

Check out the slash parser's internals to see an example of this.

Step 3: Account for custom initialization

Check out the parser.ampersand.js file for an excellent demonstration of this.

Final Step: Redefine the appropriate methods and properties

Again, check out any one of the parsers' source to see an example of this in action.

The following are brief descriptions of the methods you can use.

These two methods MUST be redefined:

// Called to parse the raw window.location.hash (sans prefix)
//  into data that HashNav can understand. Note that this method
//  should NOT alter the original URI, nor its correlating objects
//  in memory. The only function of parse is to PARSE, therefore,
//  the only thing that can deviate from our hash URI is the
//  pathparse. Check the parser.slash.js file for an example.
public object parse(uri)

// Parse (JSON-like) Object To Query String
public string parseObjectToQueryString(obj)

The remaining methods may be optionally redefined:

// Gives us a link back to the HashNav class
public void setInstance(HashNav instance)

// Used by navigateTo and the like for creating "First Mode" hash URIs
//  Check the documentation for the navigateTo() method for more info.
public string createURIMode1History(data)   // data will be a number
public string createURIMode1Object(data)    // data will be an object
public string createURIMode1String(data)    // data will be a string

// Used by navigateTo and the like for creating "Second Mode" hash URIs
//  Check the documentation for the navigateTo() method for more info.
// data will be in the form of: [string, mixed]
public string createURIMode2(data)

// Used by navigateTo and the like for creating "Third Mode" hash URIs
//  Check the documentation for the navigateTo() method for more info.
// data will be in the form of: [string, string, mixed]
public string createURIMode3(data)

Your parser should live with the other parsers under the HashNav.parsers namespace. Moreover, know that setInstance gives you access to a complete HashNav instance. Therefore, any "options" should be obeyed by your parser (most specifically: options.queryMakeFalse).

Once you're done, you can load it up and use it just like any other parser!

Pro Tips

  • REMOVE OBSERVER EVENTS USING unregisterObserver() OR unobserve(), NOT window.removeEvents()!
  • The word/string all is treated as a "keyword" within most HashNav methods, so avoid using it as an argument accidentally.
  • Hash URIs are always trimmed of erroneous whitespace (using String.trim())!
  • The values for all parsed parameters are, due to MooTools's QueryString library, interpreted as strings.
  • Query params do not overwrite one another when using the ampersand parser (they do when using the slash parser). In param1=1&param2=2&param1=3, the value 3 will not overwrite the value 1! In this case, both values are stored within an array instead of a regular string, which represents the recognized value of the parameter. (ie. { param1:["1", "3"], param2:"2" })
  • Page or "state" names (home in #!/home/param/1) are completely and utterly stripped of whitespace using MooTools's String.clean() method when stored internally; however, events may still trigger when these invalid pages are navigated to in the browser.
  • Use the triggerEvent() method whenever you register a new observer (or after you're finished registering all of your observers or initializing a page.) Here's an example.
    • If you're a cool professional who knows what (s)he is doing, you'll find times when you don't want to use triggerEvent() after registering an observer or two! Ooh! Aah!
  • It is generally unwise to nest parameters inside of other parameters, even though it is partially supported (set() does not support parameter-array notation). Serializing whole objects using the page's URI is an even worse idea. Instead of nesting parameters/objects, just use separate parameters (or store the data in a variable/session/server-side)!
  • Sometimes IE<9 misbehaves if it is in quirks mode. Make sure to use PROPER LEGAL doctypes if you want HashNav to function at its full capability.
  • WARNING: Switching parsers in the middle of a session could have disasterous effects when combined with HashNav.History module! Parsers do NOT know how to read each other's history entries, nor should they.
  • It is generally a good idea to use the explicitChange quantifier whenever possible, especially when using relative hashes. This will cut down on your overall page processing time since "different" hashes that evaluate to the same data will not trigger any observers.

Coming Soon

  • Total rebuild of the playground (ground up) -- for one, changing parsers via GUI will be super easy
  • Going to add a tutorial to the playground
  • Going to add an in-browser "console" to the playground (for you, IE)
  • Going to write a "HashNav builder"/"script element generator" (similar to MooTools's Core/More builders) to hopefully cut down on the documentation a bit
  • Unit test suite to easily perform rigorous and exhaustive tests on all of HashNav's components (should severly cut into the amount of bugs that may/might/could exist)
  • It has been suggested that the explicitChange quantifier default to true instead of false when HashNav_T-WildcardLogic.js is available. I am leaning towards this.
  • In the spirit of maximum clarity: there will be a general consolidation of the terminology used in these docs.
    • e.g. "query params" <-> "parameters" <-> "params" <-> "hash URI parameters" <-> etc.
  • Make it so that startPolling and stopPolling behave the same (cannot be called) when native onhashchange functionality is detected within the browser.

Documented Bugs

  • None yet, woot!

Did you discover a new bug? An inconsistency in the docs somewhere? Typo in the code? Please Report it here first.

License

This code ships with the same lisence as the MooTools framework. Basically: this work is mine; however, you may use and/or modify anything here however and whenever you want. Attribution etc. is not at all necessary (although very welcomed). Still, if you fix any bugs or add anything interesting, I'd like know ;)

Check the change log for more information on releases!

Jump to Line
Something went wrong with that request. Please try again.