﻿/*
* jHash v1.0.0
* http://jhash.codeplex.com
* 
* Copyright (c) 2010 Chris Pietschmann
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy of 
* this software and associated documentation files (the "Software"), to deal in the 
* Software without restriction, including without limitation the rights to use, copy, 
* modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the 
* following conditions:
* 
* The above copyright notice and this permission notice shall be included in all copies 
* or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
(function (window)
{
    var hashChangeSupported = ('onhashchange' in window);
    var jHash = window.jHash = {
        jhash: "1.0.0",
        change: function (handler)
        {
            if (hashChangeSupported)
            {
                attachEvent(window, "hashchange", handler);
            } else
            {
                eventHandlers.push(handler);
            }
        },
        unbind: function (handler)
        {
            var i = 0, len = 0;
            if (hashChangeSupported)
            {
                detachEvent(window, "hashchange", handler);
            } else
            {
                arrayRemove(eventHandlers, eventHandlers.indexOf(handler));
            }
            return this;
        },
        val: function (name, value)
        {
            var ho = hashToObject(window.location.hash);
            if (arguments.length === 2)
            {
                ho[name.toString()] = (value === null ? '' : value);
                return this.set(this.root(), ho);
            } else if (arguments.length === 1 && typeof (name) === 'string')
            {
                return ho[name.toString()];
            }
            return ho;
        },
        set: function (values)
        {
            if (arguments.length === 1)
            {
                window.location.hash = (typeof (values) === "string" ? values : objectToHash(values));
            }
            return this;
        },
        hash: function ()
        {
            return (window.location.hash.indexOf("#") === 0 ? window.location.hash.substring(1, window.location.hash.length) : window.location.hash);
        },
        remove: function (name)
        {
            var ho = hashToObject(window.location.hash);
            ho[name.toLowerCase()] = undefined;
            return this.set(this.root(), ho);
        }
    };

    if (!hashChangeSupported)
    {
        window.setInterval(function ()
        {
            var currentHash = window.location.hash;
            if (previousHashValue !== currentHash)
            {
                for (var i in eventHandlers)
                {
                    eventHandlers[i].call(window);
                }
            }
            previousHashValue = currentHash;
        }, 200);
    }

    var eventHandlers = [],
        previousHashValue = window.location.hash,
        attachEvent = function (element, evtName, handler)
        {
            if (element.addEventListener)
            {
                element.addEventListener(evtName, handler, false);
            } else if (element.attachEvent)
            {
                element.attachEvent("on" + evtName, handler);
            } else
            {
                element["on" + evtName] += handler;
            }
        },
        detachEvent = function (element, evtName, handler)
        {
            if (element.removeEventListener)
            {
                element.removeEventListener(evtName, handler, false);
            } else if (element.detachEvent)
            {
                element.detachEvent("on" + evtName, handler);
            } else
            {
                element["on" + evtName] -= handler;
            }
        },
        arrayRemove = function (array, from, to)
        {
            /* function source: http://ejohn.org/blog/javascript-array-remove/ */
            var rest = array.slice((to || from) + 1 || array.length);
            array.length = from < 0 ? array.length + from : from;
            return array.push.apply(array, rest);
        },
        hashToObject = function (hash)
        {
            /* create a "dictionary" object for the passed in hash value */
            var obj = {}, pair = null, strHash = hash.substring(0, hash.length);
            if (strHash.indexOf("#") === 0)
            {
                strHash = strHash.substring(1, strHash.length);
            }
            var parts = strHash.split(",");
            for (i = 0; i < parts.length; i++)
            {
                pair = parts[i].split(":");
                obj[pair[0].toString()] = pair[1];
            }
            return obj;
        },
        objectToHash = function (object)
        {
            var s = "";
            for (var i in object)
            {
                if (object[i] !== undefined)
                {
                    if (s.length > 0)
                    {
                        s += ",";
                    }
                    s += i.toString() + ":" + object[i].toString();
                }
            }
            return s;
        }
})(window);
