/**
 * Cookie Manager by Dave Miller, Alberon Ltd.
 * Copyright (c) Alberon Ltd 2012. All rights reserved.
 *
 * BASIC USAGE
 *
 * CookieManager.add({
 *     // A name to identify this type of cookie
 *     name:                    "analytics",
 *
 *     // Run immediately if the user has opted in
 *     if_opted_in:             function() {},
 *
 *     // Run immediately if the user has opted out
 *     if_opted_out:            function() {},
 *
 *     // Run immediately if the user hasn't made a choice
 *     if_unknown:              function() {},
 *
 *     // Run at DOM ready if the user has opted in (requires jQuery)
 *     at_ready_if_opted_in:    function() {},
 *
 *     // Run at DOM ready if the user has opted out (requires jQuery)
 *     at_ready_if_opted_out:   function() {},
 *
 *     // Run at DOM ready if the user hasn't made a choice (requires jQuery)
 *     at_ready_if_unknown:     function() {},
 *
 *     // Run when the user changes status to opt in
 *     at_opt_in:               function() {},
 *
 *     // Run when the user changes status to opt out
 *     at_opt_out:              function() {},
 *
 *     // Run when the user clears their cookies
 *     at_reset:                function() {},
 * });
 *
 * Name is required, all event handlers (methods) are optional.
 *
 * Different handlers should have different names if the user can opt in/out of
 * them separately. Alternatively you can add multiple handlers with the same
 * name and they will be linked together - i.e. events will be run on all
 * handlers with the same name (in the order they are added).
 *
 * You can provide a string instead of a function and the method with that name
 * will be run instead. For example:
 *
 * CookieManager.add({
 *     name:            "sample",
 *
 *     // This is a custom method called run()
 *     run:             function() {},
 *
 *     // The run() method will be called immediately if the user is opted in
 *     if_opted_in:     "run",
 *
 *     // The run() method will also be called when the user opts in
 *     at_opt_in:       "run",
 * });
 *
 * Within the event handlers above (and custom methods) you can call:
 * - this.get_status()  // returns "accept", "block" or "unknown"
 * - this.opt_in()      // set status to "accept"
 * - this.opt_out()     // set status to "block"
 * - this.reset()       // set status to "unknown" (delete setting)
 *
 * Note: Because of this you should not create any custom method with these
 * names.
 *
 * You can also call these methods directly, passing in the name:
 * - CookieManager.get_status(name)
 * - CookieManager.opt_in(name)
 * - CookieManager.opt_out(name)
 * - CookieManager.reset(name)
 *
 * These is also a method to reset all known settings at once:
 * - CookieManager.reset_all()
 *
 * Some helper methods for working with cookies are available:
 * - CookieManager.get_cookie(name, defaultValue)
 * - CookieManager.set_cookie(name, value, expires, path, domain, secure)
 * - CookieManager.delete_cookie(name, path, domain, secure)
 *
 * The following config options can be set *after* loading this file but
 * *before* calling any methods:
 * - CookieManager.cookie_name = "cookieName";  // Default: CookieManager
 * - CookieManager.cookie_days = 730;           // Default: 2 years
 *
 * Note: Only one cookie is used for all handlers. They are concatenated in
 * the format "handler1=allow,handler2=block", where "handler1" is the 'name'
 * property specified when adding the handler.
 *
 * ADVANCED USAGE
 *
 * You can register an event handler to be notified when the user changes the
 * status for any cookie handler:
 *
 * CookieManager.subscribe("set_status", function(name, status) { ... });
 *
 * The CookieManager.add() method returns a CookieManagerHandler instance that
 * includes all your custom methods/properties and the ones listed above
 * (get_status, opt_in, opt_out, reset).
 *
 * You can also create a CookieManagerHandler instance manually if you want and
 * pass that to CookieManager.add() instead of a plain object.
 *
 * DEVELOPER NOTES
 *
 * The following properties & methods are for internal use only:
 * - CookieManager.handlers                 // An array holding all the handlers
 * - CookieManager.get_statuses()           // Returns an object of statuses, e.g. {handler1: "allow"}
 * - CookieManager.run(handler, methodName) // Run the named method on the given handler object (if possible)
 * - CookieManager.runAll(name, methodName) // Run the named method on all handlers with the given name
 */
(function(){

// Create a global CookieManager namespace
window.CookieManager = {}

// The cookie name can be changed if necessary
CookieManager.cookie_name = "CookieManager";

// The length of time the cookie will be stored for
CookieManager.cookie_days = 730; // 2 years

// General cookie functions
CookieManager.get_cookie = function(name, defaultValue)
{
    // Source: http://www.quirksmode.org/js/cookies.html
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i=0;i < ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0)
            return unescape(c.substring(nameEQ.length, c.length));
    }
    return (defaultValue || null);
}

CookieManager.set_cookie = function(name, value, expires, path, domain, secure)
{
    if (path === null) {
        path = "/";
    }

    // Source: http://techpatterns.com/downloads/javascript_cookies.php
    // set time, it's in milliseconds
    var today = new Date();
    today.setTime( today.getTime() );

    /*
    if the expires variable is set, make the correct
    expires time, the current script below will set
    it for x number of days, to make it for hours,
    delete * 24, for minutes, delete * 60 * 24
    */
    if (expires) {
        expires = expires * 1000 * 60 * 60 * 24;
    }
    var expires_date = new Date( today.getTime() + (expires) );

    document.cookie =
        name + "=" +escape( value ) +
        ( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) +
        ( ( path ) ? ";path=" + path : "" ) +
        ( ( domain ) ? ";domain=" + domain : "" ) +
        ( ( secure ) ? ";secure" : "" );
}

CookieManager.delete_cookie = function(name, path, domain, secure)
{
    CookieManager.set_cookie(name, "", -1, path, domain, secure);
}

// Get all the known cookie statuses as an object
CookieManager.get_statuses = function()
{
    var obj = {};
    var cookie = CookieManager.get_cookie(CookieManager.cookie_name);
    if (cookie) {
        cookie = cookie.split(",");
        for (var i = 0; i < cookie.length; i++) {
            var parts = cookie[i].split("=");
            if (parts.length == 2) {
                obj[parts[0]] = parts[1];
            }
        }
    }
    return obj;
}

// Get the current status for a given cookie ("allow", "block" or "unknown")
CookieManager.get_status = function(name)
{
    var statuses = CookieManager.get_statuses()
    if (statuses[name]) {
        return statuses[name];
    } else {
        return "unknown";
    }
}

// Set the status for a given cookie
CookieManager.set_status = function(name, value)
{
    var statuses = CookieManager.get_statuses()

    // Set the new value (or remove if it's being set to "unknown")
    if (!value || value == "unknown") {
        delete statuses[name];
    } else {
        statuses[name] = value;
    }

    // Build up an array of values
    var parts = [];
    for (var i in statuses) {
        parts.push(i + "=" + statuses[i]);
    }

    // Set or delete the cookie
    if (parts.length > 0) {
        CookieManager.set_cookie(CookieManager.cookie_name, parts.join(","), CookieManager.cookie_days);
    } else {
        CookieManager.delete_cookie(CookieManager.cookie_name);
    }

    // Notify any event handlers
    CookieManager.notify("set_status", name, value);
}

// All the handlers that have been added
CookieManager.handlers = []

// Add a new cookie blocker
CookieManager.add = function(handler)
{
    // If a plain object is given, convert it to a CookieManagerHandler
    if (!(handler instanceof CookieManagerHandler)) {
        handler = new CookieManagerHandler(handler);
    }

    // Keep a reference to the code for use if the status changes
    CookieManager.handlers.push(handler);

    // Run the appropriate code immediately
    var status = CookieManager.get_status(handler.name);
    if (status == "allow") {
        CookieManager.run(handler, "if_opted_in");
    } else if (status == "block") {
        CookieManager.run(handler, "if_opted_out");
    } else {
        CookieManager.run(handler, "if_unknown");
    }

    // Run the appropriate code when the document is ready (require jQuery)
    if (jQuery) {
        jQuery(document).ready(function()
        {
            CookieManager.run(handler, "at_ready");
            if (status == "allow") {
                CookieManager.run(handler, "at_ready_if_opted_in");
            } else if (status == "block") {
                CookieManager.run(handler, "at_ready_if_opted_out");
            } else {
                CookieManager.run(handler, "at_ready_if_unknown");
            }
        });
    }

    // Return the CookieManagerHandler object in case the user wants a reference
    // to it
    return handler;
}

// Run a particular method for a given cookie blocker, if it exists
CookieManager.run = function(handler, methodName)
{
    if (handler[methodName]) {
        if (typeof handler[methodName] == "string") {
            // If it's a string, treat it as a pointer to another named function
            // e.g. {run: function() {...}, at_opt_in: "run"}
            CookieManager.run(handler, handler[methodName]);
        } else {
            handler[methodName]()
        }
    }
}

// Run a particular method for all cookie blockers with a given name
CookieManager.runAll = function(name, methodName)
{
    for (var i = 0; i < CookieManager.handlers.length; i++) {
        if (CookieManager.handlers[i].name == name) {
            CookieManager.run(CookieManager.handlers[i], methodName);
        }
    }
}

// Opt in
CookieManager.opt_in = function(name)
{
    CookieManager.set_status(name, "allow");
    CookieManager.runAll(name, "at_opt_in");
}

// Opt out
CookieManager.opt_out = function(name)
{
    CookieManager.set_status(name, "block");
    CookieManager.runAll(name, "at_opt_out");
}

// Reset settings
CookieManager.reset = function(name)
{
    CookieManager.set_status(name, "unknown");
    CookieManager.runAll(name, "at_reset");
}

// Reset all known settings
CookieManager.reset_all = function()
{
    var statuses = CookieManager.get_statuses();
    for (i in statuses) {
        CookieManager.reset(i);
    }
}

// Allow user code to be notified whenever something changes
CookieManager.subscribed = {}

CookieManager.subscribe = function(evt, fn)
{
    if (!CookieManager.subscribed[evt]) {
        CookieManager.subscribed[evt] = [];
    }
    CookieManager.subscribed[evt].push(fn);
}

CookieManager.notify = function(evt)
{
    if (CookieManager.subscribed[evt]) {
        var args = Array.prototype.slice.call(arguments, 1);
        for (var i in CookieManager.subscribed[evt]) {
            CookieManager.subscribed[evt][i].apply(null, args);
        }
    }
}

// Create a prototype (class) for each handler to use
window.CookieManagerHandler = function(methods)
{
    // Copy all the methods (and variables) from the given object
    if (methods) {
        for (var i in methods) {
            this[i] = methods[i];
        }
    }
}

// Default settings
CookieManagerHandler.prototype.name = "default";

// Helper methods
CookieManagerHandler.prototype.get_status = function()
{
    CookieManager.get_status(this.name);
}

CookieManagerHandler.prototype.opt_in = function()
{
    CookieManager.opt_in(this.name);
}

CookieManagerHandler.prototype.opt_out = function()
{
    CookieManager.opt_out(this.name);
}

CookieManagerHandler.prototype.reset = function()
{
    CookieManager.reset(this.name);
}

}());

