var bas = bas || {};
bas.utils = bas.utils || {};
bas.theme = bas.theme || {};
bas.search = bas.search || {};

bas.utils.loadScript = function (pUrl, pAfterPageLoad, pLoadAsync) {
    /// <summary>
    /// Given a URL, load a javascript file
    /// <param name='pUrl'>string. URL of JS file to load</param>
    /// <param name='pAfterPageLoad'>bool. optional. Load the file after page load?</param>
    /// <param name='pLoadAsync'>bool. optional. Load the file asyncronously?</param>
    ///</summary>
    var async = pLoadAsync || false;
    if (pAfterPageLoad) {
        $(document).ready(function () {
            var s = document.createElement('script');
            s.src = pUrl;
            var root = document.getElementsByTagName('script')[0];
            s.async = async;
            root.parentNode.insertBefore(s, root);
        });
    } else {
        var s = document.createElement('script');
        s.src = pUrl;
        var root = document.getElementsByTagName('script')[0];
        s.async = async;
        root.parentNode.insertBefore(s, root);
    }
}

bas.utils.log = function (pMessage, pLogObjects) {
    /// <summary>
    /// Convenience wrapper for logging data/errors to the console
    /// <param name='pMessage'>string. Message to log</param>
    /// <param name='pLogObjects'>object, or array of objects. Params to be logged to the console</param>
    ///</summary>

    if (console && console.log) {
        console.log(pMessage, pLogObjects);
    }
}

bas.utils.callPageService = function (pMethod, pRequestParams, pAsync) {
    /// <summary> Call the PageServices webservice </summary>
    /// <param name='pMethod'>string. Webmethod name</param>
    /// <param name='pRequestParams'>JS Object. Needs to have properties that map to the parameter names in the webmethod</param>
    /// <param name='pAsync'>optional bool. Is the call async or not. Default is false.</param>
    /// <returns> Response object from webservice </returns>

    var pRequestParams = pRequestParams || {};
    var pAsync = pAsync || false;
    var _result; //returned result object as defined by the webmethod

    $.ajax({
        type: "POST",
        url: '/WebServices/PageServices.asmx/' + pMethod,
        contentType: "application/json; charset=utf-8",
        async: pAsync,
        dataType: "json",
        data: JSON.stringify(pRequestParams),
        success: function (result) {
            _result = result.d;
            if (_result.Error && _result.Error != '')
                bas.utils.log('bas.utils.callPageService error calling ' + pMethod, [pMethod, pRequestParams, pAsync, result]);
        },
        error: function (result) {
            _result = result.d;
            bas.utils.log('bas.utils.callPageService error calling ' + pMethod, [pMethod, pRequestParams, pAsync, result]);
        }
    });
    return _result;
}


bas.utils.ajaxWrapperObjectParam = function (options) {
    var defaultOptions = {
        method: '',
        data: {},
        successFunc: false,
        error: false,
        context: false,
        service: false,
        isAsync: true,
        persistedValues: false
    };

    var opts = $.extend({}, defaultOptions , options);
    bas.utils.ajaxWrapper(opts.method, opts.data, opts.successFunc, opts.error, opts.context, opts.service, opts.isAsync, opts.persistedValues);
};

bas.utils.ajaxWrapper = function (method, data, successFunc, error, context, service, isAsync, persistedValues) {
    /// <summary>
    /// Wrapper for jquery's post function
    /// <param name='method'>web service method to call</param>
    /// <param name='data'>the data to post</param>
    /// <param name='successFunc'>function to call on success</param>
    /// <param name='error'>function to call on error</param>
    /// <param name='context'>context from which to call the success callback</param>
    /// <param name='service'>the asmx service path</param>
    /// <param name='isAsync'>perform async, defaults true</param>
    ///</summary>
    var me = context || this;
    service = service || '/WebServices/PageServices.asmx/';
    isAsync = typeof isAsync === 'undefined' ? true : isAsync;

    if (error) { //wrap the error function so the only thing returned is the actual error message string
        var oldErrorFunc = error;
        error = function (msg) {
            var error = eval("(" + msg.responseText + ")");
            oldErrorFunc(error);
        }
    }

    if (!successFunc) {
        successFunc = function (msg) { };
    }

    $.ajax({
        data: JSON.stringify(data),
        type: "POST",
        url: service + method,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (json) { successFunc.apply(me, [json.d, persistedValues]); },
        error: error,
        async: isAsync
    });
}

bas.utils.addScriptToPage = function (pUrl) {
    /// <summary>Add a js file include to the current page</summary>
    (function (d, url) {
        var p = d.getElementsByTagName('head')[0] || d.getElementsByTagName('script')[0];
        var s = d.createElement('script');
        s.src = url;
        p.appendChild(s);
    } (document, pUrl));
}

bas.utils.showModal = function (pUrl, pTitle, pAllowClose, pHeight, pWidth) {
    ///<summary>show a modal popup iframe for a given url</summary>
    ///<param name='pUrl'>string. url to the page that you want shown in the modal</param>
    ///<param name='pTitle'>string. optional. No HTML</param>
    ///<param name='pAllowClose'>bool. optional. show the modal close button?</param>
    ///<param name='pHeight'>int. optional. </param>
    ///<param name='pWidth'>int. optional. </param>
    ///<returns>nothing</returns>
    var pWidth = pWidth || 500;
    var pHeight = pHeight || 400;
    var pTitle = pTitle || '';
//  if (pAllowClose == 'undefined' || pAllowClose) {
//  if (pUrl.indexOf('?') > -1)
//      pUrl += '&modal=true';
//  else
//      pUrl += '?modal=true';
//  }
    $.fn.colorbox({ iframe: true, transition: 'none', title: pTitle, href: pUrl, innerWidth: pWidth, innerHeight: pHeight });
}

bas.utils.showModalMessage = function(pMessage, pTitle, pCss, pHeight, pWidth) {
    ///<summary>show a basic modal popup with the passed message</summary>
    ///<param name='pMessage'>string. can contain HTML</param>
    ///<param name='pTitle'>string. No HTML</param>
    ///<param name='pCss'>string. do not include style tags</param>
    ///<param name='pHeight'>int. optional. </param>
    ///<param name='pWidth'>int. optional. </param>
    ///<returns>nothing</returns>

    var pTitle = pTitle || '';
    var pCss = pCss || '';

    if (encodeURI) {
        pMessage = encodeURI(pMessage);
        pCss= encodeURI(pCss);
    } else {
        pMessage = escape(pMessage);
        pCss= escape(pCss);
    }

    var url = "/Modals/MessageBox.aspx?Message=" + pMessage+ "&css=" + pCss;
    bas.utils.showModal(url, pTitle, true, pHeight, pWidth);
}

bas.utils.showModalError = function(pMessage, pTitle, pCss, pHeight, pWidth) {
    ///<summary>show a basic error modal popup with the passed message</summary>
    ///<param name='pMessage'>string. can contain HTML</param>
    ///<param name='pTitle'>string. optional. No HTML</param>
    ///<param name='pCss'>string. optional. do not include style tags</param>
    ///<param name='pHeight'>int. optional. </param>
    ///<param name='pWidth'>int. optional. </param>
    ///<returns>nothing</returns>

    var pMessage = pMessage || "An unexpected error occurred";    
    pMessage = "<div class='error'>" + pMessage+ "</div>";
    var pCss = pCss || ".error {background-color:pink;font-weight:bold;border:solid 1px red;padding:5px;}";
    var pTitle = pTitle || "Error";

    bas.utils.showModalMessage(pMessage, pTitle, pCss, pHeight, pWidth);
}

bas.utils.getCurrencySymbol = function (pCurrencyCode) {
    /// <summary> Log the history for a search from an external system </summary>
    /// <param name='pCurrencyCode'>3 character string. ex: 'USD', 'GBP' see: http://en.wikipedia.org/wiki/ISO_4217</param>

    if (!pCurrencyCode)
       return '$';
    var code = pCurrencyCode.toString().toUpperCase();
    if (code === 'USD')
       return '$';
    if (code === 'GBP' || code === 'UKP')
       return '£';
    if (code === 'EUR')
       return '€';
    if (code === 'ITL')
       return '£';
    if (code === 'JPY')
       return '¥';
    return '$';
}
bas.search.logExternalSearch = function (pTerms, pMatchCount, pSystem) {
    /// <summary> Log the history for a search from an external system </summary>
    /// <param name='pTerms'>string. The original search terms entered by the user</param>
    /// <param name='pMatchCount'>int. Number of matches or results returned</param>
    /// <param name='pSystem'>string. max chars: 15. Short name of external system</param>
    /// <returns> bool. True/False if the search was logged successfully </returns>

    var params = {};
    params.originalTerms = pTerms.toString();
    params.matchCount = parseInt((pMatchCount || 0), 10);
    params.system = pSystem.toString();
    params.storeId = document.siteObject ? document.siteObject.storeID : 1;
    var result = bas.utils.callPageService('LogExternalSearch', params);
    return result;
}

bas.utils.getCsv = function (pArrayOfJsObjects, pFilename, callBack) {
    ///<summary>Given an array of (same) javascript objects, download a csv file - useful for grids</summary>

    var postdata = {};
    postdata.filename = pFilename || 'YeahBuddy.csv';
    postdata.data = JSON.stringify(pArrayOfJsObjects);
    var uid = $.ajax("/csv.ashx", { async: false, data: postdata, type: 'POST', success: callBack, error: callBack  }).responseText;
    if (uid.toString().length > 50) {
        console.log('ERROR: bas.utils.getCsv', uid);
        return;
    }
    var url = '/csv.ashx?&download=' + uid;
    location.href = url;
}

bas.theme.setUserThemeItem = function (pKey, pValue) {
    var data = { key: pKey, value: pValue };

    $.ajax({
        async: false,
        type: "POST",
        url: '/WebServices/PageServices.asmx/SetUserThemeItem',
        data: JSON.stringify(data),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (result) { return true; },
        error: function (result) { alert('SetUserThemeItem Error: ' + result.Error); return false; }
    });
};
bas.theme.setUserThemeName = function (value) {
    bas.theme.setUserThemeItem('name', value);
};

jQuery.cookie = function (name, value, options) {
    if (typeof value != 'undefined') { // name and value given, set cookie
        options = options || {};
        if (value === null) {
            value = '';
            options.expires = -1;
        }
        var expires = '';
        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
            var date;
            if (typeof options.expires == 'number') {
                date = new Date();
                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
            } else {
                date = options.expires;
            }
            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
        }
        // CAUTION: Needed to parenthesize options.path and options.domain
        // in the following expressions, otherwise they evaluate to undefined
        // in the packed version for some reason...
        var path = options.path ? '; path=' + (options.path) : '';
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    } else { // only name given, get cookie
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
};


//////////
///
///header cookie read
///
//////////
function createCookie(name, value) {
    var date = new Date();
    date.setTime(date.getTime() + (768960000000));
    var expires = '; expires=' + date.toGMTString();
    document.cookie = name + '=' + value + expires + '; path=/';
}
function readCookie(name) {
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var keyValuePair = ca[i].split('=');
        if (keyValuePair[0].replace(' ', '') == name)
            return keyValuePair[1];
    }
    return null;
}



function tb_init() {
    // $.fn.colorbox.init();
}

function tb_remove() {
    $.fn.colorbox.close();
}


//from /script/new/utilities/common.js?v=38497931416B47385A41593D
var BuildASign = BuildASign || {};
BuildASign.Utilities = BuildASign.Utilities || {};

BuildASign.Utilities.ToMoney = function (amount) {
    if (typeof amount != 'number') {
        amount = jQuery.global.parseFloat(amount);
    }
    return jQuery.global.format(amount, "c2");
};

BuildASign.Utilities.GetShortDateString = function (jsonDate) {

    jsonDate = jsonDate || (new Date());
    var date;
    try {
        if (typeof (jsonDate) == "string")
            date = BuildASign.Utilities.GetShortDateFromJsonString(jsonDate);
        else
            date = jsonDate; //already a date obj
    } catch (err) {
        return '';
    }

    var result = '';

    var month = date.getMonth() + 1;
    var day = date.getDate();
    var year = date.getFullYear();

    result = month + "/" + day + "/" + year;

    var hours = date.getHours();
    if (hours > 12)
        hours -= 12;
    var minutes = date.getMinutes();
    if (minutes < 10) {
        minutes = "0" + minutes;
    }
    result += " " + hours + ":" + minutes + " ";
    if (date.getHours() > 11) {
        result += "PM";
    } else {
        result += "AM";
    }

    return result;
};

BuildASign.Utilities.GetShortDateFromJsonString = function (jsonDate) {
    return new Date(parseInt(/\/Date\((\d+).*/.exec(jsonDate)[1]));
};

//make it so when pressing enter while in a text field the specified button gets pressed
BuildASign.Utilities.LinkTextFieldToButton = function (textField, button) {
    textField.keypress(function (e) {
        if (e.keyCode === 13) {
            button.click();
            e.preventDefault();
            e.stopPropagation();
        }

    })
};

BuildASign.Utilities.Trace = function (msg, obj) {
    if (window.console && window.console.debug) {
        window.console.debug(msg, obj);
    } else if (window.console && window.console.log) {
        window.console.log(msg, obj);
    }
};

BuildASign.Utilities.QueryStringCollection = function () {

    var location = window.location.search;
    var result = {};

    var keyValues = location.replace('?', '').split('&');
    for (var i = 0; i < keyValues.length; i++) {
        var keyValueItem = keyValues[i].split('=');
        result[keyValueItem[0]] = keyValueItem[1];
    }

    //set this to the result dictionary so that all subsequent calls get the dictionary without having to recalculate everything
    BuildASign.Utilities.QueryStringCollection = function () { return result };
    return result;
};

BuildASign.Utilities.ReplaceQueryStringValue = function (location, key, newValue) {

    var compareLocation = location.toLowerCase();
    var compareKey = key.toLowerCase();
    var queryStringStart = location.indexOf('?');
    if (queryStringStart < 0) {
        queryStringStart = location.length;
    }

    var keyIndex = compareLocation.indexOf(compareKey, queryStringStart);
    if (keyIndex > 0) {
        var valueStart = compareLocation.indexOf('=', keyIndex) + 1;
        if (valueStart <= 0) {
            //must be malformed location, add in a value
            location += "=val";
            compareLocation = location.toLowerCase();
            valueStart = keyIndex + compareKey.length + 1;
        }

        var valueEnd = compareLocation.indexOf('&', valueStart);
        if (valueEnd < 0) {
            valueEnd = location.length;
        }

        location = location.substring(0, valueStart) + newValue + location.substring(valueEnd, location.length);
    } else {
        if (queryStringStart != location.length) {
            location = location + "&" + key + "=" + newValue;
        } else {
            location = location + "?" + key + "=" + newValue;
        }
    }

    return location;
};
































// ColorBox v1.3.16 - a full featured, light-weight, customizable lightbox based on jQuery 1.3+
// Copyright (c) 2011 Jack Moore - jack@colorpowered.com
// Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
(function ($, document, window) {
    var 
    // ColorBox Default Settings.	
    // See http://colorpowered.com/colorbox for details.
	defaults = {
	    transition: "elastic",
	    speed: 300,
	    width: false,
	    initialWidth: "600",
	    innerWidth: false,
	    maxWidth: false,
	    height: false,
	    initialHeight: "450",
	    innerHeight: false,
	    maxHeight: false,
	    scalePhotos: true,
	    scrolling: true,
	    inline: false,
	    html: false,
	    iframe: false,
	    fastIframe: true,
	    photo: false,
	    href: false,
	    title: false,
	    rel: false,
	    opacity: 0.9,
	    preloading: true,
	    current: "image {current} of {total}",
	    previous: "previous",
	    next: "next",
	    close: "close",
	    open: false,
	    returnFocus: true,
	    loop: true,
	    slideshow: false,
	    slideshowAuto: true,
	    slideshowSpeed: 2500,
	    slideshowStart: "start slideshow",
	    slideshowStop: "stop slideshow",
	    onOpen: false,
	    onLoad: false,
	    onComplete: false,
	    onCleanup: false,
	    onClosed: false,
	    overlayClose: true,
	    escKey: true,
	    arrowKey: true
	},

    // Abstracting the HTML and event identifiers for easy rebranding
	colorbox = 'colorbox',
	prefix = 'cbox',

    // Events	
	event_open = prefix + '_open',
	event_load = prefix + '_load',
	event_complete = prefix + '_complete',
	event_cleanup = prefix + '_cleanup',
	event_closed = prefix + '_closed',
	event_purge = prefix + '_purge',

    // Special Handling for IE
	isIE = $.browser.msie && !$.support.opacity, // feature detection alone gave a false positive on at least one phone browser and on some development versions of Chrome.
	isIE6 = isIE && $.browser.version < 7,
	event_ie6 = prefix + '_IE6',

    // Cached jQuery Object Variables
	$overlay,
	$box,
	$wrap,
	$content,
	$topBorder,
	$leftBorder,
	$rightBorder,
	$bottomBorder,
	$related,
	$window,
	$loaded,
	$loadingBay,
	$loadingOverlay,
	$title,
	$current,
	$slideshow,
	$next,
	$prev,
	$close,
	$groupControls,

    // Variables for cached values or use across multiple functions
	settings = {},
	interfaceHeight,
	interfaceWidth,
	loadedHeight,
	loadedWidth,
	element,
	index,
	photo,
	open,
	active,
	closing = false,

	publicMethod,
	boxElement = prefix + 'Element';

    // ****************
    // HELPER FUNCTIONS
    // ****************

    // jQuery object generator to reduce code size
    function $div(id, cssText) {
        var div = document.createElement('div');
        div.id = id ? prefix + id : false;
        div.style.cssText = cssText || false;
        return $(div);
    }

    // Convert % values to pixels
    function setSize(size, dimension) {
        dimension = dimension === 'x' ? $window.width() : $window.height();
        return (typeof size === 'string') ? Math.round((/%/.test(size) ? (dimension / 100) * parseInt(size, 10) : parseInt(size, 10))) : size;
    }

    // Checks an href to see if it is a photo.
    // There is a force photo option (photo: true) for hrefs that cannot be matched by this regex.
    function isImage(url) {
        return settings.photo || /\.(gif|png|jpg|jpeg|bmp)(?:\?([^#]*))?(?:#(\.*))?$/i.test(url);
    }

    // Assigns function results to their respective settings.  This allows functions to be used as values.
    function process(settings) {
        for (var i in settings) {
            if ($.isFunction(settings[i]) && i.substring(0, 2) !== 'on') { // checks to make sure the function isn't one of the callbacks, they will be handled at the appropriate time.
                settings[i] = settings[i].call(element);
            }
        }
        settings.rel = settings.rel || element.rel || 'nofollow';
        settings.href = $.trim(settings.href || $(element).attr('href'));
        settings.title = settings.title || element.title;
    }

    function trigger(event, callback) {
        if (callback) {
            callback.call(element);
        }
        $.event.trigger(event);
    }

    // Slideshow functionality
    function slideshow() {
        var 
		timeOut,
		className = prefix + "Slideshow_",
		click = "click." + prefix,
		start,
		stop,
		clear;

        if (settings.slideshow && $related[1]) {
            start = function () {
                $slideshow
					.text(settings.slideshowStop)
					.unbind(click)
					.bind(event_complete, function () {
					    if (index < $related.length - 1 || settings.loop) {
					        timeOut = setTimeout(publicMethod.next, settings.slideshowSpeed);
					    }
					})
					.bind(event_load, function () {
					    clearTimeout(timeOut);
					})
					.one(click + ' ' + event_cleanup, stop);
                $box.removeClass(className + "off").addClass(className + "on");
                timeOut = setTimeout(publicMethod.next, settings.slideshowSpeed);
            };

            stop = function () {
                clearTimeout(timeOut);
                $slideshow
					.text(settings.slideshowStart)
					.unbind([event_complete, event_load, event_cleanup, click].join(' '))
					.one(click, start);
                $box.removeClass(className + "on").addClass(className + "off");
            };

            if (settings.slideshowAuto) {
                start();
            } else {
                stop();
            }
        }
    }

    function launch(elem) {
        if (!closing) {

            element = elem;

            process($.extend(settings, $.data(element, colorbox)));

            $related = $(element);

            index = 0;

            if (settings.rel !== 'nofollow') {
                $related = $('.' + boxElement).filter(function () {
                    var relRelated = $.data(this, colorbox).rel || this.rel;
                    return (relRelated === settings.rel);
                });
                index = $related.index(element);

                // Check direct calls to ColorBox.
                if (index === -1) {
                    $related = $related.add(element);
                    index = $related.length - 1;
                }
            }

            if (!open) {
                open = active = true; // Prevents the page-change action from queuing up if the visitor holds down the left or right keys.

                $box.show();

                if (settings.returnFocus) {
                    try {
                        element.blur();
                        $(element).one(event_closed, function () {
                            try {
                                this.focus();
                            } catch (e) {
                                // do nothing
                            }
                        });
                    } catch (e) {
                        // do nothing
                    }
                }

                // +settings.opacity avoids a problem in IE when using non-zero-prefixed-string-values, like '.5'
                $overlay.css({ "opacity": +settings.opacity, "cursor": settings.overlayClose ? "pointer" : "auto" }).show();

                // Opens inital empty ColorBox prior to content being loaded.
                settings.w = setSize(settings.initialWidth, 'x');
                settings.h = setSize(settings.initialHeight, 'y');
                publicMethod.position(0);

                if (isIE6) {
                    $window.bind('resize.' + event_ie6 + ' scroll.' + event_ie6, function () {
                        $overlay.css({ width: $window.width(), height: $window.height(), top: $window.scrollTop(), left: $window.scrollLeft() });
                    }).trigger('resize.' + event_ie6);
                }

                trigger(event_open, settings.onOpen);

                $groupControls.add($title).hide();

                $close.html(settings.close).show();
            }

            publicMethod.load(true);
        }
    }

    // ****************
    // PUBLIC FUNCTIONS
    // Usage format: $.fn.colorbox.close();
    // Usage from within an iframe: parent.$.fn.colorbox.close();
    // ****************

    publicMethod = $.fn[colorbox] = $[colorbox] = function (options, callback) {
        var $this = this, autoOpen;

        if (!$this[0] && $this.selector) { // if a selector was given and it didn't match any elements, go ahead and exit.
            return $this;
        }
        options.innerWidth = options.innerWidth ? options.innerWidth + 20 : options.innerWidth;

        options = options || {};

        if (callback) {
            options.onComplete = callback;
        }

        if (!$this[0] || $this.selector === undefined) { // detects $.colorbox() and $.fn.colorbox()
            $this = $('<a/>');
            options.open = true; // assume an immediate open
        }

        $this.each(function () {
            $.data(this, colorbox, $.extend({}, $.data(this, colorbox) || defaults, options));
            $(this).addClass(boxElement);
        });

        autoOpen = options.open;

        if ($.isFunction(autoOpen)) {
            autoOpen = autoOpen.call($this);
        }

        if (autoOpen) {
            launch($this[0]);
        }

        return $this;
    };

    // Initialize ColorBox: store common calculations, preload the interface graphics, append the html.
    // This preps colorbox for a speedy open when clicked, and lightens the burdon on the browser by only
    // having to run once, instead of each time colorbox is opened.
    publicMethod.init = function () {
        // Create & Append jQuery Objects
        $window = $(window);
        $box = $div().attr({ id: colorbox, 'class': isIE ? prefix + (isIE6 ? 'IE6' : 'IE') : '' });
        $overlay = $div("Overlay", isIE6 ? 'position:absolute' : '').hide();

        $wrap = $div("Wrapper");
        $content = $div("Content").append(
			$loaded = $div("LoadedContent", 'width:0; height:0; overflow:hidden'),
			$loadingOverlay = $div("LoadingOverlay").add($div("LoadingGraphic")),
			$title = $div("Title"),
			$current = $div("Current"),
			$next = $div("Next"),
			$prev = $div("Previous"),
			$slideshow = $div("Slideshow").bind(event_open, slideshow),
			$close = $div("Close")
		);
        $wrap.append( // The 3x3 Grid that makes up ColorBox
			$div().append(
				$div("TopLeft"),
				$topBorder = $div("TopCenter"),
				$div("TopRight")
			),
			$div(false, 'clear:left').append(
				$leftBorder = $div("MiddleLeft"),
				$content,
				$rightBorder = $div("MiddleRight")
			),
			$div(false, 'clear:left').append(
				$div("BottomLeft"),
				$bottomBorder = $div("BottomCenter"),
				$div("BottomRight")
			)
		).children().children().css({ 'float': 'left' });

        $loadingBay = $div(false, 'position:absolute; width:9999px; visibility:hidden; display:none');

        $('body').prepend($overlay, $box.append($wrap, $loadingBay));

        $content.children()
		.hover(function () {
		    $(this).addClass('hover');
		}, function () {
		    $(this).removeClass('hover');
		}).addClass('hover');

        // Cache values needed for size calculations
        interfaceHeight = $topBorder.height() + $bottomBorder.height() + $content.outerHeight(true) - $content.height(); //Subtraction needed for IE6
        interfaceWidth = $leftBorder.width() + $rightBorder.width() + $content.outerWidth(true) - $content.width();
        loadedHeight = $loaded.outerHeight(true);
        loadedWidth = $loaded.outerWidth(true);

        // Setting padding to remove the need to do size conversions during the animation step.
        $box.css({ "padding-bottom": interfaceHeight, "padding-right": interfaceWidth }).hide();

        // Setup button events.
        $next.click(function () {
            publicMethod.next();
        });
        $prev.click(function () {
            publicMethod.prev();
        });
        $close.click(function () {
            publicMethod.close();
        });

        $groupControls = $next.add($prev).add($current).add($slideshow);

        // Adding the 'hover' class allowed the browser to load the hover-state
        // background graphics.  The class can now can be removed.
        $content.children().removeClass('hover');

        $('.' + boxElement).live('click', function (e) {
            // checks to see if it was a non-left mouse-click and for clicks modified with ctrl, shift, or alt.
            if (!((e.button !== 0 && typeof e.button !== 'undefined') || e.ctrlKey || e.shiftKey || e.altKey)) {
                e.preventDefault();
                launch(this);
            }
        });

        $overlay.click(function () {
            if (settings.overlayClose) {
                publicMethod.close();
            }
        });

        // Set Navigation Key Bindings
        $(document).bind("keydown", function (e) {
            if (open && settings.escKey && e.keyCode === 27) {
                e.preventDefault();
                publicMethod.close();
            }
            if (open && settings.arrowKey && !active && $related[1]) {
                if (e.keyCode === 37 && (index || settings.loop)) {
                    e.preventDefault();
                    $prev.click();
                } else if (e.keyCode === 39 && (index < $related.length - 1 || settings.loop)) {
                    e.preventDefault();
                    $next.click();
                }
            }
        });
    };

    publicMethod.remove = function () {
        $box.add($overlay).remove();
        $('.' + boxElement).die('click').removeData(colorbox).removeClass(boxElement);
    };

    publicMethod.position = function (speed, loadedCallback) {
        var 
		animate_speed,
        // keeps the top and left positions within the browser's viewport.
		posTop = Math.max(document.documentElement.clientHeight - settings.h - loadedHeight - interfaceHeight, 0) / 2 + $window.scrollTop(),
		posLeft = Math.max($window.width() - settings.w - loadedWidth - interfaceWidth, 0) / 2 + $window.scrollLeft();

        // setting the speed to 0 to reduce the delay between same-sized content.
        animate_speed = ($box.width() === settings.w + loadedWidth && $box.height() === settings.h + loadedHeight) ? 0 : speed;

        // this gives the wrapper plenty of breathing room so it's floated contents can move around smoothly,
        // but it has to be shrank down around the size of div#colorbox when it's done.  If not,
        // it can invoke an obscure IE bug when using iframes.
        $wrap[0].style.width = $wrap[0].style.height = "9999px";

        function modalDimensions(that) {
            // loading overlay height has to be explicitly set for IE6.
            $topBorder[0].style.width = $bottomBorder[0].style.width = $content[0].style.width = that.style.width;
            $loadingOverlay[0].style.height = $loadingOverlay[1].style.height = $content[0].style.height = $leftBorder[0].style.height = $rightBorder[0].style.height = that.style.height;
        }

        $box.dequeue().animate({ width: settings.w + loadedWidth, height: settings.h + loadedHeight, top: posTop, left: posLeft }, {
            duration: animate_speed,
            complete: function () {
                modalDimensions(this);

                active = false;

                // shrink the wrapper down to exactly the size of colorbox to avoid a bug in IE's iframe implementation.
                $wrap[0].style.width = (settings.w + loadedWidth + interfaceWidth) + "px";
                $wrap[0].style.height = (settings.h + loadedHeight + interfaceHeight) + "px";

                if (loadedCallback) {
                    loadedCallback();
                }
            },
            step: function () {
                modalDimensions(this);
            }
        });
    };

    publicMethod.resize = function (options) {
        if (open) {
            options = options || {};

            if (options.width) {
                settings.w = setSize(options.width, 'x') - loadedWidth - interfaceWidth;
            }
            if (options.innerWidth) {
                settings.w = setSize(options.innerWidth, 'x');
            }
            $loaded.css({ width: settings.w });

            if (options.height) {
                settings.h = setSize(options.height, 'y') - loadedHeight - interfaceHeight;
            }
            if (options.innerHeight) {
                settings.h = setSize(options.innerHeight, 'y');
            }
            if (!options.innerHeight && !options.height) {
                var $child = $loaded.wrapInner("<div style='overflow:auto'></div>").children(); // temporary wrapper to get an accurate estimate of just how high the total content should be.
                settings.h = $child.height();
                $child.replaceWith($child.children()); // ditch the temporary wrapper div used in height calculation
            }
            $loaded.css({ height: settings.h });

            publicMethod.position(settings.transition === "none" ? 0 : settings.speed);
        }
    };

    publicMethod.prep = function (object) {
        if (!open) {
            return;
        }

        var speed = settings.transition === "none" ? 0 : settings.speed;

        $window.unbind('resize.' + prefix);
        $loaded.remove();
        $loaded = $div('LoadedContent').html(object);

        function getWidth() {
            settings.w = settings.w || $loaded.width();
            settings.w = settings.mw && settings.mw < settings.w ? settings.mw : settings.w;
            return settings.w;
        }
        function getHeight() {
            settings.h = settings.h || $loaded.height();
            settings.h = settings.mh && settings.mh < settings.h ? settings.mh : settings.h;
            return settings.h;
        }

        $loaded.hide()
		.appendTo($loadingBay.show())// content has to be appended to the DOM for accurate size calculations.
		.css({ width: getWidth(), overflow: settings.scrolling ? 'auto' : 'hidden' })
		.css({ height: getHeight() })// sets the height independently from the width in case the new width influences the value of height.
		.prependTo($content);

        $loadingBay.hide();

        // floating the IMG removes the bottom line-height and fixed a problem where IE miscalculates the width of the parent element as 100% of the document width.
        //$(photo).css({'float': 'none', marginLeft: 'auto', marginRight: 'auto'});

        $(photo).css({ 'float': 'none' });

        // Hides SELECT elements in IE6 because they would otherwise sit on top of the overlay.
        if (isIE6) {
            $('select').not($box.find('select')).filter(function () {
                return this.style.visibility !== 'hidden';
            }).css({ 'visibility': 'hidden' }).one(event_cleanup, function () {
                this.style.visibility = 'inherit';
            });
        }

        function setPosition(s) {
            publicMethod.position(s, function () {
                var prev, prevSrc, next, nextSrc, total = $related.length, iframe, complete;

                if (!open) {
                    return;
                }

                complete = function () {
                    $loadingOverlay.hide();
                    trigger(event_complete, settings.onComplete);
                };

                if (isIE) {
                    //This fadeIn helps the bicubic resampling to kick-in.
                    if (photo) {
                        $loaded.fadeIn(100);
                    }
                }

                $title.html(settings.title).add($loaded).show();

                if (total > 1) { // handle grouping
                    if (typeof settings.current === "string") {
                        $current.html(settings.current.replace(/\{current\}/, index + 1).replace(/\{total\}/, total)).show();
                    }

                    $next[(settings.loop || index < total - 1) ? "show" : "hide"]().html(settings.next);
                    $prev[(settings.loop || index) ? "show" : "hide"]().html(settings.previous);

                    prev = index ? $related[index - 1] : $related[total - 1];
                    next = index < total - 1 ? $related[index + 1] : $related[0];

                    if (settings.slideshow) {
                        $slideshow.show();
                    }

                    // Preloads images within a rel group
                    if (settings.preloading) {
                        nextSrc = $.data(next, colorbox).href || next.href;
                        prevSrc = $.data(prev, colorbox).href || prev.href;

                        nextSrc = $.isFunction(nextSrc) ? nextSrc.call(next) : nextSrc;
                        prevSrc = $.isFunction(prevSrc) ? prevSrc.call(prev) : prevSrc;

                        if (isImage(nextSrc)) {
                            $('<img/>')[0].src = nextSrc;
                        }

                        if (isImage(prevSrc)) {
                            $('<img/>')[0].src = prevSrc;
                        }
                    }
                } else {
                    $groupControls.hide();
                }

                if (settings.iframe) {
                    iframe = $('<iframe frameborder=0/>').addClass(prefix + 'Iframe')[0];

                    if (settings.fastIframe) {
                        complete();
                    } else {
                        $(iframe).load(complete);
                    }
                    iframe.name = prefix + (+new Date());
                    iframe.src = settings.href;

                    if (!settings.scrolling) {
                        iframe.scrolling = "no";
                    }

                    if (isIE) {
                        iframe.allowTransparency = "true";
                    }

                    $(iframe).appendTo($loaded).one(event_purge, function () {
                        iframe.src = "//about:blank";
                    });
                } else {
                    complete();
                }

                if (settings.transition === 'fade') {
                    $box.fadeTo(speed, 1, function () {
                        $box[0].style.filter = "";
                    });
                } else {
                    $box[0].style.filter = "";
                }

                $window.bind('resize.' + prefix, function () {
                    publicMethod.position(0);
                });
            });
        }

        if (settings.transition === 'fade') {
            $box.fadeTo(speed, 0, function () {
                setPosition(0);
            });
        } else {
            setPosition(speed);
        }
    };

    publicMethod.load = function (launched) {
        var href, setResize, prep = publicMethod.prep;

        active = true;

        photo = false;

        element = $related[index];

        if (!launched) {
            process($.extend(settings, $.data(element, colorbox)));
        }

        trigger(event_purge);

        trigger(event_load, settings.onLoad);

        settings.h = settings.height ?
				setSize(settings.height, 'y') - loadedHeight - interfaceHeight :
				settings.innerHeight && setSize(settings.innerHeight, 'y');

        settings.w = settings.width ?
				setSize(settings.width, 'x') - loadedWidth - interfaceWidth :
				settings.innerWidth && setSize(settings.innerWidth, 'x');

        // Sets the minimum dimensions for use in image scaling
        settings.mw = settings.w;
        settings.mh = settings.h;

        // Re-evaluate the minimum width and height based on maxWidth and maxHeight values.
        // If the width or height exceed the maxWidth or maxHeight, use the maximum values instead.
        if (settings.maxWidth) {
            settings.mw = setSize(settings.maxWidth, 'x') - loadedWidth - interfaceWidth;
            settings.mw = settings.w && settings.w < settings.mw ? settings.w : settings.mw;
        }
        if (settings.maxHeight) {
            settings.mh = setSize(settings.maxHeight, 'y') - loadedHeight - interfaceHeight;
            settings.mh = settings.h && settings.h < settings.mh ? settings.h : settings.mh;
        }

        href = settings.href;

        $loadingOverlay.show();

        if (settings.inline) {
            // Inserts an empty placeholder where inline content is being pulled from.
            // An event is bound to put inline content back when ColorBox closes or loads new content.
            $div().hide().insertBefore($(href)[0]).one(event_purge, function () {
                $(this).replaceWith($loaded.children());
            });
            prep($(href));
        } else if (settings.iframe) {
            // IFrame element won't be added to the DOM until it is ready to be displayed,
            // to avoid problems with DOM-ready JS that might be trying to run in that iframe.
            prep(" ");
        } else if (settings.html) {
            prep(settings.html);
        } else if (isImage(href)) {
            $(photo = new Image())
			.addClass(prefix + 'Photo')
			.error(function () {
			    settings.title = false;
			    prep($div('Error').text('This image could not be loaded'));
			})
			.load(function () {
			    var percent;
			    photo.onload = null; //stops animated gifs from firing the onload repeatedly.

			    if (settings.scalePhotos) {
			        setResize = function () {
			            photo.height -= photo.height * percent;
			            photo.width -= photo.width * percent;
			        };
			        if (settings.mw && photo.width > settings.mw) {
			            percent = (photo.width - settings.mw) / photo.width;
			            setResize();
			        }
			        if (settings.mh && photo.height > settings.mh) {
			            percent = (photo.height - settings.mh) / photo.height;
			            setResize();
			        }
			    }

			    if (settings.h) {
			        photo.style.marginTop = Math.max(settings.h - photo.height, 0) / 2 + 'px';
			    }

			    if ($related[1] && (index < $related.length - 1 || settings.loop)) {
			        photo.style.cursor = 'pointer';
			        photo.onclick = function () {
			            publicMethod.next();
			        };
			    }

			    if (isIE) {
			        photo.style.msInterpolationMode = 'bicubic';
			    }

			    setTimeout(function () { // A pause because Chrome will sometimes report a 0 by 0 size otherwise.
			        prep(photo);
			    }, 1);
			});

            setTimeout(function () { // A pause because Opera 10.6+ will sometimes not run the onload function otherwise.
                photo.src = href;
            }, 1);
        } else if (href) {
            $loadingBay.load(href, function (data, status, xhr) {
                prep(status === 'error' ? $div('Error').text('Request unsuccessful: ' + xhr.statusText) : $(this).contents());
            });
        }
    };

    // Navigates to the next page/image in a set.
    publicMethod.next = function () {
        if (!active) {
            index = index < $related.length - 1 ? index + 1 : 0;
            publicMethod.load();
        }
    };

    publicMethod.prev = function () {
        if (!active) {
            index = index ? index - 1 : $related.length - 1;
            publicMethod.load();
        }
    };

    // Note: to use this within an iframe use the following format: parent.$.fn.colorbox.close();
    publicMethod.close = function () {
        if (open && !closing) {

            closing = true;

            open = false;

            trigger(event_cleanup, settings.onCleanup);

            $window.unbind('.' + prefix + ' .' + event_ie6);

            $overlay.fadeTo(200, 0);

            $box.stop().fadeTo(300, 0, function () {

                $box.add($overlay).css({ 'opacity': 1, cursor: 'auto' }).hide();

                trigger(event_purge);
                try {
                    $loaded.remove();
                } catch (Err) {
                    BuildASign.Utilities.Trace('colorbox.close() error', Err);
                }
                setTimeout(function () {
                    closing = false;
                    trigger(event_closed, settings.onClosed);
                }, 1);
            });
        }
    };

    // A method for fetching the current element ColorBox is referencing.
    // returns a jQuery object.
    publicMethod.element = function () {
        return $(element);
    };

    publicMethod.settings = defaults;

    // Initializes ColorBox when the DOM has loaded
    $(publicMethod.init);

} (jQuery, document, this));

/*!
* jQuery Globalization Plugin v1.0.0pre

* http://github.com/jquery/jquery-global
*
* Talk to Travis if you need to add another locale.
*
* Copyright Software Freedom Conservancy, Inc.
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
(function () {

    var Globalization = {}, localized = { en: {} };
    localized["default"] = localized.en;

    Globalization.extend = function (deep) {
        var target = arguments[1] || {};
        for (var i = 2, l = arguments.length; i < l; i++) {
            var source = arguments[i];
            if (source) {
                for (var field in source) {
                    var sourceVal = source[field];
                    if (typeof sourceVal !== "undefined") {
                        if (deep && (isObject(sourceVal) || isArray(sourceVal))) {
                            var targetVal = target[field];
                            // extend onto the existing value, or create a new one
                            targetVal = targetVal && (isObject(targetVal) || isArray(targetVal))
                            ? targetVal
                            : (isArray(sourceVal) ? [] : {});
                            target[field] = this.extend(true, targetVal, sourceVal);
                        }
                        else {
                            target[field] = sourceVal;
                        }
                    }
                }
            }
        }
        return target;
    }

    Globalization.findClosestCulture = function (name) {
        var match;
        if (!name) {
            return this.culture || this.cultures["default"];
        }
        if (isString(name)) {
            name = name.split(',');
        }
        if (isArray(name)) {
            var lang,
            cultures = this.cultures,
            list = name,
            i, l = list.length,
            prioritized = [];
            for (i = 0; i < l; i++) {
                name = trim(list[i]);
                var pri, parts = name.split(';');
                lang = trim(parts[0]);
                if (parts.length === 1) {
                    pri = 1;
                }
                else {
                    name = trim(parts[1]);
                    if (name.indexOf("q=") === 0) {
                        name = name.substr(2);
                        pri = parseFloat(name, 10);
                        pri = isNaN(pri) ? 0 : pri;
                    }
                    else {
                        pri = 1;
                    }
                }
                prioritized.push({ lang: lang, pri: pri });
            }
            prioritized.sort(function (a, b) {
                return a.pri < b.pri ? 1 : -1;
            });

            // exact match
            for (i = 0; i < l; i++) {
                lang = prioritized[i].lang;
                match = cultures[lang];
                if (match) {
                    return match;
                }
            }

            // neutral language match
            for (i = 0; i < l; i++) {
                lang = prioritized[i].lang;
                do {
                    var index = lang.lastIndexOf("-");
                    if (index === -1) {
                        break;
                    }
                    // strip off the last part. e.g. en-US => en
                    lang = lang.substr(0, index);
                    match = cultures[lang];
                    if (match) {
                        return match;
                    }
                }
                while (1);
            }

            // last resort: match first culture using that language
            for (i = 0; i < l; i++) {
                lang = prioritized[i].lang;
                for (var cultureKey in cultures) {
                    var culture = cultures[cultureKey];
                    if (culture.language == lang) {
                        return culture;
                    }
                }
            }
        }
        else if (typeof name === 'object') {
            return name;
        }
        return match || null;
    }
    Globalization.preferCulture = function (name) {
        this.culture = this.findClosestCulture(name) || this.cultures["default"];
    }
    Globalization.localize = function (key, culture, value) {
        // usign default culture in case culture is not provided
        if (typeof culture !== 'string') {
            culture = this.culture.name || this.culture || "default";
        }
        culture = this.cultures[culture] || { name: culture };

        var local = localized[culture.name];
        if (arguments.length === 3) {
            if (!local) {
                local = localized[culture.name] = {};
            }
            local[key] = value;
        }
        else {
            if (local) {
                value = local[key];
            }
            if (typeof value === 'undefined') {
                var language = localized[culture.language];
                if (language) {
                    value = language[key];
                }
                if (typeof value === 'undefined') {
                    value = localized["default"][key];
                }
            }
        }
        return typeof value === "undefined" ? null : value;
    }
    Globalization.format = function (value, format, culture) {
        culture = this.findClosestCulture(culture);
        if (typeof value === "number") {
            value = formatNumber(value, format, culture);
        }
        else if (value instanceof Date) {
            value = formatDate(value, format, culture);
        }
        return value;
    }
    Globalization.parseInt = function (value, radix, culture) {
        return Math.floor(this.parseFloat(value, radix, culture));
    }
    Globalization.parseFloat = function (value, radix, culture) {
        // make radix and culture optional
        if (typeof radix === "undefined" && typeof culture === "undefined") {
            culture = this.culture;
            radix = 10;
        }

        // make radix optional
        if (typeof radix === "string") {
            culture = radix;
            radix = 10;
        }

        culture = this.findClosestCulture(culture);
        var ret = NaN,
        nf = culture.numberFormat;

        if (value.indexOf(culture.numberFormat.currency.symbol) > -1) {
            // remove currency symbol
            value = value.replace(culture.numberFormat.currency.symbol, "");
            // replace decimal seperator
            value = value.replace(culture.numberFormat.currency["."], culture.numberFormat["."]);
        }

        // trim leading and trailing whitespace
        value = trim(value);

        // allow infinity or hexidecimal
        if (regexInfinity.test(value)) {
            ret = parseFloat(value, radix);
        }
        else if (!radix && regexHex.test(value)) {
            ret = parseInt(value, 16);
        }
        else {
            var signInfo = parseNegativePattern(value, nf, nf.pattern[0]),
            sign = signInfo[0],
            num = signInfo[1];
            // determine sign and number
            if (sign === "" && nf.pattern[0] !== "-n") {
                signInfo = parseNegativePattern(value, nf, "-n");
                sign = signInfo[0];
                num = signInfo[1];
            }
            sign = sign || "+";
            // determine exponent and number
            var exponent,
            intAndFraction,
            exponentPos = num.indexOf('e');
            if (exponentPos < 0) exponentPos = num.indexOf('E');
            if (exponentPos < 0) {
                intAndFraction = num;
                exponent = null;
            }
            else {
                intAndFraction = num.substr(0, exponentPos);
                exponent = num.substr(exponentPos + 1);
            }
            // determine decimal position
            var integer,
            fraction,
            decSep = nf['.'],
            decimalPos = intAndFraction.indexOf(decSep);
            if (decimalPos < 0) {
                integer = intAndFraction;
                fraction = null;
            }
            else {
                integer = intAndFraction.substr(0, decimalPos);
                fraction = intAndFraction.substr(decimalPos + decSep.length);
            }
            // handle groups (e.g. 1,000,000)
            var groupSep = nf[","];
            integer = integer.split(groupSep).join('');
            var altGroupSep = groupSep.replace(/\u00A0/g, " ");
            if (groupSep !== altGroupSep) {
                integer = integer.split(altGroupSep).join('');
            }
            // build a natively parsable number string
            var p = sign + integer;
            if (fraction !== null) {
                p += '.' + fraction;
            }
            if (exponent !== null) {
                // exponent itself may have a number patternd
                var expSignInfo = parseNegativePattern(exponent, nf, "-n");
                p += 'e' + (expSignInfo[0] || "+") + expSignInfo[1];
            }
            if (regexParseFloat.test(p)) {
                ret = parseFloat(p);
            }
        }
        return ret;
    }
    Globalization.parseDate = function (value, formats, culture) {
        culture = this.findClosestCulture(culture);

        var date, prop, patterns;
        if (formats) {
            if (typeof formats === "string") {
                formats = [formats];
            }
            if (formats.length) {
                for (var i = 0, l = formats.length; i < l; i++) {
                    var format = formats[i];
                    if (format) {
                        date = parseExact(value, format, culture);
                        if (date) {
                            break;
                        }
                    }
                }
            }
        }
        else {
            patterns = culture.calendar.patterns;
            for (prop in patterns) {
                date = parseExact(value, patterns[prop], culture);
                if (date) {
                    break;
                }
            }
        }
        return date || null;
    }

    // 1.    When defining a culture, all fields are required except the ones stated as optional.
    // 2.    You can use Globalization.extend to copy an existing culture and provide only the differing values,
    //       a good practice since most cultures do not differ too much from the 'default' culture.
    //       DO use the 'default' culture if you do this, as it is the only one that definitely
    //       exists.
    // 3.    Other plugins may add to the culture information provided by extending it. However,
    //       that plugin may extend it prior to the culture being defined, or after. Therefore,
    //       do not overwrite values that already exist when defining the baseline for a culture,
    //       by extending your culture object with the existing one.
    // 4.    Each culture should have a ".calendars" object with at least one calendar named "standard"
    //       which serves as the default calendar in use by that culture.
    // 5.    Each culture should have a ".calendar" object which is the current calendar being used,
    //       it may be dynamically changed at any time to one of the calendars in ".calendars".

    // To define a culture, use the following pattern, which handles defining the culture based
    // on the 'default culture, extending it with the existing culture if it exists, and defining
    // it if it does not exist.
    // Globalization.cultures.foo = Globalization.extend(true, Globalization.extend(true, {}, Globalization.cultures['default'], fooCulture), Globalization.cultures.foo)

    var cultures = Globalization.cultures = Globalization.cultures || {};
    var en = cultures["default"] = cultures.en = Globalization.extend(true, {
        // A unique name for the culture in the form <language code>-<country/region code>
        name: "en",
        // the name of the culture in the english language
        englishName: "English",
        // the name of the culture in its own language
        nativeName: "English",
        // whether the culture uses right-to-left text
        isRTL: false,
        // 'language' is used for so-called "specific" cultures.
        // For example, the culture "es-CL" means "Spanish, in Chili".
        // It represents the Spanish-speaking culture as it is in Chili,
        // which might have different formatting rules or even translations
        // than Spanish in Spain. A "neutral" culture is one that is not
        // specific to a region. For example, the culture "es" is the generic
        // Spanish culture, which may be a more generalized version of the language
        // that may or may not be what a specific culture expects.
        // For a specific culture like "es-CL", the 'language' field refers to the
        // neutral, generic culture information for the language it is using.
        // This is not always a simple matter of the string before the dash.
        // For example, the "zh-Hans" culture is netural (Simplified Chinese).
        // And the 'zh-SG' culture is Simplified Chinese in Singapore, whose lanugage
        // field is "zh-CHS", not "zh".
        // This field should be used to navigate from a specific culture to it's
        // more general, neutral culture. If a culture is already as general as it
        // can get, the language may refer to itself.
        language: "en",
        // numberFormat defines general number formatting rules, like the digits in
        // each grouping, the group separator, and how negative numbers are displayed.
        numberFormat: {
            // [negativePattern]
            // Note, numberFormat.pattern has no 'positivePattern' unlike percent and currency,
            // but is still defined as an array for consistency with them.
            //  negativePattern: one of "(n)|-n|- n|n-|n -"
            pattern: ["-n"],
            // number of decimal places normally shown
            decimals: 2,
            // string that separates number groups, as in 1,000,000
            ',': ",",
            // string that separates a number from the fractional portion, as in 1.99
            '.': ".",
            // array of numbers indicating the size of each number group.
            // TODO: more detailed description and example
            groupSizes: [3],
            // symbol used for positive numbers
            '+': "+",
            // symbol used for negative numbers
            '-': "-",
            percent: {
                // [negativePattern, positivePattern]
                //     negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %"
                //     positivePattern: one of "n %|n%|%n|% n"
                pattern: ["-n %", "n %"],
                // number of decimal places normally shown
                decimals: 2,
                // array of numbers indicating the size of each number group.
                // TODO: more detailed description and example
                groupSizes: [3],
                // string that separates number groups, as in 1,000,000
                ',': ",",
                // string that separates a number from the fractional portion, as in 1.99
                '.': ".",
                // symbol used to represent a percentage
                symbol: "%"
            },
            currency: {
                // [negativePattern, positivePattern]
                //     negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)"
                //     positivePattern: one of "$n|n$|$ n|n $"
                pattern: ["($n)", "$n"],
                // number of decimal places normally shown
                decimals: 2,
                // array of numbers indicating the size of each number group.
                // TODO: more detailed description and example
                groupSizes: [3],
                // string that separates number groups, as in 1,000,000
                ',': ",",
                // string that separates a number from the fractional portion, as in 1.99
                '.': ".",
                // symbol used to represent currency
                symbol: "$"
            }
        },
        // calendars defines all the possible calendars used by this culture.
        // There should be at least one defined with name 'standard', and is the default
        // calendar used by the culture.
        // A calendar contains information about how dates are formatted, information about
        // the calendar's eras, a standard set of the date formats,
        // translations for day and month names, and if the calendar is not based on the Gregorian
        // calendar, conversion functions to and from the Gregorian calendar.
        calendars: {
            standard: {
                // name that identifies the type of calendar this is
                name: "Gregorian_USEnglish",
                // separator of parts of a date (e.g. '/' in 11/05/1955)
                '/': "/",
                // separator of parts of a time (e.g. ':' in 05:44 PM)
                ':': ":",
                // the first day of the week (0 = Sunday, 1 = Monday, etc)
                firstDay: 0,
                days: {
                    // full day names
                    names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
                    // abbreviated day names
                    namesAbbr: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
                    // shortest day names
                    namesShort: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
                },
                months: {
                    // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar)
                    names: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ""],
                    // abbreviated month names
                    namesAbbr: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""]
                },
                // AM and PM designators in one of these forms:
                // The usual view, and the upper and lower case versions
                //      [standard,lowercase,uppercase]
                // The culture does not use AM or PM (likely all standard date formats use 24 hour time)
                //      null
                AM: ["AM", "am", "AM"],
                PM: ["PM", "pm", "PM"],
                eras: [
                // eras in reverse chronological order.
                // name: the name of the era in this culture (e.g. A.D., C.E.)
                // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era.
                // offset: offset in years from gregorian calendar
                {"name": "A.D.", "start": null, "offset": 0 }
            ],
                // when a two digit year is given, it will never be parsed as a four digit
                // year greater than this year (in the appropriate era for the culture)
                // Set it as a full year (e.g. 2029) or use an offset format starting from
                // the current year: "+19" would correspond to 2029 if the current year 2010.
                twoDigitYearMax: 2029,
                // set of predefined date and time patterns used by the culture
                // these represent the format someone in this culture would expect
                // to see given the portions of the date that are shown.
                patterns: {
                    // short date pattern
                    d: "M/d/yyyy",
                    // long date pattern
                    D: "dddd, MMMM dd, yyyy",
                    // short time pattern
                    t: "h:mm tt",
                    // long time pattern
                    T: "h:mm:ss tt",
                    // long date, short time pattern
                    f: "dddd, MMMM dd, yyyy h:mm tt",
                    // long date, long time pattern
                    F: "dddd, MMMM dd, yyyy h:mm:ss tt",
                    // month/day pattern
                    M: "MMMM dd",
                    // month/year pattern
                    Y: "yyyy MMMM",
                    // S is a sortable format that does not vary by culture
                    S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss"
                }
                // optional fields for each calendar:
                /*
                monthsGenitive:
                Same as months but used when the day preceeds the month.
                Omit if the culture has no genitive distinction in month names.
                For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx
                convert:
                Allows for the support of non-gregorian based calendars. This convert object is used to
                to convert a date to and from a gregorian calendar date to handle parsing and formatting.
                The two functions:
                fromGregorian(date)
                Given the date as a parameter, return an array with parts [year, month, day]
                corresponding to the non-gregorian based year, month, and day for the calendar.
                toGregorian(year, month, day)
                Given the non-gregorian year, month, and day, return a new Date() object
                set to the corresponding date in the gregorian calendar.
                */
            }
        }
    }, cultures.en);
    en.calendar = en.calendar || en.calendars.standard;

    var de = cultures["de"] = cultures.de = Globalization.extend(true, {
        name: "de",
        englishName: "German",
        nativeName: "Deutsch",
        language: "de",
        numberFormat: {
            groupSizes: [3],
            pattern: ["-n"],
            ",": ".",
            ".": ",",
            NaN: "n. def.",
            negativeInfinity: "-unendlich",
            positiveInfinity: "+unendlich",
            percent: {
                groupSizes: [3],
                pattern: ["-n%", "n%"],
                ",": ".",
                ".": ","
            },
            currency: {
                groupSizes: [3],
                pattern: ["-n $", "n $"],
                ",": ".",
                ".": ",",
                symbol: "€"
            }
        },
        calendars: {
            standard: {
                "/": ".",
                firstDay: 1,
                days: {
                    names: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"],
                    namesAbbr: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
                    namesShort: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"]
                },
                months: {
                    names: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember", ""],
                    namesAbbr: ["Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez", ""]
                },
                AM: null,
                PM: null,
                eras: [{ "name": "n. Chr.", "start": null, "offset": 0}],
                patterns: {
                    d: "dd.MM.yyyy",
                    D: "dddd, d. MMMM yyyy",
                    t: "HH:mm",
                    T: "HH:mm:ss",
                    f: "dddd, d. MMMM yyyy HH:mm",
                    F: "dddd, d. MMMM yyyy HH:mm:ss",
                    M: "dd MMMM",
                    Y: "MMMM yyyy"
                }
            }
        }
    }, cultures.de);

    var enGB = cultures["en-GB"] = cultures.enGB = Globalization.extend(true, {
        name: "en-GB",
        englishName: "English (United Kingdom)",
        nativeName: "English (United Kingdom)",
        isRTL: false,
        language: "en-GB",
        numberFormat: {
            pattern: ["-n"],
            decimals: 2,
            ',': ",",
            '.': ".",
            groupSizes: [3],
            '+': "+",
            '-': "-",
            percent: {
                pattern: ["-n %", "n %"],
                decimals: 2,
                groupSizes: [3],
                ',': ",",
                '.': ".",
                symbol: "%"
            },
            currency: {
                pattern: ["-$n", "$n"],
                decimals: 2,
                groupSizes: [3],
                ',': ",",
                '.': ".",
                symbol: "£"
            }
        },
        calendars: {
            standard: {
                name: "Gregorian_USEnglish",
                '/': "/",
                ':': ":",
                firstDay: 1,
                days: {
                    names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
                    namesAbbr: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
                    namesShort: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
                },
                months: {
                    names: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ""],
                    namesAbbr: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""]
                },
                AM: ["AM", "am", "AM"],
                PM: ["PM", "pm", "PM"],
                eras: [
                { "name": "A.D.", "start": null, "offset": 0 }
            ],
                twoDigitYearMax: 2029,
                patterns: {
                    d: "dd/MM/yyyy",
                    D: "dd MMMM yyyy",
                    t: "HH:mm",
                    T: "HH:mm:ss",
                    f: "dd MMMM yyyy HH:mm",
                    F: "dd MMMM yyyy HH:mm:ss",
                    M: "dd MMMM",
                    Y: "MMMM yyyy",
                    S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss"
                }
            }
        }
    }, cultures.enGB);

    var regexTrim = /^\s+|\s+$/g,
    regexInfinity = /^[+-]?infinity$/i,
    regexHex = /^0x[a-f0-9]+$/i,
    regexParseFloat = /^[+-]?\d*\.?\d*(e[+-]?\d+)?$/,
    toString = Object.prototype.toString;

    function startsWith(value, pattern) {
        return value.indexOf(pattern) === 0;
    }

    function endsWith(value, pattern) {
        return value.substr(value.length - pattern.length) === pattern;
    }

    function trim(value) {
        return (value + "").replace(regexTrim, "");
    }

    function zeroPad(str, count, left) {
        for (var l = str.length; l < count; l++) {
            str = (left ? ('0' + str) : (str + '0'));
        }
        return str;
    }

    function isArray(obj) {
        return toString.call(obj) === "[object Array]";
    }

    function isString(obj) {
        return toString.call(obj) === "[object String]";
    }

    function isObject(obj) {
        return toString.call(obj) === "[object Object]";
    }

    function arrayIndexOf(array, item) {
        if (array.indexOf) {
            return array.indexOf(item);
        }
        for (var i = 0, length = array.length; i < length; i++) {
            if (array[i] === item) {
                return i;
            }
        }
        return -1;
    }

    // *************************************** Numbers ***************************************

    function expandNumber(number, precision, formatInfo) {
        var groupSizes = formatInfo.groupSizes,
        curSize = groupSizes[0],
        curGroupIndex = 1,
        factor = Math.pow(10, precision),
        rounded = Math.round(number * factor) / factor;
        if (!isFinite(rounded)) {
            rounded = number;
        }
        number = rounded;

        var numberString = number + "",
        right = "",
        split = numberString.split(/e/i),
        exponent = split.length > 1 ? parseInt(split[1], 10) : 0;
        numberString = split[0];
        split = numberString.split(".");
        numberString = split[0];
        right = split.length > 1 ? split[1] : "";

        var l;
        if (exponent > 0) {
            right = zeroPad(right, exponent, false);
            numberString += right.slice(0, exponent);
            right = right.substr(exponent);
        }
        else if (exponent < 0) {
            exponent = -exponent;
            numberString = zeroPad(numberString, exponent + 1);
            right = numberString.slice(-exponent, numberString.length) + right;
            numberString = numberString.slice(0, -exponent);
        }

        if (precision > 0) {
            right = formatInfo['.'] +
            ((right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision));
        }
        else {
            right = "";
        }

        var stringIndex = numberString.length - 1,
        sep = formatInfo[","],
        ret = "";

        while (stringIndex >= 0) {
            if (curSize === 0 || curSize > stringIndex) {
                return numberString.slice(0, stringIndex + 1) + (ret.length ? (sep + ret + right) : right);
            }
            ret = numberString.slice(stringIndex - curSize + 1, stringIndex + 1) + (ret.length ? (sep + ret) : "");

            stringIndex -= curSize;

            if (curGroupIndex < groupSizes.length) {
                curSize = groupSizes[curGroupIndex];
                curGroupIndex++;
            }
        }
        return numberString.slice(0, stringIndex + 1) + sep + ret + right;
    }


    function parseNegativePattern(value, nf, negativePattern) {
        var neg = nf["-"],
        pos = nf["+"],
        ret;
        switch (negativePattern) {
            case "n -":
                neg = ' ' + neg;
                pos = ' ' + pos;
                // fall through
            case "n-":
                if (endsWith(value, neg)) {
                    ret = ['-', value.substr(0, value.length - neg.length)];
                }
                else if (endsWith(value, pos)) {
                    ret = ['+', value.substr(0, value.length - pos.length)];
                }
                break;
            case "- n":
                neg += ' ';
                pos += ' ';
                // fall through
            case "-n":
                if (startsWith(value, neg)) {
                    ret = ['-', value.substr(neg.length)];
                }
                else if (startsWith(value, pos)) {
                    ret = ['+', value.substr(pos.length)];
                }
                break;
            case "(n)":
                if (startsWith(value, '(') && endsWith(value, ')')) {
                    ret = ['-', value.substr(1, value.length - 2)];
                }
                break;
        }
        return ret || ['', value];
    }

    function formatNumber(value, format, culture) {
        if (!format || format === 'i') {
            return culture.name.length ? value.toLocaleString() : value.toString();
        }
        format = format || "D";

        var nf = culture.numberFormat,
        number = Math.abs(value),
        precision = -1,
        pattern;
        if (format.length > 1) precision = parseInt(format.slice(1), 10);

        var current = format.charAt(0).toUpperCase(),
        formatInfo;

        switch (current) {
            case "D":
                pattern = 'n';
                if (precision !== -1) {
                    number = zeroPad("" + number, precision, true);
                }
                if (value < 0) number = -number;
                break;
            case "N":
                formatInfo = nf;
                // fall through
            case "C":
                formatInfo = formatInfo || nf.currency;
                // fall through
            case "P":
                formatInfo = formatInfo || nf.percent;
                pattern = value < 0 ? formatInfo.pattern[0] : (formatInfo.pattern[1] || "n");
                if (precision === -1) precision = formatInfo.decimals;
                number = expandNumber(number * (current === "P" ? 100 : 1), precision, formatInfo);
                break;
            default:
                throw "Bad number format specifier: " + current;
        }

        var patternParts = /n|\$|-|%/g,
        ret = "";
        for (; ; ) {
            var index = patternParts.lastIndex,
            ar = patternParts.exec(pattern);

            ret += pattern.slice(index, ar ? ar.index : pattern.length);

            if (!ar) {
                break;
            }

            switch (ar[0]) {
                case "n":
                    ret += number;
                    break;
                case "$":
                    ret += nf.currency.symbol;
                    break;
                case "-":
                    // don't make 0 negative
                    if (/[1-9]/.test(number)) {
                        ret += nf["-"];
                    }
                    break;
                case "%":
                    ret += nf.percent.symbol;
                    break;
            }
        }

        return ret;
    }

    // *************************************** Dates ***************************************

    function outOfRange(value, low, high) {
        return value < low || value > high;
    }

    function expandYear(cal, year) {
        // expands 2-digit year into 4 digits.
        var now = new Date(),
        era = getEra(now);
        if (year < 100) {
            var twoDigitYearMax = cal.twoDigitYearMax;
            twoDigitYearMax = typeof twoDigitYearMax === 'string' ? new Date().getFullYear() % 100 + parseInt(twoDigitYearMax, 10) : twoDigitYearMax;
            var curr = getEraYear(now, cal, era);
            year += curr - (curr % 100);
            if (year > twoDigitYearMax) {
                year -= 100;
            }
        }
        return year;
    }

    function getEra(date, eras) {
        if (!eras) return 0;
        var start, ticks = date.getTime();
        for (var i = 0, l = eras.length; i < l; i++) {
            start = eras[i].start;
            if (start === null || ticks >= start) {
                return i;
            }
        }
        return 0;
    }

    function toUpper(value) {
        // 'he-IL' has non-breaking space in weekday names.
        return value.split("\u00A0").join(' ').toUpperCase();
    }

    function toUpperArray(arr) {
        var results = [];
        for (var i = 0, l = arr.length; i < l; i++) {
            results[i] = toUpper(arr[i]);
        }
        return results;
    }

    function getEraYear(date, cal, era, sortable) {
        var year = date.getFullYear();
        if (!sortable && cal.eras) {
            // convert normal gregorian year to era-shifted gregorian
            // year by subtracting the era offset
            year -= cal.eras[era].offset;
        }
        return year;
    }

    function getDayIndex(cal, value, abbr) {
        var ret,
        days = cal.days,
        upperDays = cal._upperDays;
        if (!upperDays) {
            cal._upperDays = upperDays = [
            toUpperArray(days.names),
            toUpperArray(days.namesAbbr),
            toUpperArray(days.namesShort)
        ];
        }
        value = toUpper(value);
        if (abbr) {
            ret = arrayIndexOf(upperDays[1], value);
            if (ret === -1) {
                ret = arrayIndexOf(upperDays[2], value);
            }
        }
        else {
            ret = arrayIndexOf(upperDays[0], value);
        }
        return ret;
    }

    function getMonthIndex(cal, value, abbr) {
        var months = cal.months,
        monthsGen = cal.monthsGenitive || cal.months,
        upperMonths = cal._upperMonths,
        upperMonthsGen = cal._upperMonthsGen;
        if (!upperMonths) {
            cal._upperMonths = upperMonths = [
            toUpperArray(months.names),
            toUpperArray(months.namesAbbr)
        ];
            cal._upperMonthsGen = upperMonthsGen = [
            toUpperArray(monthsGen.names),
            toUpperArray(monthsGen.namesAbbr)
        ];
        }
        value = toUpper(value);
        var i = arrayIndexOf(abbr ? upperMonths[1] : upperMonths[0], value);
        if (i < 0) {
            i = arrayIndexOf(abbr ? upperMonthsGen[1] : upperMonthsGen[0], value);
        }
        return i;
    }

    function appendPreOrPostMatch(preMatch, strings) {
        // appends pre- and post- token match strings while removing escaped characters.
        // Returns a single quote count which is used to determine if the token occurs
        // in a string literal.
        var quoteCount = 0,
        escaped = false;
        for (var i = 0, il = preMatch.length; i < il; i++) {
            var c = preMatch.charAt(i);
            switch (c) {
                case '\'':
                    if (escaped) {
                        strings.push("'");
                    }
                    else {
                        quoteCount++;
                    }
                    escaped = false;
                    break;
                case '\\':
                    if (escaped) {
                        strings.push("\\");
                    }
                    escaped = !escaped;
                    break;
                default:
                    strings.push(c);
                    escaped = false;
                    break;
            }
        }
        return quoteCount;
    }

    function expandFormat(cal, format) {
        // expands unspecified or single character date formats into the full pattern.
        format = format || "F";
        var pattern,
        patterns = cal.patterns,
        len = format.length;
        if (len === 1) {
            pattern = patterns[format];
            if (!pattern) {
                throw "Invalid date format string '" + format + "'.";
            }
            format = pattern;
        }
        else if (len === 2 && format.charAt(0) === "%") {
            // %X escape format -- intended as a custom format string that is only one character, not a built-in format.
            format = format.charAt(1);
        }
        return format;
    }

    function getParseRegExp(cal, format) {
        // converts a format string into a regular expression with groups that
        // can be used to extract date fields from a date string.
        // check for a cached parse regex.
        var re = cal._parseRegExp;
        if (!re) {
            cal._parseRegExp = re = {};
        }
        else {
            var reFormat = re[format];
            if (reFormat) {
                return reFormat;
            }
        }

        // expand single digit formats, then escape regular expression characters.
        var expFormat = expandFormat(cal, format).replace(/([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1"),
        regexp = ["^"],
        groups = [],
        index = 0,
        quoteCount = 0,
        tokenRegExp = getTokenRegExp(),
        match;

        // iterate through each date token found.
        while ((match = tokenRegExp.exec(expFormat)) !== null) {
            var preMatch = expFormat.slice(index, match.index);
            index = tokenRegExp.lastIndex;

            // don't replace any matches that occur inside a string literal.
            quoteCount += appendPreOrPostMatch(preMatch, regexp);
            if (quoteCount % 2) {
                regexp.push(match[0]);
                continue;
            }

            // add a regex group for the token.
            var m = match[0],
            len = m.length,
            add;
            switch (m) {
                case 'dddd': case 'ddd':
                case 'MMMM': case 'MMM':
                case 'gg': case 'g':
                    add = "(\\D+)";
                    break;
                case 'tt': case 't':
                    add = "(\\D*)";
                    break;
                case 'yyyy':
                case 'fff':
                case 'ff':
                case 'f':
                    add = "(\\d{" + len + "})";
                    break;
                case 'dd': case 'd':
                case 'MM': case 'M':
                case 'yy': case 'y':
                case 'HH': case 'H':
                case 'hh': case 'h':
                case 'mm': case 'm':
                case 'ss': case 's':
                    add = "(\\d\\d?)";
                    break;
                case 'zzz':
                    add = "([+-]?\\d\\d?:\\d{2})";
                    break;
                case 'zz': case 'z':
                    add = "([+-]?\\d\\d?)";
                    break;
                case '/':
                    add = "(\\" + cal["/"] + ")";
                    break;
                default:
                    throw "Invalid date format pattern '" + m + "'.";
                    break;
            }
            if (add) {
                regexp.push(add);
            }
            groups.push(match[0]);
        }
        appendPreOrPostMatch(expFormat.slice(index), regexp);
        regexp.push("$");

        // allow whitespace to differ when matching formats.
        var regexpStr = regexp.join('').replace(/\s+/g, "\\s+"),
        parseRegExp = { 'regExp': regexpStr, 'groups': groups };

        // cache the regex for this format.
        return re[format] = parseRegExp;
    }

    function getTokenRegExp() {
        // regular expression for matching date and time tokens in format strings.
        return /\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g;
    }

    function parseExact(value, format, culture) {
        // try to parse the date string by matching against the format string
        // while using the specified culture for date field names.
        value = trim(value);
        var cal = culture.calendar,
        // convert date formats into regular expressions with groupings.
        // use the regexp to determine the input format and extract the date fields.
        parseInfo = getParseRegExp(cal, format),
        match = new RegExp(parseInfo.regExp).exec(value);
        if (match === null) {
            return null;
        }
        // found a date format that matches the input.
        var groups = parseInfo.groups,
        era = null, year = null, month = null, date = null, weekDay = null,
        hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null,
        pmHour = false;
        // iterate the format groups to extract and set the date fields.
        for (var j = 0, jl = groups.length; j < jl; j++) {
            var matchGroup = match[j + 1];
            if (matchGroup) {
                var current = groups[j],
                clength = current.length,
                matchInt = parseInt(matchGroup, 10);
                switch (current) {
                    case 'dd': case 'd':
                        // Day of month.
                        date = matchInt;
                        // check that date is generally in valid range, also checking overflow below.
                        if (outOfRange(date, 1, 31)) return null;
                        break;
                    case 'MMM':
                    case 'MMMM':
                        month = getMonthIndex(cal, matchGroup, clength === 3);
                        if (outOfRange(month, 0, 11)) return null;
                        break;
                    case 'M': case 'MM':
                        // Month.
                        month = matchInt - 1;
                        if (outOfRange(month, 0, 11)) return null;
                        break;
                    case 'y': case 'yy':
                    case 'yyyy':
                        year = clength < 4 ? expandYear(cal, matchInt) : matchInt;
                        if (outOfRange(year, 0, 9999)) return null;
                        break;
                    case 'h': case 'hh':
                        // Hours (12-hour clock).
                        hour = matchInt;
                        if (hour === 12) hour = 0;
                        if (outOfRange(hour, 0, 11)) return null;
                        break;
                    case 'H': case 'HH':
                        // Hours (24-hour clock).
                        hour = matchInt;
                        if (outOfRange(hour, 0, 23)) return null;
                        break;
                    case 'm': case 'mm':
                        // Minutes.
                        min = matchInt;
                        if (outOfRange(min, 0, 59)) return null;
                        break;
                    case 's': case 'ss':
                        // Seconds.
                        sec = matchInt;
                        if (outOfRange(sec, 0, 59)) return null;
                        break;
                    case 'tt': case 't':
                        // AM/PM designator.
                        // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of
                        // the AM tokens. If not, fail the parse for this format.
                        pmHour = cal.PM && (matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2]);
                        if (!pmHour && (!cal.AM || (matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2]))) return null;
                        break;
                    case 'f':
                        // Deciseconds.
                    case 'ff':
                        // Centiseconds.
                    case 'fff':
                        // Milliseconds.
                        msec = matchInt * Math.pow(10, 3 - clength);
                        if (outOfRange(msec, 0, 999)) return null;
                        break;
                    case 'ddd':
                        // Day of week.
                    case 'dddd':
                        // Day of week.
                        weekDay = getDayIndex(cal, matchGroup, clength === 3);
                        if (outOfRange(weekDay, 0, 6)) return null;
                        break;
                    case 'zzz':
                        // Time zone offset in +/- hours:min.
                        var offsets = matchGroup.split(/:/);
                        if (offsets.length !== 2) return null;
                        hourOffset = parseInt(offsets[0], 10);
                        if (outOfRange(hourOffset, -12, 13)) return null;
                        var minOffset = parseInt(offsets[1], 10);
                        if (outOfRange(minOffset, 0, 59)) return null;
                        tzMinOffset = (hourOffset * 60) + (startsWith(matchGroup, '-') ? -minOffset : minOffset);
                        break;
                    case 'z': case 'zz':
                        // Time zone offset in +/- hours.
                        hourOffset = matchInt;
                        if (outOfRange(hourOffset, -12, 13)) return null;
                        tzMinOffset = hourOffset * 60;
                        break;
                    case 'g': case 'gg':
                        var eraName = matchGroup;
                        if (!eraName || !cal.eras) return null;
                        eraName = trim(eraName.toLowerCase());
                        for (var i = 0, l = cal.eras.length; i < l; i++) {
                            if (eraName === cal.eras[i].name.toLowerCase()) {
                                era = i;
                                break;
                            }
                        }
                        // could not find an era with that name
                        if (era === null) return null;
                        break;
                }
            }
        }
        var result = new Date(), defaultYear, convert = cal.convert;
        defaultYear = convert ? convert.fromGregorian(result)[0] : result.getFullYear();
        if (year === null) {
            year = defaultYear;
        }
        else if (cal.eras) {
            // year must be shifted to normal gregorian year
            // but not if year was not specified, its already normal gregorian
            // per the main if clause above.
            year += cal.eras[(era || 0)].offset;
        }
        // set default day and month to 1 and January, so if unspecified, these are the defaults
        // instead of the current day/month.
        if (month === null) {
            month = 0;
        }
        if (date === null) {
            date = 1;
        }
        // now have year, month, and date, but in the culture's calendar.
        // convert to gregorian if necessary
        if (convert) {
            result = convert.toGregorian(year, month, date);
            // conversion failed, must be an invalid match
            if (result === null) return null;
        }
        else {
            // have to set year, month and date together to avoid overflow based on current date.
            result.setFullYear(year, month, date);
            // check to see if date overflowed for specified month (only checked 1-31 above).
            if (result.getDate() !== date) return null;
            // invalid day of week.
            if (weekDay !== null && result.getDay() !== weekDay) {
                return null;
            }
        }
        // if pm designator token was found make sure the hours fit the 24-hour clock.
        if (pmHour && hour < 12) {
            hour += 12;
        }
        result.setHours(hour, min, sec, msec);
        if (tzMinOffset !== null) {
            // adjust timezone to utc before applying local offset.
            var adjustedMin = result.getMinutes() - (tzMinOffset + result.getTimezoneOffset());
            // Safari limits hours and minutes to the range of -127 to 127.  We need to use setHours
            // to ensure both these fields will not exceed this range.  adjustedMin will range
            // somewhere between -1440 and 1500, so we only need to split this into hours.
            result.setHours(result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60);
        }
        return result;
    }

    function formatDate(value, format, culture) {
        var cal = culture.calendar,
        convert = cal.convert;
        if (!format || !format.length || format === 'i') {
            var ret;
            if (culture && culture.name.length) {
                if (convert) {
                    // non-gregorian calendar, so we cannot use built-in toLocaleString()
                    ret = formatDate(value, cal.patterns.F, culture);
                }
                else {
                    var eraDate = new Date(value.getTime()),
                    era = getEra(value, cal.eras);
                    eraDate.setFullYear(getEraYear(value, cal, era));
                    ret = eraDate.toLocaleString();
                }
            }
            else {
                ret = value.toString();
            }
            return ret;
        }

        var eras = cal.eras,
        sortable = format === "s";
        format = expandFormat(cal, format);

        // Start with an empty string
        ret = [];
        var hour,
        zeros = ['0', '00', '000'],
        foundDay,
        checkedDay,
        dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g,
        quoteCount = 0,
        tokenRegExp = getTokenRegExp(),
        converted;

        function padZeros(num, c) {
            var r, s = num + '';
            if (c > 1 && s.length < c) {
                r = (zeros[c - 2] + s);
                return r.substr(r.length - c, c);
            }
            else {
                r = s;
            }
            return r;
        }

        function hasDay() {
            if (foundDay || checkedDay) {
                return foundDay;
            }
            foundDay = dayPartRegExp.test(format);
            checkedDay = true;
            return foundDay;
        }

        function getPart(date, part) {
            if (converted) {
                return converted[part];
            }
            switch (part) {
                case 0: return date.getFullYear();
                case 1: return date.getMonth();
                case 2: return date.getDate();
            }
        }

        if (!sortable && convert) {
            converted = convert.fromGregorian(value);
        }

        for (; ; ) {
            // Save the current index
            var index = tokenRegExp.lastIndex,
            // Look for the next pattern
            ar = tokenRegExp.exec(format);

            // Append the text before the pattern (or the end of the string if not found)
            var preMatch = format.slice(index, ar ? ar.index : format.length);
            quoteCount += appendPreOrPostMatch(preMatch, ret);

            if (!ar) {
                break;
            }

            // do not replace any matches that occur inside a string literal.
            if (quoteCount % 2) {
                ret.push(ar[0]);
                continue;
            }

            var current = ar[0],
            clength = current.length;

            switch (current) {
                case "ddd":
                    //Day of the week, as a three-letter abbreviation
                case "dddd":
                    // Day of the week, using the full name
                    var names = (clength === 3) ? cal.days.namesAbbr : cal.days.names;
                    ret.push(names[value.getDay()]);
                    break;
                case "d":
                    // Day of month, without leading zero for single-digit days
                case "dd":
                    // Day of month, with leading zero for single-digit days
                    foundDay = true;
                    ret.push(padZeros(getPart(value, 2), clength));
                    break;
                case "MMM":
                    // Month, as a three-letter abbreviation
                case "MMMM":
                    // Month, using the full name
                    var part = getPart(value, 1);
                    ret.push((cal.monthsGenitive && hasDay())
                    ? cal.monthsGenitive[clength === 3 ? "namesAbbr" : "names"][part]
                    : cal.months[clength === 3 ? "namesAbbr" : "names"][part]);
                    break;
                case "M":
                    // Month, as digits, with no leading zero for single-digit months
                case "MM":
                    // Month, as digits, with leading zero for single-digit months
                    ret.push(padZeros(getPart(value, 1) + 1, clength));
                    break;
                case "y":
                    // Year, as two digits, but with no leading zero for years less than 10
                case "yy":
                    // Year, as two digits, with leading zero for years less than 10
                case "yyyy":
                    // Year represented by four full digits
                    part = converted ? converted[0] : getEraYear(value, cal, getEra(value, eras), sortable);
                    if (clength < 4) {
                        part = part % 100;
                    }
                    ret.push(padZeros(part, clength));
                    break;
                case "h":
                    // Hours with no leading zero for single-digit hours, using 12-hour clock
                case "hh":
                    // Hours with leading zero for single-digit hours, using 12-hour clock
                    hour = value.getHours() % 12;
                    if (hour === 0) hour = 12;
                    ret.push(padZeros(hour, clength));
                    break;
                case "H":
                    // Hours with no leading zero for single-digit hours, using 24-hour clock
                case "HH":
                    // Hours with leading zero for single-digit hours, using 24-hour clock
                    ret.push(padZeros(value.getHours(), clength));
                    break;
                case "m":
                    // Minutes with no leading zero  for single-digit minutes
                case "mm":
                    // Minutes with leading zero  for single-digit minutes
                    ret.push(padZeros(value.getMinutes(), clength));
                    break;
                case "s":
                    // Seconds with no leading zero for single-digit seconds
                case "ss":
                    // Seconds with leading zero for single-digit seconds
                    ret.push(padZeros(value.getSeconds(), clength));
                    break;
                case "t":
                    // One character am/pm indicator ("a" or "p")
                case "tt":
                    // Multicharacter am/pm indicator
                    part = value.getHours() < 12 ? (cal.AM ? cal.AM[0] : " ") : (cal.PM ? cal.PM[0] : " ");
                    ret.push(clength === 1 ? part.charAt(0) : part);
                    break;
                case "f":
                    // Deciseconds
                case "ff":
                    // Centiseconds
                case "fff":
                    // Milliseconds
                    ret.push(padZeros(value.getMilliseconds(), 3).substr(0, clength));
                    break;
                case "z":
                    // Time zone offset, no leading zero
                case "zz":
                    // Time zone offset with leading zero
                    hour = value.getTimezoneOffset() / 60;
                    ret.push((hour <= 0 ? '+' : '-') + padZeros(Math.floor(Math.abs(hour)), clength));
                    break;
                case "zzz":
                    // Time zone offset with leading zero
                    hour = value.getTimezoneOffset() / 60;
                    ret.push((hour <= 0 ? '+' : '-') + padZeros(Math.floor(Math.abs(hour)), 2) +
                    // Hard coded ":" separator, rather than using cal.TimeSeparator
                    // Repeated here for consistency, plus ":" was already assumed in date parsing.
                    ":" + padZeros(Math.abs(value.getTimezoneOffset() % 60), 2));
                    break;
                case "g":
                case "gg":
                    if (cal.eras) {
                        ret.push(cal.eras[getEra(value, eras)].name);
                    }
                    break;
                case "/":
                    ret.push(cal["/"]);
                    break;
                default:
                    throw "Invalid date format pattern '" + current + "'.";
                    break;
            }
        }
        return ret.join('');
    }

    // EXPORTS
    jQuery.global = Globalization;
})();


(function ($) {

    $.fn.delayedTextCommitChange = function (options) {
        return this.each(function () {
            new delayedItem($(this), options.delay, options.cb);
        });

        function delayedItem(item, delay, cb) {
            var charCount = 0;

            item.keyup(function (e) {
                if (e.keyCode == 9 || e.keyCode == 16) { //ingnore shift and tab
                    return;
                }

                var adjustedDelay = e.keyCode == 13 ? 0 : delay; //no delay if user presses enter

                ++charCount;
                var closureCharCount = charCount;
                setTimeout(function () {
                    if (charCount == closureCharCount) {
                        cb.call();
                    }
                }, adjustedDelay);
            }).keydown(function(e) {
                //prevent enter key from submitting form
                if(e.keyCode == 13) {
                    e.cancelBubble = true;
                    if (e.stopPropogation) {
                        e.stopPropagation();
                    }

                    return false;
                }
            }).bind("paste", function () {
                ++charCount;
                var closureCharCount = charCount;
                setTimeout(function () {
                    if (charCount == closureCharCount) {
                        cb.call();
                    }
                }, delay);
            });
        }
    };

})(jQuery, this);

