/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.

 * vBulletin Usage: md5hash(input,output)
 * Recommend: input = password input field; output = hidden field

 */
(function (sdwndw) {
    /*
     * Configurable variables. You may need to tweak these to be compatible with
     * the server-side, but the defaults work in most cases.
     */
    sdwndw.hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
    sdwndw.b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
    sdwndw.chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
    
    /*
     * These are the functions you'll usually want to call
     * They take string arguments and return either hex or base-64 encoded strings
     */
    sdwndw.hex_md5 = function (s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));};
    sdwndw.b64_md5 = function (s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));};
    sdwndw.str_md5 = function (s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));};
    sdwndw.hex_hmac_md5 = function (key, data) { return binl2hex(core_hmac_md5(key, data)); };
    sdwndw.b64_hmac_md5 = function (key, data) { return binl2b64(core_hmac_md5(key, data)); };
    sdwndw.str_hmac_md5 = function (key, data) { return binl2str(core_hmac_md5(key, data)); };

    /*
     * Calculate the MD5 of an array of little-endian words, and a bit length
     */
    sdwndw.core_md5 = function (x, len)
    {
        /* append padding */
        x[len >> 5] |= 0x80 << ((len) % 32);
        x[(((len + 64) >>> 9) << 4) + 14] = len;

        var a =  1732584193;
        var b = -271733879;
        var c = -1732584194;
        var d =  271733878;

        for(var i = 0; i < x.length; i += 16)
        {
            var olda = a;
            var oldb = b;
            var oldc = c;
            var oldd = d;

            a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
            d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
            c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
            b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
            a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
            d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
            c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
            b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
            a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
            d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
            c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
            b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
            a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
            d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
            c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
            b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

            a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
            d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
            c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
            b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
            a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
            d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
            c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
            b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
            a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
            d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
            c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
            b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
            a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
            d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
            c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
            b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

            a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
            d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
            c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
            b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
            a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
            d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
            c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
            b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
            a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
            d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
            c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
            b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
            a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
            d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
            c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
            b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

            a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
            d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
            c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
            b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
            a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
            d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
            c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
            b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
            a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
            d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
            c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
            b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
            a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
            d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
            c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
            b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

            a = safe_add(a, olda);
            b = safe_add(b, oldb);
            c = safe_add(c, oldc);
            d = safe_add(d, oldd);
        }
        return Array(a, b, c, d);

    };

    /*
     * These functions implement the four basic operations the algorithm uses.
     */
    sdwndw.md5_cmn = function (q, a, b, x, s, t)
    {
        return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
    };
    sdwndw.md5_ff = function (a, b, c, d, x, s, t)
    {
        return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
    };
    sdwndw.md5_gg = function (a, b, c, d, x, s, t)
    {
        return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
    };
    sdwndw.md5_hh = function (a, b, c, d, x, s, t)
    {
        return md5_cmn(b ^ c ^ d, a, b, x, s, t);
    };
    sdwndw.md5_ii = function (a, b, c, d, x, s, t)
    {
        return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
    };

    /*
     * Calculate the HMAC-MD5, of a key and some data
     */
    sdwndw.core_hmac_md5 = function (key, data)
    {
        var bkey = str2binl(key);
        if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);

        var ipad = Array(16), opad = Array(16);
        for(var i = 0; i < 16; i++)
        {
            ipad[i] = bkey[i] ^ 0x36363636;
            opad[i] = bkey[i] ^ 0x5C5C5C5C;
        }

        var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
        return core_md5(opad.concat(hash), 512 + 128);
    };

    /*
     * Add integers, wrapping at 2^32. This uses 16-bit operations internally
     * to work around bugs in some JS interpreters.
     */
    sdwndw.safe_add = function (x, y)
    {
        var lsw = (x & 0xFFFF) + (y & 0xFFFF);
        var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
        return (msw << 16) | (lsw & 0xFFFF);
    };

    /*
     * Bitwise rotate a 32-bit number to the left.
     */
    sdwndw.bit_rol = function (num, cnt)
    {
        return (num << cnt) | (num >>> (32 - cnt));
    };

    /*
     * Convert a string to an array of little-endian words
     * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
     */
    sdwndw.str2binl = function (str)
    {
        var bin = new Array();
        var mask = (1 << chrsz) - 1;
        for(var i = 0; i < str.length * chrsz; i += chrsz)
            bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
        return bin;
    };

    /*
     * Convert an array of little-endian words to a string
     */
    sdwndw.binl2str = function (bin)
    {
        var str = "";
        var mask = (1 << chrsz) - 1;
        for(var i = 0; i < bin.length * 32; i += chrsz)
            str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
        return str;
    };

    /*
     * Convert an array of little-endian words to a hex string.
     */
    sdwndw.binl2hex = function (binarray)
    {
        var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
        var str = "";
        for(var i = 0; i < binarray.length * 4; i++)
        {
            str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
                hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
        }
        return str;
    };

    /*
     * Convert an array of little-endian words to a base-64 string
     */
    sdwndw.binl2b64 = function (binarray)
    {
        var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        var str = "";
        for(var i = 0; i < binarray.length * 4; i += 3)
        {
            var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
            for(var j = 0; j < 4; j++)
            {
                if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
                else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
            }
        }
        return str;
    };

    sdwndw.str_to_ent = function (str)
    {
        var result = '';
        var i;

        for (i = 0; i < str.length; i++)
        {
            var c = str.charCodeAt(i);
            var tmp = '';

            if (c > 255)
            {

                while (c >= 1)
                {
                    tmp = "0123456789" . charAt(c % 10) + tmp;
                    c = c / 10;
                }

                if (tmp == '')
                {
                    tmp = "0";
                }
                tmp = "#" + tmp;
                tmp = "&" + tmp;
                tmp = tmp + ";";

                result += tmp;
            }
            else
            {
                result += str.charAt(i);
            }
        }
        return result;
    };

    sdwndw.trim = function (s)
    {
        while (s.substring(0, 1) == ' ')
        {
            s = s.substring(1, s.length);
        }
        while (s.substring(s.length-1, s.length) == ' ')
        {
            s = s.substring(0, s.length-1);
        }
        return s;
    };

    sdwndw.md5hash = function (input, output_html, output_utf, skip_empty)
    {

        if (navigator.userAgent.indexOf("Mozilla/") == 0 && parseInt(navigator.appVersion) >= 4)
        {
            var md5string = hex_md5(str_to_ent(trim(input.value)));
            output_html.value = md5string;
            if (output_utf)
            {
                md5string = hex_md5(trim(input.value));
                output_utf.value = md5string;
            }
            if (!skip_empty)
            {
                // implemented like this to make sure un-updated templates behave as before
                input.value = '';
            }
        }

        return true;
    };
})(window);

;/**
* hoverIntent is similar to jQuery's built-in "hover" function except that
* instead of firing the onMouseOver event immediately, hoverIntent checks
* to see if the user's mouse has slowed down (beneath the sensitivity
* threshold) before firing the onMouseOver event.
* 
* hoverIntent r6 // 2011.02.26 // jQuery 1.5.1+
* <http://cherne.net/brian/resources/jquery.hoverIntent.html>
* 
* hoverIntent is currently available for use in all personal or commercial 
* projects under both MIT and GPL licenses. This means that you can choose 
* the license that best suits your project, and use it accordingly.
* 
* // basic usage (just like .hover) receives onMouseOver and onMouseOut functions
* $("ul li").hoverIntent( showNav , hideNav );
* 
* // advanced usage receives configuration object only
* $("ul li").hoverIntent({
*   sensitivity: 7, // number = sensitivity threshold (must be 1 or higher)
*   interval: 100,   // number = milliseconds of polling interval
*   over: showNav,  // function = onMouseOver callback (required)
*   timeout: 0,   // number = milliseconds delay before onMouseOut function call
*   out: hideNav    // function = onMouseOut callback (required)
* });
* 
* @param  f  onMouseOver function || An object with configuration options
* @param  g  onMouseOut function  || Nothing (use configuration options object)
* @author    Brian Cherne brian(at)cherne(dot)net
*/
(function($) {
    $.fn.hoverIntent = function(f,g) {
        // default configuration options
        var cfg = {
            sensitivity: 7,
            interval: 100,
            timeout: 0
        };
        // override configuration options with user supplied object
        cfg = $.extend(cfg, g ? { over: f, out: g } : f );

        // instantiate variables
        // cX, cY = current X and Y position of mouse, updated by mousemove event
        // pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
        var cX, cY, pX, pY;

        // A private function for getting mouse position
        var track = function(ev) {
            cX = ev.pageX;
            cY = ev.pageY;
        };

        // A private function for comparing current and previous mouse position
        var compare = function(ev,ob) {
            ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
            // compare mouse positions to see if they've crossed the threshold
            if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
                $(ob).unbind("mousemove",track);
                // set hoverIntent state to true (so mouseOut can be called)
                ob.hoverIntent_s = 1;
                return cfg.over.apply(ob,[ev]);
            } else {
                // set previous coordinates for next time
                pX = cX; pY = cY;
                // use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
                ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
            }
        };

        // A private function for delaying the mouseOut function
        var delay = function(ev,ob) {
            ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
            ob.hoverIntent_s = 0;
            return cfg.out.apply(ob,[ev]);
        };

        // A private function for handling mouse 'hovering'
        var handleHover = function(e) {
            // copy objects to be passed into t (required for event object to be passed in IE)
            var ev = jQuery.extend({},e);
            var ob = this;

            // cancel hoverIntent timer if it exists
            if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }

            // if e.type == "mouseenter"
            if (e.type == "mouseenter") {
                // set "previous" X and Y position based on initial entry point
                pX = ev.pageX; pY = ev.pageY;
                // update "current" X and Y position based on mousemove
                $(ob).bind("mousemove",track);
                // start polling interval (self-calling timeout) to compare mouse coordinates over time
                if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}

            // else e.type == "mouseleave"
            } else {
                // unbind expensive mousemove event
                $(ob).unbind("mousemove",track);
                // if hoverIntent state is true, then call the mouseOut function after the specified delay
                if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
            }
        };

        // bind the function to the two event listeners
        return this.bind('mouseenter',handleHover).bind('mouseleave',handleHover);
    };
})(jQuery);

;(function (sdwndw) {

sdwndw.genericdialog = function (title, msg, nomodal, ok, cancel, id)
{
    var div = document.createElement('div');
    div.id = (typeof(id) == 'undefined') ? 'generic_dialog_' + parseInt(((new Date).getTime())/ 1000) : id;
    div.title = title;

    $('body').append(div);
    var gdialog = $('#' + div.id);
    var gbuttons = {};
    if (cancel)
    {
        gbuttons.Cancel = function ()
        {
            if (typeof(cancel) == "function")
            {
                cancel(this);
            }
            $(this).dialog('close');
            $('#' + div.id).remove();
        };
    }
    if (ok)
    {
        gbuttons.Ok = function ()
        {
            if (typeof(ok) == "function")
            {
                ok(this);
            }
            $(this).dialog('close');
            $('#' + div.id).remove();
        };
    }
    gdialog.dialog({
        autoOpen: false,
        zIndex: 30000,
        modal: !nomodal,
        buttons: gbuttons
    });
    gdialog.html(msg);
    gdialog.dialog('open');
    $(".ui-dialog-buttonpane button:last").focus();
    return div.id;
};

sdwndw.hidegenericdialog = function (id)
{
    id = (typeof(id) == 'undefined') ? 'generic_dialog' : id;
    var gdialog = $('#' + id);
    if (gdialog && gdialog.length > 0)
    {
        gdialog.dialog('close');
        gdialog.remove();
    }
};

sdwndw.successdialog = function (msg, okfunc, w)
{
    if (typeof $.fn.dialog === 'undefined')
    {
        alert(msg);
        return;
    }

    hidegenericdialog();
    var sdialog = $("#success_dialog");
    if (sdialog.length === 0)
    {
        $("body").append("<div id='success_dialog' title='Success'></div>");
        sdialog = $("#success_dialog");
        sdialog.dialog({
            autoOpen: false,
            modal: true,
            zIndex: 30001,
            width: w || '',
            buttons: {
                Ok: function ()
                {
                    if (okfunc)
                    {
                        okfunc(this);
                    }
                    $(this).dialog('close');
                }
            }
        });
    }
    sdialog.html(msg);
    sdialog.dialog("open");
    $(".ui-dialog-buttonpane button:last").focus();
};

sdwndw.errordialog = function (error, nobuttons, w, noOverlay)
{
    if (typeof $.fn.dialog === 'undefined')
    {
        alert(error);
        return;
    }

    hidegenericdialog();
    var errdialog = $("#errordialog"),
        noOverlay = noOverlay || false;
    if (errdialog.length === 0)
    {
        $("body").append("<div id='errordialog' title='Error'></div>");
        errdialog = $("#errordialog");
        errdialog.dialog({
            bgiframe: true,
            autoOpen: false,
            modal: !!noOverlay,
            width: w || '',
            buttons: (nobuttons ? {} : {
                Ok: function()
                {
                    $(this).dialog('close');
                }
            })
        }).parent('.ui-dialog').css('zIndex', 10000000); // I am sorry.
        // Fix for removal of z-index property in jQuery Ui v1.10
    }
    errdialog.html(error);
    errdialog.dialog('open');
    $(".ui-dialog-buttonpane button:last").focus();
};

sdwndw.unverifiedEmailDialog = function ()
{
    var $modal = $('#unverifiedEmailError');

    if ($modal.length > 0)
    {
        $modal.modal({overlayClose:true, maxWidth:'420px'});
    }

    return false;
};

})(window);

$(document).ready(function() {
    if ($("#unverifiedEmailError").length > 0)
    {
        $("body").on("click", ".sendActivationEmail", function (e) {
            e.preventDefault();
            $.resendValidation({
                success : function (response) {
                    $.modal.close();
                    successdialog(response.message, null, 300);
                }, error: function (response) {
                    $.modal.close();
                    errordialog(response.message, null, 300);
                }
            });
        });
    }
});

;(function (sdwndw) {
sdwndw.TRACKABLE_SEARCH_TERMS = ['amazon', 'laptop', 'staples', 'kohls', 'tablet', 'walmart', 'sears', 'ssd', 'lowes', 'kmart', '40"', 'virus', 'train', 'lease', 'reebok', 'wynn', 'google chrome', 'travelpro', 'old spice', 'baby stroller'];
sdwndw.closeMenuTimer;
sdwndw.menuHoverTimer;
sdwndw.main_menu_open;
sdwndw.test;
sdwndw.prefsLoaded = false;

// Fix for console.log not being available in IE
sdwndw.console = console || {};
if (typeof console.log == 'undefined')
{
    console.log = $.noop;
}

sdwndw.openLoginDropdown = function (e)
{
    if (e)
    {
        e.preventDefault();
    }
    $(".global_sso th").hide();
    $("#signin_menu").css({
        right: ($.browser.ie ? -99 : -100),
        left: null,
        top: 8,
        marginTop: null,
        marginLeft: null
    }).toggle();
    $("#global_userbar_notlogged").append($("#signin_menu"));
    $(".signin").toggleClass("menu-open");
    $("#username").focus();
    return false;
}

sdwndw.hideLoginDropdown = function (e)
{
    if (e)
    {
        e.preventDefault();
    }
    $(".signin").removeClass("menu-open");
    $("fieldset#signin_menu").hide();
    return;
}

sdwndw.showGlobalLogin = function (no_sso, title)
{
    if (no_sso)
    {
        $(".global_sso").width(270).find(".sso_selections").hide();
    }
    else
    {
        $(".global_sso").width(370).find(".sso_selections").show();
    }

    $(".signin").removeClass("menu-open");
    $("body").append($("#signin_menu"));
    $("#signin_menu").css({
        width: no_sso ? 272 : 372,
        top: "50%",
        left: "50%",
        right: null,
        marginTop: -125,
        marginLeft: -1 * ((no_sso ? 272 : 372) / 2)
    }).show();
    $(".global_sso th").show();

    $(".global_sso th a").hover(function () {
        $(this).addClass("ui-state-hover");
    }, function () {
        $(this).removeClass("ui-state-hover");
    }).click(hideGlobalLogin);

    $("#signin_menu").draggable({
        handle: $(".global_sso th")
    });

    if (title)
    {
        $(".global_sso th span").text(title);
    }

    return false;
}

sdwndw.hideGlobalLogin = function ()
{
    $("#signin_menu").width(372).draggable('destroy').hide();
    $(".global_sso th").hide();
    $(".global_sso th a").removeClass("ui-state-hover").unbind();
    return false;
}

sdwndw.notice_close = function (notice)
{
    $(".notice_" + notice).slideUp();
    var d = new Date();
    var t = parseInt(d.getTime() / 1000, 10);
    createCookie("sdnotice" + notice, t, 365);
}

sdwndw.menu_closesub = function ()
{
    main_menu_open = false;
    $("#main_menu_02 table").hide().find("li").hide();
    $("#main_menu_03 table").hide().find("li").hide();
    $("#main_menu_05 table").hide().find("li").hide();
}

sdwndw.stopCloseMenuTimer = function ()
{
    clearTimeout(closeMenuTimer);
}

sdwndw.startCloseMenuTimer = function ()
{
    clearTimeout(menuHoverTimer);
    stopCloseMenuTimer();
    closeMenuTimer = setTimeout(menu_closesub, 100);
}

sdwndw.menu_showsub = function (ele)
{
    $("#main_menu_02 table").show().find("li").slideDown("fast");
    $("#main_menu_03 table").show().find("li").slideDown("fast");
    $("#main_menu_05 table").show().find("li").slideDown("fast");
    main_menu_open = true;
}

sdwndw.menuHover = function (e)
{
    var elTarget = $(e.target);
    var menuWait = 450;
    stopCloseMenuTimer();
    clearTimeout(menuHoverTimer);
    while (!elTarget.is("#menu2"))
    {
        if (elTarget.is("#main_menu_05, #main_menu_03, #main_menu_02"))
        {
            if (!main_menu_open)
            {
                clearTimeout(menuHoverTimer);
                menuHoverTimer = setTimeout(function() {
                    menu_showsub(elTarget);
                },menuWait);
            }
            break;
        }
        else if (elTarget.is("#main_menu_01, #main_menu_04"))
        {
            clearTimeout(menuHoverTimer);
            break;
        }
        else
        {
            elTarget = elTarget.parent();
        }
    }
}

sdwndw.menu_bind = function ()
{
    menu_closesub();
    $("#menu2").mouseover(menuHover).mouseout(startCloseMenuTimer);
}

sdwndw.setUserOptions = function()
{
    if (!prefsLoaded)
    {
        var cookie = readCookie("pageWidth" + cookie_suffix);
        currentWidth = cookie ? cookie : defWidth;

        cookie = readCookie("fontSize" + cookie_suffix);
        currentFontSize = cookie ? cookie : defFontSize;

        //setWidth(currentWidth);
        //setFontSize(currentFontSize);
        prefsLoaded = true;
    }
}

sdwndw.sdinit = function ()
{
    setUserOptions();
    menu_bind();
}

sdwndw.toggle_firstpost_tab = function (tab)
{
    var allvistabs = $("#sharethistab:visible,#publicfeedback:visible");
    var tabcontainer = $("#firstpost_tabcontainer");
    if ("#" + allvistabs.attr("id") == tab)
    {
        tabcontainer.slideToggle("fast");
    }
    else
    {
        if (allvistabs.length === 0)
        {
            tabcontainer.hide();
        }
        allvistabs.not(tab).hide();
        $(tab).fadeIn("fast");
        tabcontainer.slideDown();
    }
}

window.sdLoadQueue.push([function() {
    if ((typeof define !== 'function' || (define && !define.amd)) && $ && $.fn && $.fn.lazyload)
    {
        // Only lazy loading this way when this file isn't being shimmed
        $(".lazyimg").show().lazyload({
            'effect': 'show',
            'event': 'sporty',
            'skip_invisible': false
        });

        setTimeout(function() { $(".lazyimg").trigger("sporty"); }, 500);
    }
}, 'criticalPath']);

// I want this to load LAST.
window.addEventListener('load', function ()
{
    // Allow thread display "jump" in event of hash getting nuked
    var allowedSections = [
        'Black Friday',
        'News&Articles',
        'ThreadDetails',
        'Qool_Content',
        'ThreadForumView',
    ];

    if (typeof window.dataLayer !== 'undefined' && allowedSections.indexOf(dataLayer.page.section) !== -1)
    {
        var postRe = new RegExp('p=(\\d+)');
        var postId;
        var targetElement;

        if (postRe.test(window.location.search))
        {
            postId = window.location.search.match(postRe);
            postId = postId[1];

            if (window.location.hash.indexOf('post' + postId) === -1)
            {
                targetElement = document.getElementById('post' + postId);

                if (typeof targetElement === 'object')
                {
                    window.scroll(0, targetElement.getBoundingClientRect().top);
                }
            }
        }
    }
});

$(document).ready(function() {
    sdinit();
    //list_button_listener();

    $(".beta .icon").click(function(e) {
        var d = new Date('2/14/2017');
        var bannerType = $(e.target).data('optInBannerType');

        if (bannerType == 'classic-moving')
        {
            document.cookie = 'fpDismissedClassicWarningBanner=1; expires=' + d.toUTCString() + '; path=/';

            $(".beta").hide();
        }
        else
        {
            var confirm_var = confirm("If you want to change your view later you can use the view links at the bottom of the page");
            if (confirm_var)
            {
                document.cookie = 'fpDismissedOptInBanner=1; expires=' + d.toUTCString() + '; path=/';

                $(".beta").hide();
            }
            else
            {
                return false;
            }
        }
    });

    if ($.fn.tooltipster)
    {
        $('#featuredDealsInfo').tooltipster({
            contentAsHTML: true,
            theme        : 'tooltipster-sd',
            position     : 'bottom',
            maxWidth     : '300',
            interactive  : true
        });
    }

    // Code for holiday giveaway banner countdown
    if ($(".gw_counter_clock").length > 0)
    {
        var next_giveaway_in = new Date(parseInt($(".gw_counter_clock").attr("sddata_date")));
        $('.gw_counter_clock').countdown({until: next_giveaway_in, format: 'ms', layout: "<span>{mnn}</span> Minutes <span>{snn}</span> Seconds"});
    }

    $(document).click(function (ev) {
        if ($(ev.target).parents('#global_userbar_logged').length === 0)
        {
            if ($("#global_userbar_pulldown").is(":visible"))
            {
                $("#global_userbar_pulldown,.settings_box.sbuserbar").fadeOut('fast');
            }
        }

        if ($(ev.target).parents('.gridpop').length === 0 && !$(ev.target).is(".quickview"))
        {
            if ($(".gridpop:visible"))
            {
                $(".gridpop").hide().removeClass("gridpop");
            }
        }
    });

    if ($(".search_pagenav_menu").length > 0)
    {
        $(".search_pagenav_menu").each(function (i) {
            $(this).attr("id", "pagenav."+i);
            var pn = vBmenu.register("pagenav."+i);
            pn.addr = $(this).attr("title");
            $(this).attr("title", "");
        });

        if (fetch_object('pagenav_form'))
        {
            fetch_object('pagenav_form').gotopage   = vBpagenav.prototype.form_gotopage;
        }

        if (fetch_object('pagenav_ibtn'))
        {
            fetch_object('pagenav_ibtn').onclick    = vBpagenav.prototype.ibtn_onclick;
        }

        if (fetch_object('pagenav_itxt'))
        {
            fetch_object('pagenav_itxt').onkeypress = vBpagenav.prototype.itxt_onkeypress;
        }
    }

    $(".signin").click(openLoginDropdown);

    $(document).mouseup(function(e) {
        if ($(e.target).parents("#signin_menu").length === 0)
        {
            hideLoginDropdown();
        }

        var deal_modal_container = $('#deal_modal_container');
        if ($("body").is(e.target)) {
            deal_modal_container.detach();
        }
        else if (deal_modal_container.is(':visible') && !deal_modal_container.is(e.target) && deal_modal_container.has(e.target).length === 0 &&  !($(e.target).parents('.ui-dialog').length > 0) && !$(e.target).parents('.loginbox_container').length>0 && !($(e.target).is('.loginbox_container')))
        {
            deal_modal_container.detach();
        }

        var coupon_modal_container = $('#coupon_modal_container');
        if ($("body").is(e.target))
        {
            coupon_modal_container.detach();
        }
        else if (coupon_modal_container.is(':visible') && !coupon_modal_container.is(e.target) && coupon_modal_container.has(e.target).length === 0 && !($(e.target).parents('.ui-dialog').length > 0) && !$(e.target).parents('.loginbox_container').length>0 && !($(e.target).is('.loginbox_container')))
        {
          coupon_modal_container.detach();
        }
    });

    $("#searchin_option, #newsearch_showposts").change(function(){
        var applyBtnHidden = ($(".applyBtn").length == 0);

        if (applyBtnHidden)
        {
            apply_filters(true, true);
        }
    });

    var search_box = $("#classic_search_box, #newsearch_box");
    // var search_menu = $("#searchbar_menu");
    var search_menu = $("#live_search_result");

    search_box.focus(function() {
        if  ($(this).val() == $(this).attr("ph-text"))
        {
            $(this).val("");
        }
        if ($(this).attr('id') == 'classic_search_box')
        {
            search_menu.slideDown("fast");
        }
        $(this).data("focus", true);
    }).blur(function() {
        if (!search_menu.data("hover") && ($(this).attr('id') == 'classic_search_box'))
        {
            search_menu.slideUp("fast");
        }
        if ($(this).val() == "")
        {
            $(this).val($(this).attr("ph-text"));
        }
        $(this).data("focus", false);
    }).keydown(function (ev) {
        if (ev.keyCode == 13)
        {
            $(this).parents('form').submit();
        }
    });

    search_menu.hover(
        function() {
            $(this).data("hover", true);
        },
        function() {
            $(this).data("hover", false);
            if (!search_box.data("focus"))
            {
                if (search_box.val() == "")
                {
                    $(this).val($(this).attr("ph-text"));
                }
                search_box.focus();
            }
        }
    );

    search_menu.find('input').click(function (ev) {
        if ($(ev.target).is("#cbgsearchfirstp"))
        {
            $("#cbgsearchtitle").prop('checked', false);
        }
        else if ($(ev.target).is("#cbgsearchtitle"))
        {
            $("#cbgsearchfirstp").prop('checked', false);
        }

    });

    $("#userbar_search_forums").click(function (ev) {
        $("#searcharea_menu").slideToggle('fast');
        ev.preventDefault();
        return false;
    });

    $("#searcharea_menu input").click(function (ev) {
        $("#userbar_search_forums").text($(this).parent().text());
        $("#searcharea_menu").slideToggle('fast');
    });

    $(document).click(function (ev) {
        if ($(ev.target).parents("#userbar_search_box_container").length === 0)
        {
            $("#searchbar_menu").slideUp('fast');
        }
        if ($(ev.target).parents("#searcharea_menu").length === 0)
        {
            $("#searcharea_menu").slideUp('fast');
        }
    });

    $("#userbar_searchform").submit(function() {
        window.optimizely = window.optimizely || [];
        window.optimizely.push(['trackEvent', 'userbar_search_submit']);

        var sbox = $('#classic_search_box');

        if (sbox.val() == "" || sbox.val() == sbox.attr("ph-text"))
        {
            window.location = "/newsearch.php";
            return false;
        }
        else
        {
            var sa = $("#userbar_searchform input[name='searcharea']").serializeArray();
            if (sa.length > 0)
            {
                sa = sa[0].value;
                if (sa == "deals")
                {
                    var forums = $('#userbar_searchforums noscript').text();
                    $("#userbar_searchforums").empty().html(forums);
                }
                else if (sa.match("f-"))
                {
                    $("#userbar_searchforums").empty().html("<input type='hidden' name='forumchoice[]' value='"+sa.split("-")[1]+"' />");
                }
                else if (sa.match("t-"))
                {
                    $("#userbar_searchforums").empty().html("<input type='hidden' name='threadid' value='"+sa.split("-")[1]+"' />");
                }
            }
            $("#searcharea_menu").remove();
        }
    });

    if ($("#sharethislinkthread").length == 1)
    {
        $("#sharethislinkthread").click(function () {
            toggle_firstpost_tab("#sharethistab");
            return false;
        });
    }

    $('#showfeedback').click(function ()
    {
        var $container = $('#firstpost_tabcontainer');
        var $content = $('#publicfeedback');

        if ($container.data('state') === 'expanded')
        {
            $content.fadeOut('fast', function ()
            {
                $container.data('state', 'collapsed');
            });
            return;
        }

        if ($container.data('populated') === 'yes')
        {
            $content.fadeIn('fast', function ()
            {
                $container.data('state', 'expanded');
            });
            return;
        }

        var $button = $('#showfeedback');
        var forumId = $button.data('forum-id');
        var threadId = $button.data('thread-id');
        var params = {
            'threadId': threadId,
            'forumId': forumId,
            'classic': true
        };

        $.post('/ajax/getThreadStats.php', params, function (response)
        {
            if (response.success)
            {
                $container.html(response.html);
                $content = $('#publicfeedback');

                $container.data('populated', 'yes');
                $content.removeAttr('style').fadeIn('fast', function ()
                {
                    $container.data('state', 'expanded');
                });
            }
        }, 'json');
    });

    $(".voteselected").click(function() {
        $("#vote_feedback").fadeOut();
        return false;
    });

    $(".showwikipost").click(function () {
        $("#wikipost").slideToggle("normal");
        return false;
    });

    $("#editwiki").click(function () {
        $("#wikipost:hidden").show();
        return false;
    });

    $(".blackarrow img").attr("src", "/images/slickdeals/arrow_down.gif");

    $(".notice_tan .notice_close").click(function () {
        notice_close("tan");
        return false;
    });

    $(".notice_blue .notice_close").click(function () {
        notice_close("blue");
        return false;
    });

    function megaHoverOver()
    {
        $(this).find(".sub").show();

        if ($(this).hasClass("user_dd"))
        {
            return;
        }

        // Calculate width of all ul's
        (function($) {
            jQuery.fn.calcSubWidth = function() {
                rowWidth = 0;
                // Calculate row
                $(this).children("ul").each(function() {
                    rowWidth += $(this).width();
                });
            };
        })(jQuery);

        if ($(this).find(".row").length > 0) // If row exists...
        {
            var biggestRow = 0;
            //Calculate each row
            $(this).find(".row").each(function() {
                $(this).calcSubWidth();
                // Find biggest row
                if (rowWidth > biggestRow)
                {
                    biggestRow = rowWidth;
                }
            });
            // Set width
            $(this).find(".sub").css({
                'width' :biggestRow
            });
            $(this).find(".row:last").css({
                'margin':'0'
            });

        }
        else // If row does not exist...
        {
            $(this).calcSubWidth();
            // Set Width
            $(this).find(".sub").css({
                'width' : rowWidth
            });
        }
    }

    function megaHoverOut()
    {
        if ($(this).find("#sub_mystuff").length > 0)
        {
            $(".settings_box.mystuff").hide();
        }
        $(this).find(".sub").hide();
    }

    var config = {
        sensitivity: 2, // number = sensitivity threshold (must be 1 or higher)
        interval: 100, // number = milliseconds for onMouseOver polling interval
        over: megaHoverOver, // function = onMouseOver callback (REQUIRED)
        timeout: 50, // number = milliseconds delay before onMouseOut
        out: megaHoverOut // function = onMouseOut callback (REQUIRED)
    };

    if ($.fn.hoverIntent)
    {
        $("ul#global_nav_v3 li").hoverIntent(config);
        $("ul#jewels .jewel_dd").hoverIntent(config);
        $("ul#user_account .user_dd").hoverIntent(config);
    }

    $("#resend_validation").click(function (e) {
        e.preventDefault();
        location.href = '/forums/register.php?do=requestemail';
    });

});

sdwndw.log_out = function ()
{
    ht = document.getElementsByTagName("html");
    ht[0].style.filter = "progid:DXImageTransform.Microsoft.BasicImage(grayscale=1)";
    if (confirm('Are you sure you want to log out?'))
    {
        return true;
    }
    else
    {
        ht[0].style.filter = "";
        return false;
    }
}

sdwndw.displayExtra = function (layer_name, expand_name)
{
    layerObject = getObject(layer_name);
    expandObject = getObject(expand_name);

    if (layerObject.style.display == "none")
    {
        layerObject.style.display = "";
        expandObject.innerHTML = "";
    }
    else
    {
        layerObject.style.display = "none";
        expandObject.innerHTML = "<span class=small><a href=\"javascript:displayExtra('"+layer_name+"','"+expand_name+"')\">Expand</a> <img src=/images/expand.jpg width=7 height=7></span>";
    }
}

jQuery.fn.fadeToggle = function(speed, easing, callback) {
    return this.animate({
        opacity: 'toggle'
    }, speed, easing, callback);
};

sdwndw.toggledeal = function (id, state)
{
    var headerid = id.replace("deal_","deal_header_");

    switch(state)
    {
        case 'ca':
            $("#" + id).hide();
            $("#" + headerid).removeClass("deal_header_expand");
            return false;
        case 'c':
            $("#" + id).hide();
            $("#" + headerid).removeClass("deal_header_expand");
            return false;
        case 'ea':
            $("#" + id).show();
            $("#" + headerid).addClass("deal_header_expand");
            return false;
        case 'e':
            $("#" + id).show();
            $("#" + headerid).addClass("deal_header_expand");
            return false;
        default:
            if (!$("#deal_list").hasClass("grid")) {
                //location.href = $("#"+id+"_permalink").attr("href");
                //return false;
                $("#" + id).toggle();
                $("#" + headerid).toggleClass("deal_header_expand");
                draw_social_sharebuttons(id);
                return false;
            }
            break;
    }
    return true;
}

sdwndw.toggle = function (obj)
{
    $(obj).toggle();
}

sdwndw.toggleimg = function (img)
{
    var img_re = new RegExp("_collapsed\\.gif$");
    if (img.src.match(img_re))
    {
        img.src = img.src.replace(img_re, '.gif');
    }
    else
    {
        img_re = new RegExp("\\.gif$");
        img.src = img.src.replace(img_re, '_collapsed.gif');
    }
}

sdwndw.togglewelcome = function (id)
{
    $("#"+id).slideUp();
    createCookie(id,1,365);
}

sdwndw.toggledeal_array = function ()
{
    var img = $("#"+arguments[0]);
    var deals = $("nonexistant");
    var dealheaders = $("nonexistant");
    var len = arguments.length;
    for (var i = 1; i < len; i++)
    {
        deals = deals.add("#" + arguments[i]);
        dealheaders = dealheaders.add("#" + arguments[i].replace("deal_", "deal_header_"));
    // draw_social_sharebuttons(arguments[i]);
    }

    if (img.attr("src").match("plus.gif"))
    {
        //hide all
        img.attr("src", "/images/slickdeals/minus.gif");

        dealheaders.addClass("deal_header_expand");
        deals.show();
    }
    else
    {
        img.attr("src", "/images/slickdeals/plus.gif");
        deals.hide();
        dealheaders.removeClass("deal_header_expand");
    }

}

sdwndw.sdfetch_object = function (idname)
{
    if (document.getElementById)
    {
        return document.getElementById(idname);
    }
    else if (document.all)
    {
        return document.all[idname];
    }
    else if (document.layers)
    {
        return document.layers[idname];
    }
    else
    {
        return null;
    }
}

sdwndw.toggle_hottopic = function (id)
{
    var canSee = 'table-row';
    if (navigator.appName.indexOf("Microsoft") > -1)
    {
        canSee = 'block';
    }
    obj = sdfetch_object(id);

    var newobj;
    if (obj.style.display==canSee)
    {
        obj.style.display="none";
        newobj = sdfetch_object(id + "_heading");
        if (newobj.className=="alt4a")
        {
            newobj.className="alt6";
        }
        else
        {
            newobj.className="alt5";
        }
    }
    else
    {
        obj.style.display=canSee;
        newobj = sdfetch_object(id + "_heading");
        if (newobj.className=="alt5")
        {
            newobj.className="alt4";
        }
        else
        {
            newobj.className="alt4a";
        }
    }
}

sdwndw.getElementsByClass = function (searchClass,node,tag)
{
    var classElements = [];
    if (!node)
    {
        node = document;
    }
    if (!tag)
    {
        tag = '*';
    }
    var els = node.getElementsByTagName(tag);
    var elsLen = els.length;
    var pattern = new RegExp('(^|\\s)'+searchClass+'(\\s|$)');
    for (i = 0, j = 0; i < elsLen; i++)
    {
        if (pattern.test(els[i].className))
        {
            classElements[j] = els[i];
            j++;
        }
    }
    return classElements;
}


sdwndw.addEvent = function (obj, type, fn)
{
    if (obj.addEventListener)
    {
        obj.addEventListener(type, fn, false);
    }
    else if (obj.attachEvent)
    {
        obj["e"+type+fn] = fn;
        obj[type+fn] = function() {
            obj["e"+type+fn](window.event);
        };
        obj.attachEvent("on"+type, obj[type+fn]);
    }
    else
    {
        obj["on"+type] = obj["e"+type+fn];
    }
}

sdwndw.insertAfter = function (parent, node, referenceNode)
{
    parent.insertBefore(node, referenceNode.nextSibling);
}

sdwndw.showDiv = function (e, id)
{
    //  if (!e) var e = window.event;
    var getdiv = sdfetch_object(id);
    getdiv.style.display = "";
    var x = (e.pageX ? e.pageX : (e.clientX ? e.clientX : 0));
    var y = (e.pageY ? e.pageY : (e.clientY ? e.clientY : 0));
    getdiv.style.left = x + 5 + "px";
    getdiv.style.top = y + 5 + "px";
}

sdwndw.closeDiv = function (e, id)
{
    if (!e)
    {
        e = window.event;
    }
    var getdiv = sdfetch_object(id);
    getdiv.style.display = "none";
}

jQuery.fn.extend({
    sdshowRow: function () {
        var animparam = {};
        animparam.opacity = 'show';
        if (!jQuery.browser.msie)
        {
            animparam.height = 'show';
        }
        this.find("td").not(".sdrowwrap").children().wrap("<div></div>").end().addClass("sdrowwrap");
        return this.show().find("div").not(".nosdanim").animate(animparam, "fast");
    },

    sdhideRow: function () {
        var animparam = {};
        animparam.opacity = 'hide';
        if (!jQuery.browser.msie)
        {
            animparam.height = 'hide';
        }
        var jqObject = this;
        this.find("td").not(".sdrowwrap").children().wrap("<div></div>").end().addClass("sdrowwrap");
        this.find("div").not(".nosdanim").slice(0).animate(animparam,"fast");
        this.find("div").not(".nosdanim").slice(0,1).animate(animparam,"fast", function() {
            return jqObject.hide();
        });
        return jqObject;

    },

    sdtoggleRow: function () {
        if (this.css("display") == "none") {
            return this.sdshowRow();
        }
        else
        {
            return this.sdhideRow();
        }
    }
});

sdwndw.cumulativeOffset = function (element)
{
    var valueT = 0, valueL = 0;
    do
    {
        valueT += element.offsetTop || 0;
        valueL += element.offsetLeft || 0;
        element = element.offsetParent;
    }
    while (element);
    return [valueL, valueT];
}

sdwndw.toggleSearchFilter = function ()
{
    var filtCol = $("#search_left");
    var $sideimg = $("#search_showhidefilter img");
    var $newsrc;
    if (filtCol.filter(":visible").length > 0)
    {
        filtCol.hide().parent().attr("width","0px").siblings().attr("width","100%");
        createCookie("hidefiltercol", 1, 365);
        $newsrc = $sideimg.attr("src").replace("hide","show");
    }
    else
    {
        createCookie("hidefiltercol", 0, 365);
        filtCol.show().parent().attr("width","150px").siblings().removeAttr("width");
        $newsrc = $sideimg.attr("src").replace("show","hide");
    }
    $sideimg.attr("src", $newsrc);
}

sdwndw.cellHover = function (ele)
{
    ele = $(ele);
    if (ele.hasClass("alt1"))
    {
        ele.removeClass("alt1").addClass("alt1Active");
    }
    else if (ele.hasClass("alt2"))
    {
        ele.removeClass("alt2").addClass("alt2Active");
    }

    if (ele.hasClass("tdClick"))
    {
        ele.addClass("cursor");
    }
}

sdwndw.cellHoverOut = function (ele)
{
    ele = $(ele);
    if (ele.hasClass("alt1Active"))
    {
        ele.removeClass("alt1Active").addClass("alt1");
    }
    else if (ele.hasClass("alt2Active"))
    {
        ele.removeClass("alt2Active").addClass("alt2");
    }

    if (ele.hasClass("tdClick"))
    {
        ele.removeClass("cursor");
    }
}

sdwndw.cellHoverClick = function (ele,linksel)
{
    ele = $(ele);
    var loc = "";
    var tdTarget = ele.find("a.tdTarget");
    if (tdTarget.length > 0)
    {
        ele = tdTarget;
    }

    if (ele.is("a"))
    {
        loc = ele.attr("href");
    }
    else
    {
        loc = ele.find("a:" + linksel).attr("href");
    }
    window.location.href = loc;
}

sdwndw.tdHover = function ()
{
    cellHover(this);
}

sdwndw.tdHoverOut = function ()
{
    cellHoverOut(this);
}

sdwndw.tdHoverClick = function (ev)
{
    if (!(ev.shiftKey || ev.ctrlKey))
    {
        cellHoverClick(this,"last");
    }
}

sdwndw.tdThreadHover = function ()
{
    cellHover(this);
    cellHover($(this).siblings(".tdPostDate"));
    cellHover($(this).siblings(".tdThread"));
}
sdwndw.tdThreadHoverOut = function ()
{
    cellHoverOut(this);
    cellHoverOut($(this).siblings(".tdPostDate"));
    cellHoverOut($(this).siblings(".tdThread"));
}
sdwndw.tdPostDateClick = function (ev)
{
    if (!(ev.shiftKey || ev.ctrlKey))
    {
        var threadlink = $(this).attr("id");
        threadlink=threadlink.replace("td_postdate_","thread_title_");
        cellHoverClick($("#"+threadlink));
    }
}

sdwndw.updateHtmlBits = function (htmlbits) {
    htmlbits = $(htmlbits);
    htmlbits.find('htmlbit').each(function() {
        var id_text = $(this).attr('id');
        var effect = $(this).attr('effect');
        var mode = $(this).attr('mode');
        var newbit = $($(this).text());
        var bit = $("#"+id_text);

        if (typeof(effect) != "undefined")
        {
            bit.hide();
            try {
                newbit.hide();
            }
            catch (e) {}
        }
        if (mode == "remove_append" || mode == "remove_prepend")
        {
            bit.remove();
            mode = mode.replace("remove_", "");
        }
        if (mode == "append")
        {
            var appendto = $("#" + $(this).attr('appendto'));
            if (appendto.children("tbody").length > 0)
            {
                appendto.children("tbody").append(newbit);
            }
            else
            {
                appendto.append(newbit);
            }
        }
        else if (mode == "prepend") {
            var prependto = $("#" + $(this).attr('prependto'));
            if (prependto.children("tbody").length > 0)
            {
                prependto.children("tbody").prepend(newbit);
            }
            else
            {
                prependto.prepend(newbit);
            }
        }
        else
        {
            bit.replaceWith(newbit);
        }

        if (effect == "slide")
        {
            newbit.slideDown();
        }
        else if (effect == "fade")
        {
            try {
                newbit.fadeIn();
            }
            catch (e) {
                newbit.show();
            }
        }
    });
    var redir = htmlbits.find('redirecturl');
    if (redir.length > 0) {
        if (redir.attr('delay'))
        {
            setTimeout("window.location = '"+redir.text()+"';", redir.attr('delay'));
        }
        else
        {
            window.location = redir.text();
        }
    }
}

sdwndw.geturlvar = function (url,varname)
{
    var vars = url.split("&");
    for (var i=0;i<vars.length;i++)
    {
        var pair = vars[i].split("=");
        if (pair[0] == varname)
        {
            return pair[1];
        }
    }
    return "";
}

// function to confirm delete post
sdwndw.confirmdelete = function (postid)
{
    const reason = prompt("Enter reason for deletion:");

    if (reason !== null && reason !== "")
    {
        $.post('/forums/editpost.php', {
            'do': 'deletepost',
            's': fetch_sessionhash(),
            'p': postid,
            'deletepost': 'delete',
            'reason': reason,
            'securitytoken': SECURITYTOKEN
        }, function (data) {
            $("#edit"+postid).html($(data).find("postbit").text());
        }, "xml");

        return false;
    }

    return true;
}

// function to confirm delete announcement
sdwndw.confirmdeleteAnnounce = function (aid)
{
    var messagebox = confirm('You have chosen to delete Announcement '+aid+'.\r\rClick OK to delete it, or Cancel to hide this prompt.');
    if (messagebox)
    {
        $.post('/forums/announcement.php', {
            'delete': 'delete',
            'do': 'delete',
            's': fetch_sessionhash(),
            'announcementid': aid,
            'securitytoken': SECURITYTOKEN,
            'ajax': 1
        }, function () {
            $("#edit"+aid).remove();
        });

        return false;
    }
    return true;
}

// function to confirm delete pms
sdwndw.confirmpmdelete = function (pmid, fid)
{
    var messagebox = confirm('You have chosen to delete this PM.\r\rClick OK to delete it, or Cancel to hide this prompt.');
    if (messagebox)
    {
        var del_data = {
            'do': 'managepm',
            'dowhat': 'delete',
            's': fetch_sessionhash(),
            'pmid': pmid,
            'folderid': fid,
            'securitytoken': SECURITYTOKEN
        };
        del_data['pm['+pmid+']'] = 'true';

        $.post('/forums/private.php', del_data, function (data) {
            if ($(data).find("success").length > 0)
            {
                location.href = '/forums/private.php?folderid=' + fid;
            }
            else
            {
                alert("Error deleting PM");
            }
        }, "xml");

        return false;
    }
    return true;
}

// function to confirm filter post
sdwndw.confirmfilter = function (postid, reloadPageOnSuccess) {
    reloadPageOnSuccess = reloadPageOnSuccess || false;
    var messagebox = confirm('You have chosen to filter Post '+postid+'.\r\rClick OK to filter it, or Cancel to hide this prompt.');
    var divobj, myform, sessinput, doinput, pinput, sinput, bodyobj;
    if (messagebox) {
        var filter_data = {
            'do': 'filterpost',
            's': fetch_sessionhash(),
            'p': postid,
            'securitytoken': SECURITYTOKEN
        };
        $.post('/forums/editpost.php', filter_data, function (data) {
            if ($(data).find("postbit").length > 0)
            {
                if (reloadPageOnSuccess)
                {
                    location.reload();
                }
                else
                {
                    $("#edit"+postid).html($(data).find("postbit").text());
                }
            }
            else
            {
                alert("Error filtering post.");
            }
        }, "xml");
        return false;
    }
    return true;
}


// function to confirm delete post history
sdwndw.confirmphdelete = function (postid, phid) {
    var messagebox = confirm('You have chosen to delete Post History '+phid+' for Post '+postid+'.\r\rClick OK to delete it, or Cancel to hide this prompt.');
    var divobj, myform, sessinput, doinput, pinput, phinput, sinput, bodyobj;
    if (messagebox)
    {
        divobj = document.createElement("div");
        divobj.display = "none";
        myform = document.createElement("form");
        myform.action = "/forums/sdposthistory.php";
        myform.method = "post";

        sessinput = document.createElement("input");
        sessinput.name = "s";
        sessinput.type = "hidden";
        sessinput.value = fetch_sessionhash();

        doinput = document.createElement("input");
        doinput.name = "do";
        doinput.type = "hidden";
        doinput.value = "delete";

        pinput = document.createElement("input");
        pinput.name = "p";
        pinput.type = "hidden";
        pinput.value = postid;

        phinput = document.createElement("input");
        phinput.name = "phid";
        phinput.type = "hidden";
        phinput.value = phid;

        sinput = document.createElement("input");
        sinput.name = "securitytoken";
        sinput.type = "hidden";
        sinput.value = SECURITYTOKEN;

        myform.appendChild(sessinput);
        myform.appendChild(doinput);
        myform.appendChild(pinput);
        myform.appendChild(phinput);
        myform.appendChild(sinput);
        divobj.appendChild(myform);
        bodyobj = document.getElementById("contentbody");
        bodyobj.appendChild(divobj);
        myform.submit();
        return false;
    }
    return true;
}

// #############################################################################
// function to toggle the collapse state of an object, and NOT save to a cookie
sdwndw.toggle_postcollapse = function (objid) {
    if (!is_regexp) {
        return false;
    }

    obj = fetch_object("collapseobj_" + objid);
    div = fetch_object("collapsediv_" + objid);
    divh = fetch_object("collapsedivh_" + objid);

    if (!obj)
    {
        // nothing to collapse!
        return false;
    }

    if (obj.style.display == "none")
    {
        obj.style.display = "";
        if (div)
        {
            div.style.display = "none";
            divh.style.display = "";
        }
    }
    else
    {
        obj.style.display = "none";
        if (div)
        {
            div.style.display = "";
            divh.style.display = "none";
        }
    }
    return false;
}

sdwndw.hideGlobalFeature = function () {
    var feature = $("#global_feature");
    feature.slideToggle("fast");

    var cookienum = readCookie("sdfeaturebar");
    var featurenum = feature.attr("version");
    cookienum = cookienum == null ? "" : cookienum;
    if (!cookienum.match(featurenum))
    {
        createCookie("sdfeaturebar", featurenum + " " + cookienum, 365);
    }

    return false;
}

sdwndw.hideGiveawayBanner = function ()
{
    var feature = $("#giveawaybanner_daily");
    feature.slideToggle("fast");

    var cookienum = readCookie("sdfeaturebar");
    var featurenum = feature.attr("version");
    cookienum = cookienum == null ? "" : cookienum;
    if (!cookienum.match(featurenum))
    {
        createCookie("sdfeaturebar", featurenum + " " + cookienum, 365);
    }

    return false;
}

sdwndw.hideannouncement = function (id)
{
    $("#"+id).hide();
    createCookie(id,1,365);
}

sdwndw.makeSlug = function (slugcontent)
{
    // convert to lowercase (important: since on next step special chars are defined in lowercase only)
    slugcontent = slugcontent.toLowerCase();
    slugcontent = slugcontent.replace(/\s+|\s+$/,"");
    // convert special chars
    var   accents={a:/\u00e1/g,e:/u00e9/g,i:/\u00ed/g,o:/\u00f3/g,u:/\u00fa/g,n:/\u00f1/g};
    for (var i in accents) slugcontent = slugcontent.replace(accents[i],i);

    var slugcontent_hyphens = slugcontent.replace(/\s/g,'-');
    var finishedslug = slugcontent_hyphens.replace(/[^a-zA-Z0-9\-]/g,'');
    finishedslug = finishedslug.toLowerCase();
    finishedslug = finishedslug.replace(/\-{2,}/g, '-');
    return finishedslug;
}

$(document).ready(function () {
    if ($.fn.injectPriorEventId) {
        $.fn.injectPriorEventId();
    }

    $("#user_account_loggedout a.login_link").click(function (ev) {
        sd_ajax_login({
            form: null,
            hide_social_buttons: null,
            custom_message: null,
            callback_function: null,
            email: null,
            nousertrap: true,
            action_source: 'Log In Button'
        });

        ev.preventDefault();
        return false;
    });

    $("#panel_register .btn_register").click(function (ev) {

        var errors = false;
        $("#panel_register label input[type!='checkbox']").each(function () {
            if ($.trim($(this).val()) == '')
            {
                $(this).parent().find('.error').show();
                errors = true;
            }
            else
            {
                $(this).parent().find('.error').hide();
            }
        });

        if (!errors)
        {
            $("#panel_register").fadeOut(function () {
                $.post('/forums/register.php', {'do': 'gethvhash'}, function (data) {
                    Recaptcha.create(data.key, 'panel_captcha_insert', {
                        lang: data.lang,
                        theme: data.theme
                    });
                    $("#panel_hash").val(data.hash);
                }, "json");
                $("#panel_captcha").fadeIn();
            });
        }

        ev.preventDefault();
        return false;
    });

    fetch_width_dynamic(); // Initialize
    update_width_dynamic(); // Set for current width
});

/**
 * Check user login state, if logged in there isn't much to do - otherwise store form data temporarily and flash login.
 *
 * @param    Object    form
 * @returns  false
 */
sdwndw.user_check = function (o)
{
    var args, data, form, preview = false;

    form = ('currentTarget' in o) ? $(o.currentTarget.form) : $(o);
    args = form.attr('data-check').split('#');
    data = form.serialize();

    target = (form.attr('action').split('newthread.php').length > 1 && form.find('input[name="subject"]').length > 0) ? form.find('input[name="subject"]')[0].value : 0;

    if ($ && args.length > 1 && vB_Editor[args[0]].prepare_submit(target, args[1]))
    {
        $.post('/forums/sdcheckuser_ajax.php', data + '&_req_method_=' + form.attr('method') + '&where_from=' + escape(form.attr('action')), function (d)
        {
            if (d.logged_in == 1)
            {
                if (preview)
                {
                    form.append('<input name="preview" value="Preview Post" />');
                }
                form.unbind('onsubmit');
                form.removeAttr('onsubmit');
                form.submit();
            }
            else
            {
                sd_ajax_login();
            }
        },
        'json');
    }
    return false;
}

/**
 * Similar to previous function, just sans certain thread specific checks.
 *
 * @param    Object    o
 * @returns  false
 */
sdwndw.uc_da = function (o)
{
    if (!('$' in window))
    {
        // No JQ - do default action.
        return true;
    }

    var form = $(o);
    var formdata = form.serialize();
    var action = form.attr('action');
    var method = form.attr('method');
    var email = form.find("[name='email']").val() || '';

    $.post('/forums/sdcheckuser_ajax.php', formdata + '&_req_method_=' + method + '&where_from=' + encodeURIComponent(action), function (data)
    {
        if (data.logged_in === 1)
        {
            form.unbind('onsubmit');
            form.removeAttr('onsubmit');
            form.submit();
        }
        else
        {
            sd_ajax_login({
                form: null,
                hide_social_buttons: null,
                custom_message: null,
                callback_function: null,
                email: email,
                action_source: 'Add Deal Alert'
            });
        }
    });
    return false;
}

sdwndw.newsletter_subscribe = function (location)
{
    var email_address = $("#newsletter_email").attr("value");
    var source = 'frontpage';
    var regex = /^([a-zA-Z0-9_\.\-\+])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
    var params = {
        'email_address': email_address,
        'source': source
    };

    if (window.SD && window.SD.RegLog)
    {
        var registrationData = window.SD.RegLog.getRegistrationDataForForm($('#collapseobj_newslettersubscribe'), {
            'method': 'email',
            'trigger': source
        });
        window.SD.RegLog.attempt('newsletter', registrationData);
        params['registrationTracking'] = registrationData;
    }

    if (!email_address || !regex.test(email_address))
    {
        SD.Analytics.newsletterTrack('unsuccessful');
        $(".ns_txt_contain_red").html("Please enter a valid email address");
        return false;
    }
    $.post('/newsletter/newsletter_subscribe.php', params, function(data) {
        if (!data.error)
        {
            toggle_collapse('newslettersubscribe');
            $("#subscribemessage").toggle();
        }
        else
        {
            SD.Analytics.newsletterTrack('unsuccessful');
            $("#collapseobj_newslettersubscribe").html("You are already subscribed!");
        }
    }, 'json');
}

sdwndw.approve_post = function(threadId, postId, button, securityToken)
{
    const params = {
        'do': 'approveposts',
        'securitytoken': securityToken,
        'plist': {},
        'threadid': threadId,
    };

    params['plist'][postId] = 1;

    $.post('/forums/inlinemod.php?threadid=' + threadId, params);
    $(button).remove();
    document.getElementById(('post_message_' + postId)).style.backgroundColor = "#D2F7D9"
}

$(document).ready(function () {
    $("#newsletter_email").keypress(function(e) {
        if (e.which == 13)
        {
            $(this).blur();
            $("#newsletter_subscribe").focus().click();
        }
    });
});

/**
 * add extra event handler for new give rep button on post to call existing vb rep menu
 *
 */
$(document).ready(function () {
    $('.giverep_btn').click(function(e){
        var postid = $(this).attr('postid');
        $('#reputation_'+postid).click();
        e.preventDefault();
        return false;
    });

    // tooltip for store info
    if ($.fn.bt && $(".store_info").length > 0)
    {
        $(".store_info").each(function( index )
        {
            let storeId = $(this).attr('data-store-id');
            $(this).bt(
                $(".store_info_tooltip[data-store-id='" + storeId + "']").html(),
                {
                    trigger: 'hover',
                    positions: ['top'],
                    fill: 'white',
                    cssStyles: {color: "black"}
                }
            );
        });
    }
});

// Replacing SDXT crud
if (typeof __linkCap == "undefined")
{
    __linkCap = new Date();
    __linkCap.setTime(new Date().getTime() + 31449600000);

    $(document).on('mousedown', 'a[data-link]', function (e)
    {
        if (typeof $ != 'undefined' && 'cookie' in $)
        {
            $.cookie('sdxt', window.location.pathname + ':' + $(this).data('link'), { expires: 364, path: '/' });
        }
        else
        {
            document.cookie = 'sdxt=' + window.location.pathname + ':' + $(this).data('link') + '; expires=' + __linkCap.toUTCString() + '; path=/';
        }
    });

    if (typeof $ != 'undefined' && 'removeCookie' in $)
    {
        $.removeCookie('sdxt');
    }
    else
    {
        document.cookie = 'sdxt=""; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/';
    }
}

})(window);

;/*
 * timeago: a jQuery plugin, version: 0.9 (2010-06-21)
 * @requires jQuery v1.2.3 or later
 *
 * Timeago is a jQuery plugin that makes it easy to support automatically
 * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
 *
 * For usage and examples, visit:
 * http://timeago.yarp.com/
 *
 * Licensed under the MIT:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright (c) 2008-2010, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
 */
(function($) {
  $.timeago = function(timestamp) {
    if (timestamp instanceof Date) return inWords(timestamp);
    else if (typeof timestamp == "string") return inWords($.timeago.parse(timestamp));
    else return inWords($.timeago.datetime(timestamp));
  };
  var $t = $.timeago;

  $.extend($.timeago, {
    settings: {
      refreshMillis: 60000,
      allowFuture: false,
      strings: {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: "ago",
        suffixFromNow: "from now",
        seconds: "less than a minute",
        minute: "about a minute",
        minutes: "%d minutes",
        hour: "about an hour",
        hours: "about %d hours",
        day: "a day",
        days: "%d days",
        month: "about a month",
        months: "%d months",
        year: "about a year",
        years: "%d years",
        numbers: []
      }
    },
    inWords: function(distanceMillis) {
      var $l = this.settings.strings;
      var prefix = $l.prefixAgo;
      var suffix = $l.suffixAgo;
      if (this.settings.allowFuture) {
        if (distanceMillis < 0) {
          prefix = $l.prefixFromNow;
          suffix = $l.suffixFromNow;
        }
        distanceMillis = Math.abs(distanceMillis);
      }

      var seconds = distanceMillis / 1000;
      var minutes = seconds / 60;
      var hours = minutes / 60;
      var days = hours / 24;
      var years = days / 365;

      function substitute(stringOrFunction, number) {
        var string = $.isFunction(stringOrFunction) ? stringOrFunction(number) : stringOrFunction;
        var value = ($l.numbers && $l.numbers[number]) || number;
        return string.replace(/%d/i, value);
      }

      var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
        seconds < 90 && substitute($l.minute, 1) ||
        minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
        minutes < 90 && substitute($l.hour, 1) ||
        hours < 24 && substitute($l.hours, Math.round(hours)) ||
        hours < 48 && substitute($l.day, 1) ||
        days < 30 && substitute($l.days, Math.floor(days)) ||
        days < 60 && substitute($l.month, 1) ||
        days < 365 && substitute($l.months, Math.floor(days / 30)) ||
        years < 2 && substitute($l.year, 1) ||
        substitute($l.years, Math.floor(years));

      return $.trim([prefix, words, suffix].join(" "));
    },
    parse: function(iso8601) {
      var s = $.trim(iso8601);
      s = s.replace(/\.\d\d\d/,""); // remove milliseconds
      s = s.replace(/-/,"/").replace(/-/,"/");
      s = s.replace(/T/," ").replace(/Z/," UTC");
      s = s.replace(/([\+-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
      return new Date(s);
    },
    datetime: function(elem) {
      // jQuery's `is()` doesn't play well with HTML5 in IE
      var isTime = $(elem).get(0).tagName.toLowerCase() == "time"; // $(elem).is("time");
      var iso8601 = isTime ? $(elem).attr("datetime") : $(elem).attr("title");
      return $t.parse(iso8601);
    }
  });

  $.fn.timeago = function() {
    var self = this;
    self.each(refresh);

    var $s = $t.settings;
    if ($s.refreshMillis > 0) {
      setInterval(function() { self.each(refresh); }, $s.refreshMillis);
    }
    return self;
  };

  function refresh() {
    var data = prepareData(this);
    if (!isNaN(data.datetime)) {
      $(this).text(inWords(data.datetime));
    }
    return this;
  }

  function prepareData(element) {
    element = $(element);
    if (!element.data("timeago")) {
      element.data("timeago", { datetime: $t.datetime(element) });
      var text = $.trim(element.text());
      if (text.length > 0) element.attr("title", text);
    }
    return element.data("timeago");
  }

  function inWords(date) {
    return $t.inWords(distance(date));
  }

  function distance(date) {
    return (new Date().getTime() - date.getTime());
  }

  // fix for IE6 suckage
  document.createElement("abbr");
  document.createElement("time");
})(jQuery);

;window.ajax_result = null;
window.abandoned = true;

window.modal_subscription = function (e)
{
    abandoned = false;
    // Are we subscribing to the newsletter?
    if ($('#modal_newsletter_subscribe').is(':checked'))
    {
        // Make sure that they have entered a valid email...
        var email_address;
        if ($('#hidden_email').length > 0)
        {
            email_address = $('#hidden_email').val();
        }
        else
        {
            email_address = $("#modal_email").val();
        }

        var regex = /^([a-zA-Z0-9_\.\-\+])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
        // Is this a valid email?
        if (!email_address || !regex.test(email_address))
        {
            SD.Analytics.newsletterTrack('unsuccessful');
            $('#modal_email').val('');
            $('#modal_email').addClass('modal_error');
            $("#modal_email").attr('placeholder', 'Please enter a valid email address');
            e.preventDefault();
            return false;
        }
        else
        {
            $.post('/newsletter/newsletter_subscribe.php', {email_address: email_address}, function(data) {
                if (data.error === false)
                {
                    SD.Analytics.newsletterTrack('successful');
                    $('#modal_feedback').append('<p>Thank you. You are now subscribed to the newsletter</p>');
                    var params = {
                        'action'   : "newsletter",
                        'threadid' : $('#threadid').val()
                    };
                    $.ajax({
                        type: "GET",
                        url: "/ajax/modal_popups.php",
                        data: params
                    });
                }
                else
                {
                    SD.Analytics.newsletterTrack('unsuccessful');
                    $("#modal_feedback").append("<p>You are already subscribed to the newsletter</p>");
                }
            }, 'json');
            e.preventDefault();
        }
    }

    var current_form = $('#modal_da_form');

    modal_uc(current_form);
    $('#modal_subscription_form').hide();
    $('#modal_feedback').show();
    $('#modal_completed').show();
    e.preventDefault();
};

window.modal_uc = function (o)
{
    $('#modal_feedback').html("<div id=\"modal_wait\"></div>");

    var form     = $(o);
    var formdata = form.serialize();
    var action   = form.attr('action');
    var method   = form.attr('method');
    var email    = form.find("[name='email']").val() || '';
    var feedback = "";

    $.post('/forums/sdcheckuser_ajax.php', formdata + '&_req_method_=' + method + '&where_from=' + escape(action), function (data)
    {
        if (data.logged_in === 1)
        {
            if ($('#modal_daily_deal_subscribe').is(':checked'))
            {
                add_modal_dealalert("daily_deal");
            }

            add_modal_dealalert("custom_keyword");

        }
        else
        {
            sd_ajax_login({
                form: null,
                hide_social_buttons: null,
                custom_message: null,
                callback_function: null,
                email: email
            });
        }
    },'json');

};

window.add_modal_dealalert = function (deal_type)
{
    abandoned = false;

    if (deal_type == "daily_deal")
    {
        var params = {
            'keyword'    : "",
            'notifyby'   : 1,
            'votelevel'  : 14,
            'frequency'  : 2,
            'forumid'    : 9,
            'alertid'    : "",
            'do'         : "doadddealalert",
            'modal_type' : 0
        };
        $.post(
            '/ajax/modal_popups.php',
            params,
            function(data)
            {
                if (data.result == "failed")
                {
                    $('#modal_feedback').append("<p>You are already subscribed to the Daily Deal Alert</p>");
                }
                else
                {
                    $('#modal_feedback').append("<p>You have added the Daily Deal Digest to your deal alerts.</p>");
                    $('#modal_edit_deal_button').show();
                }

            }
            ,"json");
    }
    else if (deal_type == "custom_keyword")
    {
        var params = {
            'keyword'    : $('#modal_keyword').attr('keyword'),
            'notifyby'   : 1,
            'votelevel'  : 14,
            'frequency'  : 2,
            'forumid'    : 9,
            'alertid'    : "",
            'do'         : "doadddealalert",
            'modal_type' : 0
        };
        $.post(
            '/ajax/modal_popups.php',
            params,
            function(data)
            {
                if (data.result == "failed")
                {
                    $('#modal_feedback').append("<p>" + data.error + "</p>");
                }
                else
                {
                    $('#modal_feedback').append("<p>You have added \"" +$('#modal_keyword').attr('keyword') + "\" to your deal alerts.</p>");
                    $('body').append(data.template);
                }
            }
            ,"json");
    }
};

$(document).ready(function () {

    $("body").on("click", '.loginbox_form a.meh', function()
    {
        $("#loginbox_overlay").hide();
    });

    $("body").on("click", "#modal_dealalert a", function (event) {
        $(".dealalerts_edit_popup").show();
        event.preventDefault();
        return false;
    });

    $(document).on('click', '#modal_close', function ()
    {
        // Are we abandoning the modal?
        if (abandoned)
        {
            var params = {};
            params['action'] = "abandoned";
            params['threadid'] = $('#threadid').val();
            $.ajax({
                type: "GET",
                url: "/ajax/modal_popups.php",
                data: params
            });
        }
        if ($('#coupon_modal_container').is(':visible'))
        {
            $('#coupon_modal_container').detach();
        }
        else if ($('#deal_modal_container').is(':visible'))
        {
            $('#deal_modal_container').detach();
        }
    });

    $(document).on('click', '#modal_dealalert_button', modal_subscription);
    $(document).on('keydown', '#modal_email', (function (e) {
        if (e.keyCode == 13)
        {
            // Do nothing
            e.preventDefault();
            return false;
        }
        else
        {
            return true;
        }
    }));

    $(document).on('click', '#modal_close_button', function (e) {
        if (abandoned)
        {
            var params = {};
            params['action'] = "abandoned";
            params['threadid'] = $('#threadid').val();
            $.ajax({
                type: "GET",
                url: "/ajax/modal_popups.php",
                data: params
            });
        }
        if ($('#coupon_modal_container').is(':visible'))
        {
            $('#coupon_modal_container').detach();
        }
        else if ($('#deal_modal_container').is(':visible'))
        {
            $('#deal_modal_container').detach();
        }
    });

    $(document).on('click', '#modal_edit_deal_button', function(e) {
        $(".dealalerts_edit_popup").show();
        abandoned = false;
    });

    $(document).on('click', '.disable_modal', function() {
        var params = {};
        params['action'] = "disable_popup";
        $.ajax({
            type: "GET",
            url: "/ajax/modal_popups.php",
            data: params,
            success: function(server_response)
            {
                if ($('#coupon_modal_container').is(':visible'))
                {
                    $('#coupon_modal_container').detach();
                }
                else if ($('#deal_modal_container').is(':visible'))
                {
                    $('#deal_modal_container').detach();
                }
            }
        });
    });
});

;/* http://keith-wood.name/countdown.html
   Countdown for jQuery v1.6.1.
   Written by Keith Wood (kbwood{at}iinet.com.au) January 2008.
   Available under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) license.
   Please attribute the author if you use it. */

/* Display a countdown timer.
   Attach it with options like:
   $('div selector').countdown(
       {until: new Date(2009, 1 - 1, 1, 0, 0, 0), onExpiry: happyNewYear}); */

(function($) { // Hide scope, no $ conflict

/* Countdown manager. */
function Countdown() {
    this.regional = []; // Available regional settings, indexed by language code
    this.regional[''] = { // Default regional settings
        // The display texts for the counters
        labels: ['Years', 'Months', 'Weeks', 'Days', 'Hours', 'Minutes', 'Seconds'],
        // The display texts for the counters if only one
        labels1: ['Year', 'Month', 'Week', 'Day', 'Hour', 'Minute', 'Second'],
        compactLabels: ['y', 'm', 'w', 'd'], // The compact texts for the counters
        whichLabels: null, // Function to determine which labels to use
        digits: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], // The digits to display
        timeSeparator: ':', // Separator for time periods
        isRTL: false // True for right-to-left languages, false for left-to-right
    };
    this._defaults = {
        br: '<br/>',
        until: null, // new Date(year, mth - 1, day, hr, min, sec) - date/time to count down to
            // or numeric for seconds offset, or string for unit offset(s):
            // 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds
        since: null, // new Date(year, mth - 1, day, hr, min, sec) - date/time to count up from
            // or numeric for seconds offset, or string for unit offset(s):
            // 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds
        timezone: null, // The timezone (hours or minutes from GMT) for the target times,
            // or null for client local
        serverSync: null, // A function to retrieve the current server time for synchronisation
        format: 'dHMS', // Format for display - upper case for always, lower case only if non-zero,
            // 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds
        layout: '', // Build your own layout for the countdown
        compact: false, // True to display in a compact format, false for an expanded one
        significant: 0, // The number of periods with values to show, zero for all
        description: '', // The description displayed for the countdown
        expiryUrl: '', // A URL to load upon expiry, replacing the current page
        expiryText: '', // Text to display upon expiry, replacing the countdown
        alwaysExpire: false, // True to trigger onExpiry even if never counted down
        onExpiry: null, // Callback when the countdown expires -
            // receives no parameters and 'this' is the containing division
        onTick: null, // Callback when the countdown is updated -
            // receives int[7] being the breakdown by period (based on format)
            // and 'this' is the containing division
        tickInterval: 1 // Interval (seconds) between onTick callbacks
    };
    $.extend(this._defaults, this.regional['']);
    this._serverSyncs = [];
    // Shared timer for all countdowns
    function timerCallBack(timestamp) {
        var drawStart = (timestamp < 1e12 ? // New HTML5 high resolution timer
            (drawStart = performance.now ?
            (performance.now() + performance.timing.navigationStart) : Date.now()) :
            // Integer milliseconds since unix epoch
            timestamp || new Date().getTime());
        if (drawStart - animationStartTime >= 1000) {
            plugin._updateTargets();
            animationStartTime = drawStart;
        }
        requestAnimationFrame(timerCallBack);
    }
    var requestAnimationFrame = window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame || window.msRequestAnimationFrame || null;
        // This is when we expect a fall-back to setInterval as it's much more fluid
    var animationStartTime = 0;
    if (!requestAnimationFrame || $.noRequestAnimationFrame) {
        $.noRequestAnimationFrame = null;
        setInterval(function() { plugin._updateTargets(); }, 980); // Fall back to good old setInterval
    }
    else {
        animationStartTime = window.animationStartTime ||
            window.webkitAnimationStartTime || window.mozAnimationStartTime ||
            window.oAnimationStartTime || window.msAnimationStartTime || new Date().getTime();
        requestAnimationFrame(timerCallBack);
    }
}

var Y = 0; // Years
var O = 1; // Months
var W = 2; // Weeks
var D = 3; // Days
var H = 4; // Hours
var M = 5; // Minutes
var S = 6; // Seconds

$.extend(Countdown.prototype, {
    /* Class name added to elements to indicate already configured with countdown. */
    markerClassName: 'hasCountdown',
    /* Name of the data property for instance settings. */
    propertyName: 'countdown',

    /* Class name for the right-to-left marker. */
    _rtlClass: 'countdown_rtl',
    /* Class name for the countdown section marker. */
    _sectionClass: 'countdown_section',
    /* Class name for the period amount marker. */
    _amountClass: 'countdown_amount',
    /* Class name for the countdown row marker. */
    _rowClass: 'countdown_row',
    /* Class name for the holding countdown marker. */
    _holdingClass: 'countdown_holding',
    /* Class name for the showing countdown marker. */
    _showClass: 'countdown_show',
    /* Class name for the description marker. */
    _descrClass: 'countdown_descr',

    /* List of currently active countdown targets. */
    _timerTargets: [],

    /* Override the default settings for all instances of the countdown widget.
       @param  options  (object) the new settings to use as defaults */
    setDefaults: function(options) {
        this._resetExtraLabels(this._defaults, options);
        $.extend(this._defaults, options || {});
    },

    /* Convert a date/time to UTC.
       @param  tz     (number) the hour or minute offset from GMT, e.g. +9, -360
       @param  year   (Date) the date/time in that timezone or
                      (number) the year in that timezone
       @param  month  (number, optional) the month (0 - 11) (omit if year is a Date)
       @param  day    (number, optional) the day (omit if year is a Date)
       @param  hours  (number, optional) the hour (omit if year is a Date)
       @param  mins   (number, optional) the minute (omit if year is a Date)
       @param  secs   (number, optional) the second (omit if year is a Date)
       @param  ms     (number, optional) the millisecond (omit if year is a Date)
       @return  (Date) the equivalent UTC date/time */
    UTCDate: function(tz, year, month, day, hours, mins, secs, ms) {
        if (typeof year == 'object' && year.constructor == Date) {
            ms = year.getMilliseconds();
            secs = year.getSeconds();
            mins = year.getMinutes();
            hours = year.getHours();
            day = year.getDate();
            month = year.getMonth();
            year = year.getFullYear();
        }
        var d = new Date();
        d.setUTCFullYear(year);
        d.setUTCDate(1);
        d.setUTCMonth(month || 0);
        d.setUTCDate(day || 1);
        d.setUTCHours(hours || 0);
        d.setUTCMinutes((mins || 0) - (Math.abs(tz) < 30 ? tz * 60 : tz));
        d.setUTCSeconds(secs || 0);
        d.setUTCMilliseconds(ms || 0);
        return d;
    },

    /* Convert a set of periods into seconds.
       Averaged for months and years.
       @param  periods  (number[7]) the periods per year/month/week/day/hour/minute/second
       @return  (number) the corresponding number of seconds */
    periodsToSeconds: function(periods) {
        return periods[0] * 31557600 + periods[1] * 2629800 + periods[2] * 604800 +
            periods[3] * 86400 + periods[4] * 3600 + periods[5] * 60 + periods[6];
    },

    /* Attach the countdown widget to a div.
       @param  target   (element) the containing division
       @param  options  (object) the initial settings for the countdown */
    _attachPlugin: function(target, options) {
        target = $(target);
        if (target.hasClass(this.markerClassName)) {
            return;
        }
        var inst = {options: $.extend({}, this._defaults), _periods: [0, 0, 0, 0, 0, 0, 0]};
        target.addClass(this.markerClassName).data(this.propertyName, inst);
        this._optionPlugin(target, options);
    },

    /* Add a target to the list of active ones.
       @param  target  (element) the countdown target */
    _addTarget: function(target) {
        if (!this._hasTarget(target)) {
            this._timerTargets.push(target);
        }
    },

    /* See if a target is in the list of active ones.
       @param  target  (element) the countdown target
       @return  (boolean) true if present, false if not */
    _hasTarget: function(target) {
        return ($.inArray(target, this._timerTargets) > -1);
    },

    /* Remove a target from the list of active ones.
       @param  target  (element) the countdown target */
    _removeTarget: function(target) {
        this._timerTargets = $.map(this._timerTargets,
            function(value) { return (value == target ? null : value); }); // delete entry
    },

    /* Update each active timer target. */
    _updateTargets: function() {
        for (var i = this._timerTargets.length - 1; i >= 0; i--) {
            this._updateCountdown(this._timerTargets[i]);
        }
    },

    /* Reconfigure the settings for a countdown div.
       @param  target   (element) the control to affect
       @param  options  (object) the new options for this instance or
                        (string) an individual property name
       @param  value    (any) the individual property value (omit if options
                        is an object or to retrieve the value of a setting)
       @return  (any) if retrieving a value */
    _optionPlugin: function(target, options, value) {
        target = $(target);
        var inst = target.data(this.propertyName);
        if (!options || (typeof options == 'string' && value == null)) { // Get option
            var name = options;
            options = (inst || {}).options;
            return (options && name ? options[name] : options);
        }

        if (!target.hasClass(this.markerClassName)) {
            return;
        }
        options = options || {};
        if (typeof options == 'string') {
            var name = options;
            options = {};
            options[name] = value;
        }
        this._resetExtraLabels(inst.options, options);
        $.extend(inst.options, options);
        this._adjustSettings(target, inst);
        var now = new Date();
        if ((inst._since && inst._since < now) || (inst._until && inst._until > now)) {
            this._addTarget(target[0]);
        }
        this._updateCountdown(target, inst);
    },

    /* Redisplay the countdown with an updated display.
       @param  target  (jQuery) the containing division
       @param  inst    (object) the current settings for this instance */
    _updateCountdown: function(target, inst) {
        var $target = $(target);
        inst = inst || $target.data(this.propertyName);
        if (!inst) {
            return;
        }
        $target.html(this._generateHTML(inst)).toggleClass(this._rtlClass, inst.options.isRTL);
        if ($.isFunction(inst.options.onTick)) {
            var periods = inst._hold != 'lap' ? inst._periods :
                this._calculatePeriods(inst, inst._show, inst.options.significant, new Date());
            if (inst.options.tickInterval == 1 ||
                    this.periodsToSeconds(periods) % inst.options.tickInterval == 0) {
                inst.options.onTick.apply(target, [periods]);
            }
        }
        var expired = inst._hold != 'pause' &&
            (inst._since ? inst._now.getTime() < inst._since.getTime() :
            inst._now.getTime() >= inst._until.getTime());
        if (expired && !inst._expiring) {
            inst._expiring = true;
            if (this._hasTarget(target) || inst.options.alwaysExpire) {
                this._removeTarget(target);
                if ($.isFunction(inst.options.onExpiry)) {
                    inst.options.onExpiry.apply(target, []);
                }
                if (inst.options.expiryText) {
                    var layout = inst.options.layout;
                    inst.options.layout = inst.options.expiryText;
                    this._updateCountdown(target, inst);
                    inst.options.layout = layout;
                }
                if (inst.options.expiryUrl) {
                    window.location = inst.options.expiryUrl;
                }
            }
            inst._expiring = false;
        }
        else if (inst._hold == 'pause') {
            this._removeTarget(target);
        }
        $target.data(this.propertyName, inst);
    },

    /* Reset any extra labelsn and compactLabelsn entries if changing labels.
       @param  base     (object) the options to be updated
       @param  options  (object) the new option values */
    _resetExtraLabels: function(base, options) {
        var changingLabels = false;
        for (var n in options) {
            if (n != 'whichLabels' && n.match(/[Ll]abels/)) {
                changingLabels = true;
                break;
            }
        }
        if (changingLabels) {
            for (var n in base) { // Remove custom numbered labels
                if (n.match(/[Ll]abels[02-9]/)) {
                    base[n] = null;
                }
            }
        }
    },

    /* Calculate interal settings for an instance.
       @param  target  (element) the containing division
       @param  inst    (object) the current settings for this instance */
    _adjustSettings: function(target, inst) {
        var now;
        var serverOffset = 0;
        var serverEntry = null;
        for (var i = 0; i < this._serverSyncs.length; i++) {
            if (this._serverSyncs[i][0] == inst.options.serverSync) {
                serverEntry = this._serverSyncs[i][1];
                break;
            }
        }
        if (serverEntry != null) {
            serverOffset = (inst.options.serverSync ? serverEntry : 0);
            now = new Date();
        }
        else {
            var serverResult = ($.isFunction(inst.options.serverSync) ?
                inst.options.serverSync.apply(target, []) : null);
            now = new Date();
            serverOffset = (serverResult ? now.getTime() - serverResult.getTime() : 0);
            this._serverSyncs.push([inst.options.serverSync, serverOffset]);
        }
        var timezone = inst.options.timezone;
        timezone = (timezone == null ? -now.getTimezoneOffset() : timezone);
        inst._since = inst.options.since;
        if (inst._since != null) {
            inst._since = this.UTCDate(timezone, this._determineTime(inst._since, null));
            if (inst._since && serverOffset) {
                inst._since.setMilliseconds(inst._since.getMilliseconds() + serverOffset);
            }
        }
        inst._until = this.UTCDate(timezone, this._determineTime(inst.options.until, now));
        if (serverOffset) {
            inst._until.setMilliseconds(inst._until.getMilliseconds() + serverOffset);
        }
        inst._show = this._determineShow(inst);
    },

    /* Remove the countdown widget from a div.
       @param  target  (element) the containing division */
    _destroyPlugin: function(target) {
        target = $(target);
        if (!target.hasClass(this.markerClassName)) {
            return;
        }
        this._removeTarget(target[0]);
        target.removeClass(this.markerClassName).empty().removeData(this.propertyName);
    },

    /* Pause a countdown widget at the current time.
       Stop it running but remember and display the current time.
       @param  target  (element) the containing division */
    _pausePlugin: function(target) {
        this._hold(target, 'pause');
    },

    /* Pause a countdown widget at the current time.
       Stop the display but keep the countdown running.
       @param  target  (element) the containing division */
    _lapPlugin: function(target) {
        this._hold(target, 'lap');
    },

    /* Resume a paused countdown widget.
       @param  target  (element) the containing division */
    _resumePlugin: function(target) {
        this._hold(target, null);
    },

    /* Pause or resume a countdown widget.
       @param  target  (element) the containing division
       @param  hold    (string) the new hold setting */
    _hold: function(target, hold) {
        var inst = $.data(target, this.propertyName);
        if (inst) {
            if (inst._hold == 'pause' && !hold) {
                inst._periods = inst._savePeriods;
                var sign = (inst._since ? '-' : '+');
                inst[inst._since ? '_since' : '_until'] =
                    this._determineTime(sign + inst._periods[0] + 'y' +
                        sign + inst._periods[1] + 'o' + sign + inst._periods[2] + 'w' +
                        sign + inst._periods[3] + 'd' + sign + inst._periods[4] + 'h' +
                        sign + inst._periods[5] + 'm' + sign + inst._periods[6] + 's');
                this._addTarget(target);
            }
            inst._hold = hold;
            inst._savePeriods = (hold == 'pause' ? inst._periods : null);
            $.data(target, this.propertyName, inst);
            this._updateCountdown(target, inst);
        }
    },

    /* Return the current time periods.
       @param  target  (element) the containing division
       @return  (number[7]) the current periods for the countdown */
    _getTimesPlugin: function(target) {
        var inst = $.data(target, this.propertyName);
        return (!inst ? null : (!inst._hold ? inst._periods :
            this._calculatePeriods(inst, inst._show, inst.options.significant, new Date())));
    },

    /* A time may be specified as an exact value or a relative one.
       @param  setting      (string or number or Date) - the date/time value
                            as a relative or absolute value
       @param  defaultTime  (Date) the date/time to use if no other is supplied
       @return  (Date) the corresponding date/time */
    _determineTime: function(setting, defaultTime) {
        var offsetNumeric = function(offset) { // e.g. +300, -2
            var time = new Date();
            time.setTime(time.getTime() + offset * 1000);
            return time;
        };
        var offsetString = function(offset) { // e.g. '+2d', '-4w', '+3h +30m'
            offset = offset.toLowerCase();
            var time = new Date();
            var year = time.getFullYear();
            var month = time.getMonth();
            var day = time.getDate();
            var hour = time.getHours();
            var minute = time.getMinutes();
            var second = time.getSeconds();
            var pattern = /([+-]?[0-9]+)\s*(s|m|h|d|w|o|y)?/g;
            var matches = pattern.exec(offset);
            while (matches) {
                switch (matches[2] || 's') {
                    case 's': second += parseInt(matches[1], 10); break;
                    case 'm': minute += parseInt(matches[1], 10); break;
                    case 'h': hour += parseInt(matches[1], 10); break;
                    case 'd': day += parseInt(matches[1], 10); break;
                    case 'w': day += parseInt(matches[1], 10) * 7; break;
                    case 'o':
                        month += parseInt(matches[1], 10);
                        day = Math.min(day, plugin._getDaysInMonth(year, month));
                        break;
                    case 'y':
                        year += parseInt(matches[1], 10);
                        day = Math.min(day, plugin._getDaysInMonth(year, month));
                        break;
                }
                matches = pattern.exec(offset);
            }
            return new Date(year, month, day, hour, minute, second, 0);
        };
        var time = (setting == null ? defaultTime :
            (typeof setting == 'string' ? offsetString(setting) :
            (typeof setting == 'number' ? offsetNumeric(setting) : setting)));
        if (time) time.setMilliseconds(0);
        return time;
    },

    /* Determine the number of days in a month.
       @param  year   (number) the year
       @param  month  (number) the month
       @return  (number) the days in that month */
    _getDaysInMonth: function(year, month) {
        return 32 - new Date(year, month, 32).getDate();
    },

    /* Determine which set of labels should be used for an amount.
       @param  num  (number) the amount to be displayed
       @return  (number) the set of labels to be used for this amount */
    _normalLabels: function(num) {
        return num;
    },

    /* Generate the HTML to display the countdown widget.
       @param  inst  (object) the current settings for this instance
       @return  (string) the new HTML for the countdown display */
    _generateHTML: function(inst) {
        var self = this;
        // Determine what to show
        inst._periods = (inst._hold ? inst._periods :
            this._calculatePeriods(inst, inst._show, inst.options.significant, new Date()));
        // Show all 'asNeeded' after first non-zero value
        var shownNonZero = false;
        var showCount = 0;
        var sigCount = inst.options.significant;
        var show = $.extend({}, inst._show);
        for (var period = Y; period <= S; period++) {
            shownNonZero |= (inst._show[period] == '?' && inst._periods[period] > 0);
            show[period] = (inst._show[period] == '?' && !shownNonZero ? null : inst._show[period]);
            showCount += (show[period] ? 1 : 0);
            sigCount -= (inst._periods[period] > 0 ? 1 : 0);
        }
        var showSignificant = [false, false, false, false, false, false, false];
        for (var period = S; period >= Y; period--) { // Determine significant periods
            if (inst._show[period]) {
                if (inst._periods[period]) {
                    showSignificant[period] = true;
                }
                else {
                    showSignificant[period] = sigCount > 0;
                    sigCount--;
                }
            }
        }
        var labels = (inst.options.compact ? inst.options.compactLabels : inst.options.labels);
        var whichLabels = inst.options.whichLabels || this._normalLabels;
        var showCompact = function(period) {
            var labelsNum = inst.options['compactLabels' + whichLabels(inst._periods[period])];
            return (show[period] ? self._translateDigits(inst, inst._periods[period]) +
                (labelsNum ? labelsNum[period] : labels[period]) + ' ' : '');
        };
        var showFull = function(period) {
            var labelsNum = inst.options['labels' + whichLabels(inst._periods[period])];
            return ((!inst.options.significant && show[period]) ||
                (inst.options.significant && showSignificant[period]) ?
                '<span class="' + plugin._sectionClass + '">' +
                '<span class="' + plugin._amountClass + '">' +
                self._translateDigits(inst, inst._periods[period]) + '</span>' + inst.options.br +
                (labelsNum ? labelsNum[period] : labels[period]) + '</span>' : '');
        };
        return (inst.options.layout ? this._buildLayout(inst, show, inst.options.layout,
            inst.options.compact, inst.options.significant, showSignificant) :
            ((inst.options.compact ? // Compact version
            '<span class="' + this._rowClass + ' ' + this._amountClass +
            (inst._hold ? ' ' + this._holdingClass : '') + '">' +
            showCompact(Y) + showCompact(O) + showCompact(W) + showCompact(D) +
            (show[H] ? this._minDigits(inst, inst._periods[H], 2) : '') +
            (show[M] ? (show[H] ? inst.options.timeSeparator : '') +
            this._minDigits(inst, inst._periods[M], 2) : '') +
            (show[S] ? (show[H] || show[M] ? inst.options.timeSeparator : '') +
            this._minDigits(inst, inst._periods[S], 2) : '') :
            // Full version
            '<span class="' + this._rowClass + ' ' + this._showClass + (inst.options.significant || showCount) +
            (inst._hold ? ' ' + this._holdingClass : '') + '">' +
            showFull(Y) + showFull(O) + showFull(W) + showFull(D) +
            showFull(H) + showFull(M) + showFull(S)) + '</span>' +
            (inst.options.description ? '<span class="' + this._rowClass + ' ' + this._descrClass + '">' +
            inst.options.description + '</span>' : '')));
    },

    /* Construct a custom layout.
       @param  inst             (object) the current settings for this instance
       @param  show             (string[7]) flags indicating which periods are requested
       @param  layout           (string) the customised layout
       @param  compact          (boolean) true if using compact labels
       @param  significant      (number) the number of periods with values to show, zero for all
       @param  showSignificant  (boolean[7]) other periods to show for significance
       @return  (string) the custom HTML */
    _buildLayout: function(inst, show, layout, compact, significant, showSignificant) {
        var labels = inst.options[compact ? 'compactLabels' : 'labels'];
        var whichLabels = inst.options.whichLabels || this._normalLabels;
        var labelFor = function(index) {
            return (inst.options[(compact ? 'compactLabels' : 'labels') +
                whichLabels(inst._periods[index])] || labels)[index];
        };
        var digit = function(value, position) {
            return inst.options.digits[Math.floor(value / position) % 10];
        };
        var subs = {desc: inst.options.description, sep: inst.options.timeSeparator,
            yl: labelFor(Y), yn: this._minDigits(inst, inst._periods[Y], 1),
            ynn: this._minDigits(inst, inst._periods[Y], 2),
            ynnn: this._minDigits(inst, inst._periods[Y], 3), y1: digit(inst._periods[Y], 1),
            y10: digit(inst._periods[Y], 10), y100: digit(inst._periods[Y], 100),
            y1000: digit(inst._periods[Y], 1000),
            ol: labelFor(O), on: this._minDigits(inst, inst._periods[O], 1),
            onn: this._minDigits(inst, inst._periods[O], 2),
            onnn: this._minDigits(inst, inst._periods[O], 3), o1: digit(inst._periods[O], 1),
            o10: digit(inst._periods[O], 10), o100: digit(inst._periods[O], 100),
            o1000: digit(inst._periods[O], 1000),
            wl: labelFor(W), wn: this._minDigits(inst, inst._periods[W], 1),
            wnn: this._minDigits(inst, inst._periods[W], 2),
            wnnn: this._minDigits(inst, inst._periods[W], 3), w1: digit(inst._periods[W], 1),
            w10: digit(inst._periods[W], 10), w100: digit(inst._periods[W], 100),
            w1000: digit(inst._periods[W], 1000),
            dl: labelFor(D), dn: this._minDigits(inst, inst._periods[D], 1),
            dnn: this._minDigits(inst, inst._periods[D], 2),
            dnnn: this._minDigits(inst, inst._periods[D], 3), d1: digit(inst._periods[D], 1),
            d10: digit(inst._periods[D], 10), d100: digit(inst._periods[D], 100),
            d1000: digit(inst._periods[D], 1000),
            hl: labelFor(H), hn: this._minDigits(inst, inst._periods[H], 1),
            hnn: this._minDigits(inst, inst._periods[H], 2),
            hnnn: this._minDigits(inst, inst._periods[H], 3), h1: digit(inst._periods[H], 1),
            h10: digit(inst._periods[H], 10), h100: digit(inst._periods[H], 100),
            h1000: digit(inst._periods[H], 1000),
            ml: labelFor(M), mn: this._minDigits(inst, inst._periods[M], 1),
            mnn: this._minDigits(inst, inst._periods[M], 2),
            mnnn: this._minDigits(inst, inst._periods[M], 3), m1: digit(inst._periods[M], 1),
            m10: digit(inst._periods[M], 10), m100: digit(inst._periods[M], 100),
            m1000: digit(inst._periods[M], 1000),
            sl: labelFor(S), sn: this._minDigits(inst, inst._periods[S], 1),
            snn: this._minDigits(inst, inst._periods[S], 2),
            snnn: this._minDigits(inst, inst._periods[S], 3), s1: digit(inst._periods[S], 1),
            s10: digit(inst._periods[S], 10), s100: digit(inst._periods[S], 100),
            s1000: digit(inst._periods[S], 1000)};
        var html = layout;
        // Replace period containers: {p<}...{p>}
        for (var i = Y; i <= S; i++) {
            var period = 'yowdhms'.charAt(i);
            var re = new RegExp('\\{' + period + '<\\}(.*)\\{' + period + '>\\}', 'g');
            html = html.replace(re, ((!significant && show[i]) ||
                (significant && showSignificant[i]) ? '$1' : ''));
        }
        // Replace period values: {pn}
        $.each(subs, function(n, v) {
            var re = new RegExp('\\{' + n + '\\}', 'g');
            html = html.replace(re, v);
        });
        return html;
    },

    /* Ensure a numeric value has at least n digits for display.
       @param  inst   (object) the current settings for this instance
       @param  value  (number) the value to display
       @param  len    (number) the minimum length
       @return  (string) the display text */
    _minDigits: function(inst, value, len) {
        value = '' + value;
        if (value.length >= len) {
            return this._translateDigits(inst, value);
        }
        value = '0000000000' + value;
        return this._translateDigits(inst, value.substr(value.length - len));
    },

    /* Translate digits into other representations.
       @param  inst   (object) the current settings for this instance
       @param  value  (string) the text to translate
       @return  (string) the translated text */
    _translateDigits: function(inst, value) {
        return ('' + value).replace(/[0-9]/g, function(digit) {
                return inst.options.digits[digit];
            });
    },

    /* Translate the format into flags for each period.
       @param  inst  (object) the current settings for this instance
       @return  (string[7]) flags indicating which periods are requested (?) or
                required (!) by year, month, week, day, hour, minute, second */
    _determineShow: function(inst) {
        var format = inst.options.format;
        var show = [];
        show[Y] = (format.match('y') ? '?' : (format.match('Y') ? '!' : null));
        show[O] = (format.match('o') ? '?' : (format.match('O') ? '!' : null));
        show[W] = (format.match('w') ? '?' : (format.match('W') ? '!' : null));
        show[D] = (format.match('d') ? '?' : (format.match('D') ? '!' : null));
        show[H] = (format.match('h') ? '?' : (format.match('H') ? '!' : null));
        show[M] = (format.match('m') ? '?' : (format.match('M') ? '!' : null));
        show[S] = (format.match('s') ? '?' : (format.match('S') ? '!' : null));
        return show;
    },

    /* Calculate the requested periods between now and the target time.
       @param  inst         (object) the current settings for this instance
       @param  show         (string[7]) flags indicating which periods are requested/required
       @param  significant  (number) the number of periods with values to show, zero for all
       @param  now          (Date) the current date and time
       @return  (number[7]) the current time periods (always positive)
                by year, month, week, day, hour, minute, second */
    _calculatePeriods: function(inst, show, significant, now) {
        // Find endpoints
        inst._now = now;
        inst._now.setMilliseconds(0);
        var until = new Date(inst._now.getTime());
        if (inst._since) {
            if (now.getTime() < inst._since.getTime()) {
                inst._now = now = until;
            }
            else {
                now = inst._since;
            }
        }
        else {
            until.setTime(inst._until.getTime());
            if (now.getTime() > inst._until.getTime()) {
                inst._now = now = until;
            }
        }
        // Calculate differences by period
        var periods = [0, 0, 0, 0, 0, 0, 0];
        if (show[Y] || show[O]) {
            // Treat end of months as the same
            var lastNow = plugin._getDaysInMonth(now.getFullYear(), now.getMonth());
            var lastUntil = plugin._getDaysInMonth(until.getFullYear(), until.getMonth());
            var sameDay = (until.getDate() == now.getDate() ||
                (until.getDate() >= Math.min(lastNow, lastUntil) &&
                now.getDate() >= Math.min(lastNow, lastUntil)));
            var getSecs = function(date) {
                return (date.getHours() * 60 + date.getMinutes()) * 60 + date.getSeconds();
            };
            var months = Math.max(0,
                (until.getFullYear() - now.getFullYear()) * 12 + until.getMonth() - now.getMonth() +
                ((until.getDate() < now.getDate() && !sameDay) ||
                (sameDay && getSecs(until) < getSecs(now)) ? -1 : 0));
            periods[Y] = (show[Y] ? Math.floor(months / 12) : 0);
            periods[O] = (show[O] ? months - periods[Y] * 12 : 0);
            // Adjust for months difference and end of month if necessary
            now = new Date(now.getTime());
            var wasLastDay = (now.getDate() == lastNow);
            var lastDay = plugin._getDaysInMonth(now.getFullYear() + periods[Y],
                now.getMonth() + periods[O]);
            if (now.getDate() > lastDay) {
                now.setDate(lastDay);
            }
            now.setFullYear(now.getFullYear() + periods[Y]);
            now.setMonth(now.getMonth() + periods[O]);
            if (wasLastDay) {
                now.setDate(lastDay);
            }
        }
        var diff = Math.floor((until.getTime() - now.getTime()) / 1000);
        var extractPeriod = function(period, numSecs) {
            periods[period] = (show[period] ? Math.floor(diff / numSecs) : 0);
            diff -= periods[period] * numSecs;
        };
        extractPeriod(W, 604800);
        extractPeriod(D, 86400);
        extractPeriod(H, 3600);
        extractPeriod(M, 60);
        extractPeriod(S, 1);
        if (diff > 0 && !inst._since) { // Round up if left overs
            var multiplier = [1, 12, 4.3482, 7, 24, 60, 60];
            var lastShown = S;
            var max = 1;
            for (var period = S; period >= Y; period--) {
                if (show[period]) {
                    if (periods[lastShown] >= max) {
                        periods[lastShown] = 0;
                        diff = 1;
                    }
                    if (diff > 0) {
                        periods[period]++;
                        diff = 0;
                        lastShown = period;
                        max = 1;
                    }
                }
                max *= multiplier[period];
            }
        }
        if (significant) { // Zero out insignificant periods
            for (var period = Y; period <= S; period++) {
                if (significant && periods[period]) {
                    significant--;
                }
                else if (!significant) {
                    periods[period] = 0;
                }
            }
        }
        return periods;
    }
});

// The list of commands that return values and don't permit chaining
var getters = ['getTimes'];

/* Determine whether a command is a getter and doesn't permit chaining.
   @param  command    (string, optional) the command to run
   @param  otherArgs  ([], optional) any other arguments for the command
   @return  true if the command is a getter, false if not */
function isNotChained(command, otherArgs) {
    if (command == 'option' && (otherArgs.length == 0 ||
            (otherArgs.length == 1 && typeof otherArgs[0] == 'string'))) {
        return true;
    }
    return $.inArray(command, getters) > -1;
}

/* Process the countdown functionality for a jQuery selection.
   @param  options  (object) the new settings to use for these instances (optional) or
                    (string) the command to run (optional)
   @return  (jQuery) for chaining further calls or
            (any) getter value */
$.fn.countdown = function(options) {
    var otherArgs = Array.prototype.slice.call(arguments, 1);
    if (isNotChained(options, otherArgs)) {
        return plugin['_' + options + 'Plugin'].
            apply(plugin, [this[0]].concat(otherArgs));
    }
    return this.each(function() {
        if (typeof options == 'string') {
            if (!plugin['_' + options + 'Plugin']) {
                throw 'Unknown command: ' + options;
            }
            plugin['_' + options + 'Plugin'].
                apply(plugin, [this].concat(otherArgs));
        }
        else {
            plugin._attachPlugin(this, options || {});
        }
    });
};

/* Initialise the countdown functionality. */
var plugin = $.countdown = new Countdown(); // Singleton instance

})(jQuery);