﻿var rsw = {};

rsw.EMPLOYEE_AVAILABILITY_AVAILABLE = 1;
rsw.EMPLOYEE_AVAILABILITY_SCHEDULED = 2;
rsw.EMPLOYEE_AVAILABILITY_TIME_OFF = 3;
rsw.EMPLOYEE_AVAILABILITY_UNAVAILABLE = 4;

/**
* Performs an Ajax call.
*
* @param params A map which contains the following:
*  url (String, required)
*      URL endpoint to POST to, e.g. "/ajax/myendpoint".
*  data (Map, required)
*      Data to pass to the URL endpoint.
*  onSuccess (function which returns void and takes one parameter containing the response body, optional)
*      Function which is invoked when the Ajax call was processed successfully.
*  onFailure (function which returns void and takes one parameter containing the response body/error message, optional)
*      Function which is invoked when the Ajax call fails.
*
* @return Nothing.
*/
rsw.ajax = function (params) {
    var url = params.url;
    var returnedDataType = params.returnedDataType || "json";

    rsw.clearErrors();

    $.ajax({
        type: "POST",
        url: params.url,
        data: params.data,
        dataType: "json",
        error: function (data_response) {
            var loggedOut = (data_response.responseText.indexOf('action="/Account/SignIn') > 0);

            if(window.console)
                console.log("Auth issue?:" + loggedOut);

            if (loggedOut) {
                location.href = "/Account/SignIn?ReturnUrl=" + encodeURI(location.href);
            } else {
                rsw.handleUnexpectedError(data_response);
            }
        },
        success: function (data_response) {
            if (window.console)
                console.log("Server responded:", data_response);

            if (data_response != null && data_response.Result == 'success') {
                if (params.onSuccess) {
                    params.onSuccess(data_response);
                } else {
                    rsw.onSuccess(data_response);
                }
                if (params.postSuccess)
                    params.postSuccess(data_response);
            } else if (data_response != null && data_response.Result == 'failure') {
                if (params.onError)
                    params.onError(data_response);
                else
                    rsw.onError(data_response);
            } else {
                rsw.handleUnexpectedError(data_response);
            }
        }
    });
};

rsw.reload = function () {
    if (window.parent != undefined)
        parent.location.href = parent.location.href;
    else
        window.location.href = window.location.href;
};


rsw.handleUnexpectedError = function(data_response) {
    jQuery.noticeAdd({
        text: 'Sorry, an error occurred. Please try again later',
        type: 'error',
        stay: true
    });
};

rsw.closeFancyBox = function () {
    rsw.closeFancyBox(false);
};

rsw.closeFancyBox = function (refresh) {
    parent.$.fancybox.close();
    if (refresh)
        rsw.reload();
};


/**
 *
 *  
 */
rsw.countChars = function (textAreaId, countDiv, keywordDiv, limit) {
    var smslimit = 100;
    var text = $('#' + textAreaId).val();
    var keyword = $('#' + keywordDiv).val();
    var textLength = text.length;
    if (keyword) {
        var keywordLength = keyword.length;
        textLength += keywordLength;
    }
    if (limit > 0) {
        $('#' + countDiv).html((textLength) + ' / ' + smslimit);
        if (textLength > limit) {
            $('#' + countDiv).html('You have a ' + limit + ' character limit.');
            $('#' + textAreaId).val(text.substr(0, limit));
            return false;
        }
    } else {
        if (textLength <= smslimit) {
            $('#' + countDiv).html((textLength) + ' / ' + smslimit);
        } else {
            var overflow = Math.ceil(textLength / smslimit);
            $('#' + countDiv).html((textLength) + ' / ' + (smslimit * overflow) + '  (' + overflow + ')');
        }
        return true;
    }
}

rsw.getErrorMessages = function (data_response, defaultMessage) {
var errorMessages = [];
defaultMessage = defaultMessage || "Sorry, we were unable to save.";

$.each(data_response.Errors, function (i, error) {
errorMessages.push(error.message);
});

if (errorMessages.length === 0)
errorMessages.push(defaultMessage);

return errorMessages;
}

rsw.displayErrorsInNoticeBubble = function (data_response, defaultMessage) {
$.noticeAdd({
text: rsw.getErrorMessages(data_response, defaultMessage).join("<br/>"),
type: "error"
});
}

/*
*  Handles sliding divs up and down for display based on the div id being passed in
*/
rsw.toggleSection = function (sectionName) {
    if ($('#' + sectionName).is(':visible')) {
        $('#' + sectionName).slideUp("fast");
    }
    if (!$('#' + sectionName).is(':visible')) {
        $('#' + sectionName).slideDown("fast");
    }
}

rsw.validateAndSubmit = function (input) {
    var myform = null;
    rsw.clearErrors();
    if (input == null && $('form').length == 1) {
        myform = $('form')[0];
    } else if ($(input).parents('form').length == 1) {
        myform = $(input).parents('form');
    }
    if (myform && $(myform).valid()) {
        myform.submit();
    }
};

rsw.clearErrors = function () {
    $(".hasError").each(function (i) {
        $(this).removeClass("hasError");
    });
    $(".field-validation-error").each(function (i) {
        $(this).text("");
        $(this).addClass("field-validation-valid");
        $(this).removeClass("field-validation-error");
    });
    $("#errors").hide().empty();
    $('.validation-summary-errors ul').empty();
};


rsw.highlightErrors = function (errors) {
    var generalErrors = [];
    var firstErrorObj = null;
    $.each(errors, function (key, obj) {
        if (obj.field != '') {
            $("#" + obj.field).addClass("hasError");
            if (!firstErrorObj)
                firstErrorObj = obj.field;
            $('label[for="' + obj.field + '"]').addClass("hasError");
            $('span[data-valmsg-for="' + obj.field + '"]').removeClass('field-validation-valid');
            $('span[data-valmsg-for="' + obj.field + '"]').addClass('field-validation-error');
            $('span[data-valmsg-for="' + obj.field + '"]').append('<span htmlfor="' + obj.field + '" generated="true" class="">' + obj.message + '</span>');
        } else {
            generalErrors.push(obj.message);
        }
    });

    if (firstErrorObj && $('input[name=' + firstErrorObj + ']'))
        $('input[name=' + firstErrorObj + ']').focus();
    if (generalErrors.length > 0) {
        var errorList = $('<ul></ul>');
        $.each(generalErrors, function (index, msg) {
            errorList.append('<li>' + msg + '</li>');
        });

        if ($(".validation-summary-valid").length > 0 && $(".validation-summary-errors").length == 0)
            $(".validation-summary-valid").removeClass("validation-summary-valid").addClass("validation-summary-errors");

        $('.validation-summary-errors').empty().append(errorList);
        $('.validation-summary-errors').show();
    }
};

rsw.onError = function (data_response) {
    if (data_response.Errors)
        rsw.highlightErrors(data_response.Errors)
        jQuery.noticeAdd({
            text: 'Unable to save, please fix any errors and try again',
            type: 'error',
            stay: false
        });
};

rsw.onSuccess = function(data_response) {
    jQuery.noticeAdd({
    text: 'Success',
    type: 'success',
    stay: false
    });
};

rsw.changeSchedule = function (selectObject) {
    var scheduleID = $(selectObject).val();
    var URL;
    if (scheduleID == 0) {
        URL = "/Schedule/Create";
    } else {
        URL = "/Home/ChangeSchedule/" + scheduleID + "?ReturnURL=" + encodeURI(location.href);
    }
    location.href = URL;
}

rsw.changeEmployee = function (selectObject) {
    var employeeId = $(selectObject).val();

    if (employeeId == 0) {
        $.each($('li[empids]'), function (i, s) {
            var shift = $(s);
            if (!shift.hasClass("hiddenForOverflow")) {
                shift.show();
            } else if (shift.is(":visible")) shift.hide();
        });
        $('li[empids]').removeClass('highlight');
        $('.moreShifts').show();
    } else {
        $('.moreShifts').hide();
        $('li[empids]:not([empids~=' + employeeId + '])').hide();
        $('li[empids][empids~=' + employeeId + ']".hiddenForOverflow"').hide();
        $('li[empids][empids~=' + employeeId + ']').addClass('highlight');
        var containers = $('li[empids][empids~=' + employeeId + ']').parent();
        $.each(containers, function (a, c) {
            var container = $(c);
            if (container.children("[empids~=" + employeeId + "]").length > 5) {
                container.children(".moreShifts").show();
            }
            $.each(container.children("[empids~=" + employeeId + "]"), function (b, s) {
                var shift = $(s);
                if (!shift.hasClass("hiddenForOverflow")) {
                    shift.show();
                } else {
                    //only show overflow shifts if other shifts are hidden...
                    if (!shift.parent().children(".moreShifts").is(":visible"))
                        shift.show();
                }
            });
        });
    }
}

/**
* Handlers for buttons that are across pages in the site
*/
$("#Activate").live("click", function () {
    var modelID = $(this).attr('modelID');
    var modelType = $(this).attr('modelType');
    // need to toggle to update 
    var active = $(this).attr('modelActive') == 'true' ? 'false' : 'true';
    var Status = { Active: active };
    rsw.ajax({
        url: '/' + modelType + '/SetActive/' + modelID,
        data: Status,
        postSuccess: function () {
            var label = ($("#Activate").text().startsWith("Deactivate") ? "Activate " : "Deactivate ");
            if (modelType.toLowerCase() === "job") modelType = "Position";
            $("#Activate").text(label + modelType);
            $("#Activate").attr('modelActive', active);
        }
    })
});


// Simple way to clear out select options
rsw.clearSelectList = function (object) {
// using this instead ob object.empty(); Potential IE 7 BUG
    $(object)[0].options.length = 0;
}

// How many keys are in the given object?
rsw.countKeysInObject = function (object) {    
    var count = 0;
    for (var key in object)
    // if (object.hasOwnProperty(key)) // doesn't work in IE!
        ++count;

    return count;
};

// Gets URL parameter value by name or undefined if none exists
rsw.getUrlParameter = function(name, url) {
    url = url || window.location.href;
    var values = new RegExp("[\\?&]" + name + "=([^&#]*)").exec(url);
    return values == null ? undefined : values[1];
};

rsw.getUrlParameters = function() {
    var map = {};
    var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, key, value) {
        map[key] = value;
    });
    return map; 
};

// Takes a base URL without a parameter string and a dictionary of parameters (optional).
rsw.constructUrl = function (urlBase, parameters) {
    var url = urlBase;
    parameters = parameters || {};

    if (rsw.countKeysInObject(parameters) == 0)
        return url;

    url += "?";

    $.each(parameters, function (key, value) {
        url += (rsw.stringEndsWith(url, "?") ? "" : "&") + rsw.urlEncode(key) + "=" + rsw.urlEncode(value);
    });

    return url;
};

rsw.urlEncode = function (value) {
    return encodeURIComponent(value).replace("'", "%27");
};

rsw.stringEndsWith = function (string, endsWith) {
    return new RegExp(rsw.escapeRegex(endsWith) + "$").test(string);
};

// Escapes all special regex characters in string and returns the result.
// Useful for using a user-entered string as a regex.
// Thanks to Theodor Zoulias        
rsw.escapeRegex = function (string) {
    return string.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
};

// Fancybox requires an "a" element to work.  So we make a fake one, add it to the body, click it, then remove it.
rsw.showFancybox = function (selectorText, settings) {
    var temporaryClass = "fancybox-" + new Date().getTime();
    var inline = !settings.type || settings.type == "inline"
    if (inline)
        var selector = $(selectorText).addClass(temporaryClass);

    rsw.hideBubbles();

    $("body").append('<a href="' + selectorText + '" class="' + temporaryClass + ' style="display: none;"></a>');
    $("a." + temporaryClass).fancybox(settings).click().remove();

    if (inline)
        selector.removeClass(temporaryClass);
};

rsw.stripLeadingCharacter = function (string, character) {
    return string.replace(new RegExp("^" + character + "+"), "");
};

rsw.roundToDecimalPlacesIfNotWholeNumber = function (number, decimalPlaces) {
    return number % 1 == 0 ? number : number.toFixed(decimalPlaces);
};

rsw.focusIfEmpty = function (selector) {
    selector = $(selector);
    if("" == $.trim(selector.val()))
        setTimeout(function() {
            selector.focus();
        }, 30);
};

rsw.initialsForName = function (name) {
    if (!name)
        return undefined;

    // Remove extra whitespace
    name = $.trim(name.replace(/\s+/g, " ")).toUpperCase();    

    if (name == "")
        return undefined;

    var nameComponents = name.split(" ");
    if (nameComponents.length == 1)
        return name.substring(0, 1);

    var initials = [];
    $.each(nameComponents, function (i, nameComponent) {
        initials.push(nameComponent.substring(0, 1));
    });

    return initials.join("");
};

rsw.nameAsLastNameCommaFirstInitial = function (name) {
    if (!name)
        return undefined;

    // Remove extra whitespace
    name = $.trim(name.replace(/\s+/g, " "));

    if (name == "")
        return undefined;

    var nameComponents = name.split(" ");
    if (nameComponents.length != 2)
        return name;

    var firstName = nameComponents[0];
    var lastName = nameComponents[1];
    return lastName + ", " + firstName.substring(0, 1) + ".";
};


rsw.firstInitialLastName = function (firstname, lastname) {
    var name = "";
    if (!lastname || lastname == "")
        return undefined;

    if (firstname && firstname.length > 1)
        return name = firstname.substring(0, 1) + ". " + lastname;
};

// Takes a date object and converts it to m/d/yyyy.
// Example: 1/29/2011.
rsw.dateToStringWithoutTime = function (date) {
    if (!date)
        return null;

    return (date.getMonth() + 1) + "/" + date.getDate() + "/" + date.getFullYear();
};

rsw.createWdCalendarDayBackgroundId = function (date) {
    return rsw.dateToStringWithoutTime(date).replace(/\//g, "-");
};

// If calendarDate is a string, parse it into a real Date object (time component optional).
// e.g. 9/21/2010 17:30
// e.g. 1/23/2011
//
// Otherwise, return the date as-is.
rsw.stringDateToDate = function (calendarDate) {
    if (!calendarDate || calendarDate instanceof Date)
        return calendarDate;

    var dateStringChunks = calendarDate.split(" ");
    var dateChunks = dateStringChunks.length == 0 ? [] : dateStringChunks[0].split("/");

    var hours = 0;
    var minutes = 0;

    if (dateStringChunks.length > 1) {
        var timeChunks = dateStringChunks[1].split(":");
        hours = parseInt(timeChunks[0]);
        minutes = parseInt(timeChunks[1]);
    }


    // Params are year, month, day, hours, minutes, seconds
    return new Date(parseInt(dateChunks[2]), parseInt(dateChunks[0]) - 1, parseInt(dateChunks[1]), hours, minutes, 0);
};

// Parameter can be a Date or String.  If string, it's converted to a Date.
rsw.prettyPrintDate = function (date) {            
    if (!(date instanceof Date))
        date = rsw.stringDateToDate(date);

    return rsw.dayOfWeek(date) + ", " + rsw.monthOfYear(date) + " " + date.getDate() + ", " + date.getFullYear();
}

rsw.jsonStringDateToDate = function (calendarDate) {
    if (!calendarDate || calendarDate instanceof Date)
        return calendarDate;

    return new Date(parseInt(calendarDate.substr(6)));
}

rsw.jsonPrettyPrintDate = function (jsonDate, hideIndicator) {
    if (jsonDate == "" || jsonDate == null) {
        return "";
    }
    var date = rsw.jsonStringDateToDate(jsonDate);
    var prettyDate = rsw.prettyPrintDate(date);
    var time = rsw.formatTimeForDisplay(date.getHours(), date.getMinutes(), hideIndicator);

    return prettyDate + " " + time;
}

// Parameter can be a Date or int.  If int, it's 1-indexed.  Sunday is day 1.
rsw.dayOfWeek = function (date) {
    return ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][date instanceof Date ? date.getDay() : date - 1];
};

// Parameter can be a Date or int.  If int, it's 1-indexed.
rsw.monthOfYear = function (date) {
    return ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][date instanceof Date ? date.getMonth() : date - 1];
};

// Returns 1-indexed day of week (e.g. 1 would be Monday, 2 Tuesday, etc.) of the first day of the given month. Assumes month is 1-indexed.
rsw.dayOfWeekOfFirstDayOfMonth = function (month, year) {
    var date = new Date(year, month - 1, 1);    
    return date.getDay();
};

// Assumes month is 1-indexed.
rsw.daysInMonth = function (month, year) {
    return new Date(year, month, 0).getDate();
};

// Assumes month is 1-indexed.
rsw.getPreviousMonth = function (month, year) {
    if (month == 1) {
        month = 12;
        --year;
    } else {
        --month;
    }

    return { month: month, year: year };
};

// Assumes month is 1-indexed.
rsw.getNextMonth = function (month, year) {
    if (month == 12) {
        month = 1;
        ++year;
    } else {
        ++month;
    }

    return { month: month, year: year };
};

rsw.formatPhone = function (phone) {
    if (phone.length == 10) {
        return "(" + phone.substring(0, 3) + ") " + phone.substring(3, 6) + "-" + phone.substring(6, 10);
    } else if (phone.length == 11) {
        return phone.substring(0, 1) + " (" + phone.substring(1, 4) + ") " + phone.substring(4, 7) + "-" + phone.substring(7, 11);
    } else {
        // TODO show invalid, or blank
        return "";
    }
}

// Hours should be 0-23.  Any hour > 23 is normalized to 0-23.
rsw.formatTimeForDisplay = function (hour, minute, hideIndicator) {
    minute = minute || 0;
    var indicator = "AM";

    if (hour > 23)
        hour -= 24;

    if (hour == 0) {
        hour = 12;
    } else if (hour >= 12) {
        if (hour > 12)
            hour -= 12;
        indicator = "PM";
    }

    if (minute < 10)
        minute = "0" + minute;

    return hour + ":" + minute + (hideIndicator ? "" : " " + indicator);
};

rsw.padLeft = function (number, character, places) {
    var padded = number + "";
    while (padded.length < places)
        padded = character + padded;

    return padded;
};

// Takes a on object (optional) with name, startDate (Date or string), and endDate (Date or string) fields.  All fields are optional.
rsw.showShiftEditor = function (params) {
    params = params || {};

    // console.log("Showing shift editor for", params);

    var parameterString = "?";

    function formatDateParameter(date) {
        date = rsw.stringDateToDate(date);
        return (date.getMonth() + 1) + "/" + date.getDate() + "/" + date.getFullYear() + " " + date.getHours() + ":" + rsw.padLeft(date.getMinutes(), "0", 2);
    }

    function addParameter(name, value) {
        if (!value || value == "")
            return;

        parameterString += (parameterString != "?" ? "&" : "") + name + "=" + encodeURIComponent(value);
    }

    if (params.name)
        addParameter("name", params.name);

    if (params.startDate)
        addParameter("startDate", formatDateParameter(params.startDate));

    if (params.endDate)
        addParameter("endDate", formatDateParameter(params.endDate));

    if (params.id)
        addParameter("shiftId", params.id);

    //onStart: function () { console.log("loading...");  },

    rsw.showFancybox("/Calendar/ShiftEditor" + parameterString, {
        type: "iframe",
        hideOnContentClick: false,
        //        onStart: function () { $.fancybox.showActivity(); console.log("loading..."); },
        //        onComplete: function () { $.fancybox.hideActivity(); console.log("done..."); },               
        showCloseButton: true,
        transitionIn: "none",
        width: 900,
        height: 500,
        padding: 0,
        scrolling: "no",
        autoScale: false,
        autoDimensions: false
    });
    $.fancybox.showActivity();

    //rsw.scrollToTop("fast");
}

// Convenience method to show a qtip bubble programmatically (you can then hide it with rsw.hideBubble()).
// Useful for when we don't use the normal "show on mouseover, hide on mouseout" behavior.
rsw.showBubble = function (targetElement, html, options) {
    options = options || {};
    options.width = options.width || 300;

    if (typeof options.width === "string")
        options.width = options.width.replace("px", "");

    rsw.hideBubbles();

    var bubble = $(targetElement).qtip({
        content: html,
        show: {
            solo: true,
            delay: 0,
            when: { event: "showBubble" },
            effect: { length: 0 }
        },
        hide: {
            delay: 0,
            when: { event: "hideBubble" },
            effect: { length: 200 }
        },
        position: {
            corner: {
                target: "rightMiddle",
                tooltip: "leftMiddle"
            },
            adjust: {
                screen: true,
                x: 5
            }
        },
        style: {
            width: options.width,
            padding: 0,
            background: "#fff",
            color: "black",
            textAlign: "center",
            border: {
                width: 3,
                radius: 6,
                color: "#505050"
            },
            //tip: "bottomLeft",
            name: "dark" // Inherit the rest of the attributes from the preset dark style
        }
    });

    bubble.trigger("showBubble");

    // Returns a handle to the markup that lives inside the qtip
    return $($(".qtip-content").children().get(0));
};

rsw.attachNotesQtip = function (target, notes) {
    notes = $.trim(notes);
    if ("" === notes)
        return;

    target.qtip({
        content: notes,
        show: "mouseover",
        hide: "mouseout",
        position: {
            corner: {
                target: "rightMiddle",
                tooltip: "leftMiddle"
            },
            adjust: {
                screen: true,
                x: 5
            }
        },
        style: {
            padding: 6,
            background: "#ffc",
            color: "black",
            textAlign: "center",
            border: {
                width: 1,
                radius: 2,
                color: "#505050"
            },
            name: "dark"
        }
    });
};

rsw.hideBubbles = function () {
    $(".qtip.qtip-active").qtip("destroy");
};


// Customer function to pull checkbox value, which needs to be 
// reversed when using jqTransform
rsw.checkBoxValue = function (checkbox) {
    var checked = $(checkbox).prop('checked');
    if ($(checkbox).is(".jqTransformHidden"))
        checked = !checked;
    return checked;
};

rsw.resetOddEven = function (objArray) {
    $.each(objArray, function (index, row) {
        $(row).removeClass('even odd');
        $(row).addClass((index % 2 == 0) ? 'odd' : 'even' );
    });
};

rsw.CALENDAR_DATE_CHANGED_EVENT_NAME = "rsw.calendar.dateChanged";
rsw.DAY_COPY_PASTE_EVENT_NAME = "rsw.calendar.dayCopyPaste";
rsw.DAY_COPY_HIGHLIGHTED_EVENT_NAME = "rsw.calendar.dayCopyHighlighted";
rsw.DAY_COPY_UNHIGHLIGHTED_EVENT_NAME = "rsw.calendar.dayCopyUnhighlighted";
rsw.DAY_COPY_SELECTED_CHANGED_EVENT_NAME = "rsw.calendar.dayCopySelectedChanged";
rsw.DAY_PASTE_HIGHLIGHTED_EVENT_NAME = "rsw.calendar.dayPasteHighlighted";
rsw.DAY_PASTE_UNHIGHLIGHTED_EVENT_NAME = "rsw.calendar.dayPasteUnhighlighted";
rsw.DAY_PASTE_SELECTED_CHANGED_EVENT_NAME = "rsw.calendar.dayPasteSelectedChanged";
rsw.SCHEDULE_PUBLISHED_EVENT_NAME = "rsw.schedule.published";
rsw.TIME_OFF_UPDATED_EVENT_NAME = "rsw.schedule.timeOffUpdated";

rsw.triggerDayCopyHighlightedEvent = function (date) {
    $(document).trigger(rsw.DAY_COPY_HIGHLIGHTED_EVENT_NAME, [date]);
};

rsw.triggerDayCopyUnhighlightedEvent = function (date) {
    $(document).trigger(rsw.DAY_COPY_UNHIGHLIGHTED_EVENT_NAME, [date]);
};

rsw.triggerDayCopySelectedChangedEvent = function (dates) {
    $(document).trigger(rsw.DAY_COPY_SELECTED_CHANGED_EVENT_NAME, [dates]);
};

rsw.triggerDayPasteHighlightedEvent = function (dates) {
    $(document).trigger(rsw.DAY_PASTE_HIGHLIGHTED_EVENT_NAME, [dates]);
};

rsw.triggerDayPasteUnhighlightedEvent = function (dates) {
    $(document).trigger(rsw.DAY_PASTE_UNHIGHLIGHTED_EVENT_NAME, [dates]);
};

rsw.triggerDayPasteSelectedChangedEvent = function (dates) {
    $(document).trigger(rsw.DAY_PASTE_SELECTED_CHANGED_EVENT_NAME, [dates]);
};

rsw.triggerDayCopyPasteEvent = function () {
    $(document).trigger(rsw.DAY_COPY_PASTE_EVENT_NAME);
};

rsw.triggerCalendarDateChangedEvent = function (date) {
    var previousDate = $(document).data("calendar.previousDate");    
    if (previousDate !== date) {
        $(document).data("calendar.previousDate", date);
        $(document).trigger(rsw.CALENDAR_DATE_CHANGED_EVENT_NAME, [rsw.stringDateToDate(date)]);
    }
};

rsw.triggerTimeOffUpdatedEvent = function () {
    $(document).trigger(rsw.TIME_OFF_UPDATED_EVENT_NAME);
};

rsw.showShiftBubble = function (params) {
    rsw.hideBubbles();
    
    var markup = $("#templateMarkup .shiftBubble").clone();
    var bubble = rsw.showBubble(params.target, markup, { width: markup.css("width") });

    bubble.find(".shiftTitle").text(params.shiftName);
    bubble.find(".shiftDate").text(rsw.prettyPrintDate(params.startDate));
    bubble.find(".shiftTime").text(rsw.formatTimeForDisplay(params.startDate.getHours(), params.startDate.getMinutes()) + " - " +
        rsw.formatTimeForDisplay(params.endDate.getHours(), params.endDate.getMinutes()));

    rsw.ajax({
        url: "/Calendar/ShiftOverviewData",
        data: "shiftId=" + params.shiftId,
        onSuccess: function (response) {
            bubble.find(".editLink").click(function () {
                if (params.onBeforeEdit)
                    params.onBeforeEdit();

                rsw.showShiftEditor({
                    id: params.shiftId
                });

                return false;
            });

            bubble.find(".deleteLink").click(function () {
                if (confirm("Really delete this shift?")) {
                    rsw.ajax({
                        url: "/Calendar/DeleteShift",
                        data: "shiftId=" + params.shiftId,
                        onSuccess: function (response) {
                            rsw.hideBubbles();

                            parent.$.noticeAdd({
                                text: "Shift deleted.",
                                type: "success"
                            });

                            if (params.onAfterDelete)
                                params.onAfterDelete();
                        },
                        onError: function (response) {
                            rsw.displayErrorsInNoticeBubble(response);
                        }
                    });
                }

                return false;
            });

            bubble.find(".cancelLink").click(function () {
                if (params.onBeforeCancel)
                    params.onBeforeCancel();

                rsw.hideBubbles();

                return false;
            });

            bubble.find(".messageLink").click(function () {
                rsw.showFancybox("/Messages/CreateMessageForShift?shiftId=" + params.shiftId, {
                    type: "iframe",
                    hideOnContentClick: false,
                    showCloseButton: true,
                    transitionIn: "none",
                    width: 520,
                    height: 500,
                    padding: 0,
                    scrolling: "no",
                    autoScale: false,
                    autoDimensions: false
                });

                return false;
            });

            var shiftOverviewsByJob = response.shiftOverviewsByJob;

            // These counts are 1 indexed because there is always a catchall "all" job.
            var hasZeroJobs = rsw.countKeysInObject(shiftOverviewsByJob) == 1;
            var hasOneJob = rsw.countKeysInObject(shiftOverviewsByJob) == 2;

            $.each(shiftOverviewsByJob, function (jobId, shiftOverviewByJob) {
                // Don't show the "all" job if there's only one job
                if (hasOneJob && jobId == 0)
                    return true;

                var option = $("<option>");
                option.attr("value", jobId);
                option.text(shiftOverviewByJob.jobName);
                bubble.find(".jobs").append(option);
            });

            bubble.find(".jobs").change(function () {
                var shiftOverviewByJob = shiftOverviewsByJob[$(this).val()];
                var openJobs = shiftOverviewByJob.needed - shiftOverviewByJob.has;

                var employeeNames = shiftOverviewByJob.employeeNames;
                if (employeeNames.length == 0)
                    employeeNames = "None";
                else
                    employeeNames = employeeNames.join(", ");

                bubble.find(".peopleNeeded").text(shiftOverviewByJob.needed);
                bubble.find(".peopleScheduled").text(shiftOverviewByJob.has);
                bubble.find(".employeesWorking").text(employeeNames);
                bubble.find(".employeeCost").text(shiftOverviewByJob.cost.toFixed(2));

                bubble.find(".openJobs").text(openJobs);
                if (openJobs == 0)
                    bubble.find(".openJobs").removeClass("nonzeroOpenJobs");
                else
                    bubble.find(".openJobs").addClass("nonzeroOpenJobs");
            });

            bubble.find(".jobs").trigger("change");

            if (!hasZeroJobs) {
                bubble.find(".jobSelectContainer").show();
            }

            var note = $.trim(response.shiftNote);
            if (note !== "") {
                bubble.find(".noteIcon").show();
                rsw.attachNotesQtip(bubble.find(".noteIcon"), note);
            }

            bubble.find(".loadingDisplay").hide();
            bubble.find(".shiftContent").show();
        }
    });
};

rsw.showUnconfirmedShiftsBubble = function (params) {
    rsw.hideBubbles();
    var markup = $("#templateMarkup .unconfirmedShiftsBubble").clone();
    var bubble = rsw.showBubble(params.target, markup, { width: markup.css("width") });

    bubble.find(".unconfirmedShiftsDate").text(rsw.prettyPrintDate(params.date));

    rsw.ajax({
        url: "/Calendar/UnconfirmedShiftsData",
        data: "date=" + rsw.dateToStringWithoutTime(params.date),
        onSuccess: function (response) {
            bubble.find(".sendRemindersLink").click(function () {
                var employeeIds = [];
                $.each(response.unconfirmedEmployees, function (i, employee) {
                    employeeIds.push(employee.id);
                });

                rsw.ajax({
                    url: "/Messages/SendShiftReminder",
                    data: {
                        employeeIds: employeeIds.join(",")
                    },
                    onSuccess: function (response) {
                        var employeeNames = [];

                        $.each(response.employees, function (i, employee) {
                            employeeNames.push(employee.name);
                        });

                        bubble.find(".cancelLink").click();

                        jQuery.noticeAdd({
                            text: employeeNames.length == 0
                                ? "Did not send a reminder message. Make sure the employees you're sending the reminder to are not inactivated and try again."
                                : "A reminder message is now being sent to these employees:\n" + employeeNames.join(", ")
                        });                        
                    }
                })

                return false;
            });

            bubble.find(".cancelLink").click(function () {
                if (params.onBeforeCancel)
                    params.onBeforeCancel();

                rsw.hideBubbles();
                return false;
            });

            var employeesListForDisplay = [];

            $.each(response.unconfirmedEmployees, function (i, employee) {
                var shifts = [];
                $.each(employee.shifts, function (j, shift) {
                    shifts.push(shift.name);
                });

                employeesListForDisplay.push(employee.name + " (" + shifts.sort().join(", ") + ")");
            });

            employeesListForDisplay.sort();

            $.each(employeesListForDisplay, function (i, employee) {
                var li = $("<li>");
                li.text(employee);
                bubble.find(".unconfirmedShiftsList").append(li);
            });

            bubble.find(".loadingDisplay").hide();
            bubble.find(".unconfirmedContent").show();
        }
    });
};

rsw.showShiftTradesBubble = function (params) {
    rsw.hideBubbles();

    rsw.ajax({
        url: "/Calendar/ShiftTradesData",
        data: "date=" + rsw.dateToStringWithoutTime(params.date),
        onSuccess: function (response) {
            // If no shift trades for the day, display an error message.
            // If there's only one, show the shift trade window right away.
            // If there are more than one, show a bubble with a clickable list of shift trades.
            if (response.shiftTrades.length == 0) {
                alert("There are no shift trade requests for today. Please reload this page to see the latest data.");
                if (params.onBeforeCancel)
                    params.onBeforeCancel();
            } else if (response.shiftTrades.length == 1) {
                var shiftTrade = response.shiftTrades[0];
                showShiftTradeFancybox(shiftTrade.scheduleShiftJobId, shiftTrade.shiftTradeId);
                if (params.onBeforeCancel)
                    params.onBeforeCancel();
            } else {
                var markup = $("#templateMarkup .shiftTradesBubble").clone();
                var bubble = rsw.showBubble(params.target, markup, { width: markup.css("width") });

                bubble.find(".shiftTradesDate").text(rsw.prettyPrintDate(params.date));

                $.each(response.shiftTrades, function(i, shiftTrade) {
                    var li = $("<li><a href='javascript://'></a> (<span></span>)</li>");
                    li.find("span").text(shiftTrade.shiftName + ": " + shiftTrade.jobName);
                    li.find("a").text(shiftTrade.employeeName).click(function() {
                        showShiftTradeFancybox(shiftTrade.scheduleShiftJobId, shiftTrade.shiftTradeId);
                        return false;
                    });
                    bubble.find(".shiftTradesList").append(li);
                });                

                bubble.find(".cancelLink").click(function () {
                    if (params.onBeforeCancel)
                        params.onBeforeCancel();

                    rsw.hideBubbles();
                    return false;
                });                                
            }
        }
    });

    function showShiftTradeFancybox(scheduleShiftJobId, shiftTradeId) {
        rsw.showFancybox("/Messages/ManageShiftTrade?scheduleShiftJobId=" + scheduleShiftJobId + "&shiftTradeId=" + shiftTradeId, {
            type: "iframe",
            hideOnContentClick: false,
            showCloseButton: true,
            transitionIn: "none",
            width: 900,
            height: 500,
            padding: 0,
            scrolling: "no",
            autoScale: false,
            autoDimensions: false
        });
    }
};

rsw.showTimeOffRequestsBubble = function (params) {
    rsw.hideBubbles();
    var markup = $("#templateMarkup .timeOffRequestsBubble").clone();
    var bubble = rsw.showBubble(params.target, markup, { width: markup.css("width") });

    bubble.find(".timeOffRequestsDate").text(rsw.prettyPrintDate(params.date));

    fetchAndDisplayTimeOffData();

    function fetchAndDisplayTimeOffData() {
        bubble.find(".timeOffContent").hide();
        bubble.find(".loadingDisplay").show();

        rsw.ajax({
            url: "/Calendar/TimeOffData",
            data: "date=" + rsw.dateToStringWithoutTime(params.date),
            onSuccess: function (response) {
                bubble.find(".noRequests").hide();
                bubble.find(".pendingRequests").hide();
                bubble.find(".approvedRequests").hide();
                bubble.find(".deniedRequests").hide();
                bubble.find(".pendingRequestList").empty();
                bubble.find(".approvedRequestList").empty();
                bubble.find(".deniedRequestList").empty();

                displayTimeoffs({
                    requests: response.newRequests,
                    listItem: ".pendingTimeOffItem",
                    requestList: ".pendingRequestList",
                    markupToShow: ".pendingRequests"
                });

                displayTimeoffs({
                    requests: response.approvedRequests,
                    listItem: ".approvedTimeOffItem",
                    requestList: ".approvedRequestList",
                    markupToShow: ".approvedRequests"
                });

                displayTimeoffs({
                    requests: response.rejectedRequests,
                    listItem: ".deniedTimeOffItem",
                    requestList: ".deniedRequestList",
                    markupToShow: ".deniedRequests"
                });

                if (response.newRequests.length == 0 && response.approvedRequests.length == 0 && response.rejectedRequests.length == 0)
                    bubble.find(".noRequests").show();

                function displayTimeoffs(params) {
                    if (params.requests.length > 0) {
                        $.each(params.requests, function (i, timeOff) {
                            var li = $("#templateMarkup " + params.listItem).clone();
                            li.find(".timeOffName").text(timeOff.employeeName);
                            li.find(".timeOffDescription").text(timeOff.description.replace('-', '- \r\n'));
                            li.find(".approveTimeOffLink").click(function () {
                                updateTimeoff(timeOff.id, true);
                                return false;
                            });
                            li.find(".denyTimeOffLink").click(function () {
                                updateTimeoff(timeOff.id, false);
                                return false;
                            });

                            bubble.find(params.requestList).append(li);
                        });

                        bubble.find(params.markupToShow).show();
                    }
                }

                function updateTimeoff(timeOffId, approve) {
                    if (timeOffId == null || "" == timeOffId) {
                        $.noticeAdd({
                            text: ("Error requesting time off"),
                            type: "error"
                        });
                        return;
                    }
                    rsw.ajax({
                        url: "/Messages/UpdateTimeOff",
                        data: {
                            timeOffId: timeOffId,
                            approve: approve,
                            cancel: false
                        },
                        onSuccess: function (response) {
                            if (response.hasConflict) {
                                rsw.showFancybox("/Messages/TimeOffConflictConfirmation?timeOffId=" + timeOffId, {
                                    type: "iframe",
                                    hideOnContentClick: false,
                                    showCloseButton: true,
                                    transitionIn: "none",
                                    width: 500,
                                    height: 240,
                                    padding: 0,
                                    scrolling: "no",
                                    'autoScale': false,
                                    'autoDimensions': false
                                });
                                return false;
                            }
                            rsw.triggerTimeOffUpdatedEvent();
                            fetchAndDisplayTimeOffData();
                        },
                        onError: function (response) {
                            rsw.displayErrorsInNoticeBubble(response);
                        }
                    });
                }

                bubble.find(".cancelLink").click(function () {
                    if (params.onBeforeCancel)
                        params.onBeforeCancel();

                    rsw.hideBubbles();
                    return false;
                });

                bubble.find(".loadingDisplay").hide();
                bubble.find(".timeOffContent").show();
            }
        });
    }
};

rsw.showTimeOffBlackoutsBubble = function (params) {
    rsw.hideBubbles();
    var markup = $("#templateMarkup .timeOffBlackoutsBubble").clone();
    var bubble = rsw.showBubble(params.target, markup, { width: markup.css("width") });
    var sender = params.sender;

    bubble.find(".timeOffBlackoutDate").text(rsw.prettyPrintDate(params.date));
    bubble.find(".timeOffBlackoutCancel").hide();
    bubble.find(".loadingDisplay").show();

    rsw.ajax({
        url: "/Calendar/TimeOffBlackoutData",
        data: "date=" + rsw.dateToStringWithoutTime(params.date),
        onSuccess: function (response) {
            bubble.find(".timeOffBlackoutCancel").show();
            bubble.find(".loadingDisplay").hide();

            bubble.find(".cancelLink").click(function () {
                if (params.onBeforeCancel)
                    params.onBeforeCancel();

                rsw.hideBubbles();
                return false;
            });

            bubble.find(".removeBlackoutLink").click(function () {
                removeTimeoffBlackout(response.timeOffBlackoutId, false);
                return false;
            });

            function removeTimeoffBlackout(blackoutId) {
                if (blackoutId == null || "" == blackoutId) {
                    $.noticeAdd({
                        text: ("Error removing time off blackout"),
                        type: "error"
                    });
                    return;
                }
                rsw.ajax({
                    url: "/Messages/RemoveTimeOffBlackout",
                    data: {
                        blackoutId: blackoutId
                    },
                    onSuccess: function (response) {
                        if (sender == "month")
                            updateCalendar();
                        else
                            calendar.reload();
                        $.noticeAdd({
                            text: ("Blackout successfully removed."),
                            type: "success"
                        });
                        return;
                    },
                    onError: function (response) {
                        rsw.displayErrorsInNoticeBubble(response);
                    }
                });
            }
        }
    });
};

rsw.showHappyBirthdayBubble = function (params) {
    rsw.hideBubbles();

    var markup = $("#templateMarkup .happyBirthdayBubble").clone();
    var bubble = rsw.showBubble(params.target, markup, { width: markup.css("width") });
    bubble.find(".happyBirthdayDate").text(rsw.prettyPrintDate(params.date));

    bubble.find(".cancelLink").click(function () {
        rsw.hideBubbles();
        return false;
    });

    rsw.ajax({
        url: "/Calendar/GetBirthdayData",
        data: "date=" + rsw.dateToStringWithoutTime(params.date),
        onSuccess: function (response) {
            $.each(response.EmployeeData, function (i, eData) {
                var li = $("<li><span class='employeeName'></span>\'s <strong><span class='birthdayAnniversary'></span></strong> </li>");
                li.find(".employeeName").text(eData.employeeName);
                li.find(".birthdayAnniversary").text(eData.birthdayAnniversary);
                bubble.find(".individualBirthdays").append(li);
            });

            bubble.find(".loadingDisplay").hide();
            bubble.find(".happyBirthdayInfo").show();
        }
    });
};


rsw.showUserClaimedTradeBubble = function (params) {
    rsw.hideBubbles();

    var markup = $("#templateMarkup .userClaimedTradeBubble").clone();
    var bubble = rsw.showBubble(params.target, markup, { width: markup.css("width") });

    bubble.find(".cancelLink").click(function () {
        rsw.hideBubbles();
        return false;
    });

    rsw.ajax({
        url: "/User/ClaimedShiftsData",
        data: "date=" + rsw.dateToStringWithoutTime(params.date),
        onSuccess: function (response) {
            $.each(response.shifts, function (i, shift) {
                var li = $("<li><span class='claimedJobName'></span> <span class='claimedShiftDate'></span><br/>Picked up by <span class='claimedBy'></span></li>");
                li.find(".claimedJobName").text(shift.jobName);
                li.find(".claimedShiftDate").text(shift.startDate + " - " + shift.endDate);
                li.find(".claimedBy").text(shift.claimedBy);
                bubble.find(".claimedTradeRequests").append(li);
            });

            bubble.find(".loadingDisplay").hide();
            bubble.find(".shiftContent").show();
        }
    });
};

rsw.showUserTimeOffBlackoutBubble = function (params) {
    rsw.hideBubbles();

    var markup = $("#templateMarkup .userTimeOffBlackoutBubble").clone();
    var bubble = rsw.showBubble(params.target, markup, { width: markup.css("width") });

    bubble.find(".timeOffBlackoutDate").text(rsw.prettyPrintDate(params.date));
    bubble.find(".cancelLink").click(function () {
        rsw.hideBubbles();
        return false;
    });
}

rsw.showUserUnclaimedTradeBubble = function (params) {
    rsw.hideBubbles();

    var markup = $("#templateMarkup .userUnclaimedTradeBubble").clone();
    var bubble = rsw.showBubble(params.target, markup, { width: markup.css("width") });

    bubble.find(".cancelLink").click(function () {
        rsw.hideBubbles();
        return false;
    });

    rsw.ajax({
        url: "/User/UnclaimedShiftsData",
        data: "date=" + rsw.dateToStringWithoutTime(params.date),
        onSuccess: function (response) {
            $.each(response.shifts, function (i, shift) {
                var li = $("<li><span class='unclaimedJobName'></span> <span class='unclaimedShiftDate'></span></li>");
                li.find(".unclaimedJobName").text(shift.jobName);
                li.find(".unclaimedShiftDate").text(shift.startDate + " - " + shift.endDate);
                bubble.find(".unclaimedTradeRequests").append(li);
            });

            bubble.find(".loadingDisplay").hide();
            bubble.find(".shiftContent").show();
        }
    });
};

rsw.showUserOpenShiftsBubble = function (params) {
    rsw.hideBubbles();

    var markup = $("#templateMarkup .userOpenShiftsBubble").clone();
    var bubble = rsw.showBubble(params.target, markup, { width: markup.css("width") });

    bubble.find(".cancelLink").click(function () {
        rsw.hideBubbles();
        return false;
    });

    rsw.ajax({
        url: "/User/OpenShiftsData",
        data: "date=" + rsw.dateToStringWithoutTime(params.date),
        onSuccess: function (response) {
            var shiftsById = {};
            var requestShiftLink = bubble.find(".requestShiftLink");
            var alreadyRequestedShiftMessage = bubble.find(".alreadyRequestedShiftMessage");
            var requestShiftMessage = bubble.find(".requestShiftMessage");

            $.each(response.shifts, function (i, shift) {
                var option = $("<option>");
                option.text(shift.position);
                option.attr("value", shift.id);

                if (i == 0)
                    option.attr("selected", "selected");

                bubble.find(".openShifts").append(option);

                shiftsById[shift.id] = shift;
            });


            if (response.shifts.length > 1) {
                bubble.find(".openShifts").show();
            }

            bubble.find(".openShifts").change(function () {
                var shift = shiftsById[$(this).val()];
                bubble.find(".openShiftStartTime").text(shift.startTime);
                bubble.find(".openShiftEndTime").text(shift.endTime);
                bubble.find(".openShiftPosition").text(shift.position);
                bubble.find(".openShiftName").text(shift.name);

                if (shift.alreadyRequested) {
                    requestShiftLink.hide();
                    alreadyRequestedShiftMessage.show();
                } else {
                    alreadyRequestedShiftMessage.hide();
                    requestShiftLink.show();
                }
            });

            // Programmatically select the first item in the dropdown
            bubble.find(".openShifts").trigger("change");

            requestShiftLink.click(function () {
                requestShiftLink.hide();
                requestShiftMessage.show();

                var shift = shiftsById[bubble.find(".openShifts").val()];

                rsw.ajax({
                    url: "/User/SendOpenShiftRequestMessage",
                    data: "scheduleShiftJobId=" + shift.scheduleShiftJobId,
                    onSuccess: function (response) {
                        rsw.hideBubbles();
                        if (!response.approvalRequired) {
                        $.noticeAdd({
                                text: "Congratulations! You've picked up this shift!",
                                type: "success"
                            });
                            setTimeout(rsw.reload(), 1500);
                        } else {
                            $.noticeAdd({
                            text: "OK, sent a request to take this shift. You'll be notified if your request is approved.",
                            type: "success"
                        });
                        }
                        
                    },
                    onError: function (response) {
                        requestShiftMessage.hide();
                        requestShiftLink.show();
                        rsw.displayErrorsInNoticeBubble(response);
                    }
                });

                return false;
            });

            bubble.find(".sendAMessageLink").click(function () {
                var shift = shiftsById[bubble.find(".openShifts").val()];
                rsw.showFancybox("/User/CreateMessage?scheduleShiftJobId=" + shift.scheduleShiftJobId, {
                    type: "iframe",
                    hideOnContentClick: false,
                    showCloseButton: true,
                    transitionIn: "none",
                    width: 500,
                    height: 525,
                    padding: 0,
                    scrolling: "no",
                    autoScale: false,
                    autoDimensions: false
                });
                return false;
            });

            bubble.find(".loadingDisplay").hide();
            bubble.find(".shiftContent").show();
        }
    });
};

rsw.showUserTimeOffBubble = function (params) {
    rsw.hideBubbles();
    var markup = $("#templateMarkup .userTimeOffBubble").clone();
    var bubble = rsw.showBubble(params.target, markup, { width: markup.css("width") });

    bubble.find(".cancelLink").click(function () {
        rsw.hideBubbles();
        return false;
    });

    bubble.find(".cancelTimeOffRequestLink").live('click', function () {
        var timeOffId = $(this).attr('timeoffid');
        rsw.showFancybox("/User/CancelTimeOff?timeOffId=" + timeOffId, {
            type: "iframe",
            hideOnContentClick: true,
            showCloseButton: true,
            transitionIn: "none",
            width: 400,
            height: 280,
            padding: 0,
            scrolling: "no",
            autoScale: false,
            autoDimensions: false
        });
        return false;
    });

    rsw.ajax({
        url: "/User/TimeOffData",
        data: "date=" + rsw.dateToStringWithoutTime(params.date),
        onSuccess: function (response) {
            $.each(response.timeOffs, function (i, timeOff) {
                var li = $("<li><span class='timeOffStatusDescription'></span>: <span class='timeOffDescription'></span>");
                if (!timeOff.denied)
                    li.append(" <a class='cancelTimeOffRequestLink' timeOffId='" + timeOff.timeOffId + "' href='#'>Cancel This Request</a></li>");
                li.find(".timeOffStatusDescription").text(timeOff.statusDescription);
                li.find(".timeOffDescription").text(timeOff.description);
                bubble.find(".timeOffs").append(li);
            });

            bubble.find(".loadingDisplay").hide();
            bubble.find(".shiftContent").show();
        }
    });
}

rsw.showUserShiftBubble = function (params) {
    rsw.hideBubbles();
    
    var markup = $("#templateMarkup .userShiftBubble").clone();
    var bubble = rsw.showBubble(params.target, markup, { width: markup.css("width") });

    rsw.ajax({
        url: "/User/ShiftOverviewData",
        data: "shiftId=" + params.shiftId,
        onSuccess: function (response) {
            bubble.find(".shiftTitle").text(response.jobName);
            bubble.find(".employeeShiftName").text(params.shiftName);
            bubble.find(".shiftDate").text(rsw.prettyPrintDate(params.startDate));
            bubble.find(".shiftTime").text(rsw.formatTimeForDisplay(params.startDate.getHours(), params.startDate.getMinutes()) + " - " +
        rsw.formatTimeForDisplay(params.endDate.getHours(), params.endDate.getMinutes()));

            bubble.find(".cancelLink").click(function () {
                if (params.onBeforeCancel)
                    params.onBeforeCancel();

                rsw.hideBubbles();

                return false;
            });

            if (response.allowsShiftTrades) {
                bubble.find(".requestShiftTradeLink").click(function () {
                    rsw.showFancybox("/User/CreateShiftTrade?shiftId=" + params.shiftId, {
                        type: "iframe",
                        hideOnContentClick: false,
                        showCloseButton: true,
                        transitionIn: "none",
                        width: 640,
                        height: 450,
                        padding: 0,
                        scrolling: "no",
                        autoScale: false,
                        autoDimensions: false
                    });
                    return false;
                });
            } else {
                bubble.find(".requestShiftTradeLink").hide();
            }

            bubble.find(".whoElseIsWorkingLink").click(function () {
                rsw.showFancybox("/User/WhoElseIsWorking?shiftId=" + params.shiftId, {
                    type: "iframe",
                    hideOnContentClick: true,
                    showCloseButton: true,
                    transitionIn: "none",
                    width: 520,
                    height: 400,
                    padding: 0,
                    scrolling: "no",
                    autoScale: false,
                    autoDimensions: false
                });
                return false;
            });

            bubble.find(".loadingDisplay").hide();
            bubble.find(".shiftContent").show();
        }
    });
};


// The "params" arg is a dictionary of named parameters:
//   target: jQuery selector of the input type='text' that you want to autocomplete on
//   data: array of objects of this format:
//      id: the ID of the element (not necessary for 'everyone' type)
//      type: the type of the element (may be 'job', 'employee', or 'everyone')
//      label: user-friendly name of the element (job name, employee name, or 'everyone')
//   onSelected: optional callback function that takes a single dictionary parameter of this format:
//      id: the ID of the selected element (not present if 'everyone' type selected)
//      type: the type of the selected element (may be 'job', 'employee', or 'everyone')
//      label: user-friendly name of the selected element (job name, employee name, or 'everyone')
//   defaultTo: optional dictionary with 'id' and 'type' params.  If you specify this,
//      the autocompleter will immediately pick/display the corresponding item.
rsw.createMessagingAutocompleter = function (params) {
    // Sort the data such that "Everyone" is the first option,
    // each job in alphabetical order comes next, then
    // employees in alphabetical order follow.
    params.data.sort(function (item1, item2) {
        if (item1.type == "everyone" && item2.type != "everyone")
            return -1;
        if (item1.type != "everyone" && item2.type == "everyone")
            return 1;
        if (item1.type == "job" && item2.type == "employee")
            return -1;
        if (item1.type == "employee" && item2.type == "job")
            return 1;

        if (item1.type == item2.type) {
            if (item1.label < item2.label)
                return -1;
            if (item1.label > item2.label)
                return 1;
            return 0;
        }
    });
    return rsw.createGenericAutocompleter(params);
};    

rsw.createGenericAutocompleter = function (params) {

    if (params.selectFirst == undefined)
        params.selectFirst = true

    if (params.AllowUserText == undefined)
        params.AllowUserText = false

    var autocompleter = $(params.target).autocomplete({
        minLength: 0,
        delay: 0,
        source: params.data,
        select: function (event, ui) {
            fireSelectEvent(ui.item);

            // Obnoxious...for IE because it doesn't close the autocompleter if you select with the mouse.
            setTimeout(function () {
                autocompleter.autocomplete("close");
            }, 10);
        },
        change: function (event, ui) {
            var textInAutocompleter = $.trim(autocompleter.val());
            var previousAutocompletedValue = rsw.getAutocompleterValue(params.target);

            // If no valid value selected, remove it from the dropdown
            if (!params.AllowUserText && (!previousAutocompletedValue || (previousAutocompletedValue.label != textInAutocompleter))) {
                rsw.setAutocompleterValue(params.target, "");
                $(params.target).val("");

                if (params.onChangedToInvalidSelection)
                    params.onChangedToInvalidSelection();
            }
        },
        create: function (event, ui) {
            if (params.selectFirst && params.data.length == 1) {
                $(params.target).val(params.data[0].label);
                fireSelectEvent(params.data[0]);
            }
        }
    });

    // Drop down the whole list automatically when the input is focused,
    // but only if there's more than 1 item in the datasource
    if (params.data.length > 1)
        $(params.target).focus(function () {
            autocompleter.autocomplete("search");
        });

    if (params.defaultTo) {
        var defaultItem;

        $.each(params.data, function (i, item) {
            if (item.id == params.defaultTo.id && item.type == params.defaultTo.type) {
                defaultItem = item;
                return false;
            }
        });

        if (defaultItem) {
            $(params.target).val(defaultItem.label);
            fireSelectEvent(defaultItem);
        }
    }

    function fireSelectEvent(item) {
        rsw.setAutocompleterValue(params.target, item);
        if (params.onSelected)
            params.onSelected(item);
    }

    return autocompleter;
};

rsw.setAutocompleterValue = function (target, item) {
    $(target).data("rsw.autocompleterValue", item);
};

rsw.getAutocompleterValue = function (target) {
    return $(target).data("rsw.autocompleterValue");
};


rsw.scrollToTop = function (duration) {
    rsw.scrollTo(0, duration);
};

rsw.scrollTo = function (scrollTop, duration) {
    scrollTop = scrollTop || 0;

    if (duration == undefined)
        duration = "slow";

    $("html:not(:animated), body:not(:animated)").animate({ scrollTop: scrollTop }, duration);
};

rsw.normalizeCurrencyString = function (string) {
    string = $.trim(string);    

    if (string.match(/^0+$/))
        return "0";

    return $.trim(string.replace(/^0+/g, "").replace(/\$/g, "").replace(/\,/g, ""));
};

rsw.containsOnlyNumbers = function (string) {
    return string.match(/^[0-9]*$/g);
};

rsw.isDecimalNumber = function (string) {
    var reg = new RegExp("^[-]?[0-9]+[\.]?[0-9]*$");
    return reg.test(string)
};

rsw.removeNonNumericCharacters = function (string) {
    return string.replace(/[^0-9]/g, "");
};

rsw.isValidCurrencyString = function(string) {
    if(!string)
        return false;

    string = rsw.normalizeCurrencyString(string);
    return string.match(/^[0-9]+[\.]?$/g) || string.match(/^[0-9]+\.[0-9]$/g) || string.match(/^[0-9]+\.[0-9][0-9]$/g)
        || string.match(/^\.[0-9]$/g) || string.match(/^\.[0-9][0-9]$/g);
}

// If there are any "publishing"-state shifts, poll the server so we can let the user know when publishing has finished.
rsw.startSchedulePublishFinishedPoller = function (params) {
    var busy = false;

    setInterval(function () {
        if (busy)
            return;        

        busy = true;

        var hasPublishingShifts = params.findHasPublishingShifts();
        var startDate = params.findStartDate();
        var endDate = params.findEndDate();

        if (hasPublishingShifts) {
            $.ajax({
                url: "/Calendar/PublishingShiftCountForDateRangePoller",
                data: {
                    startDate: startDate,
                    endDate: endDate
                },
                success: function (response) {
                    // Make sure we're on the same page by comparing startDate when the call was made to startDate for current view
                    if (response.publishingShiftsCount == 0 && startDate == params.findStartDate()) {
                        params.removePublishingShiftDisplay();
                        $.noticeAdd({
                            text: "Your schedule has finished publishing.",
                            type: "success"
                        });
                    }

                    busy = false;
                },
                error: function () {
                    // swallow any exceptions
                    busy = false;
                }
            });
        } else {
            busy = false;
        }
    }, 4000);
};

rsw.createDatePicker = function (input, firstDayOfWeek, options) {
    Date.firstDayOfWeek = firstDayOfWeek;
    Date.format = "m/d/yyyy";

    input = $(input);

    options = options || {};
    options.startDate = options.startDate || "1/1/1970";
    options.clickInput = options.clickInput || true;
    options.verticalOffset = options.verticalOffset || 42;
    options.horizontalOffset = options.horizontalOffset || -6;

    var datePicker = input.datePicker(options);

    // Find the calendar icon that gets created and have clicks on it trigger the calendar.
    // This overrides the library's default behavior where the calendar would appear right under the icon
    // if you clicked the icon, which is nasty.
    var calendarIcon = $(input.nextAll(".dp-choose-date")[0]);
    calendarIcon.click(function () {
        input.click();
        return false;
    });

    return datePicker;
};

// Simple function to calculate time difference between 2 Javascript date objects. 
// Returns an object which you can get hours, minutes, days, etc
rsw.getTimeDifference = function (earlierDate, laterDate) {
    var nTotalDiff = laterDate.getTime() - earlierDate.getTime();
    var oDiff = new Object();

    oDiff.days = Math.floor(nTotalDiff / 1000 / 60 / 60 / 24);
    nTotalDiff -= oDiff.days * 1000 * 60 * 60 * 24;

    oDiff.hours = Math.floor(nTotalDiff / 1000 / 60 / 60);
    nTotalDiff -= oDiff.hours * 1000 * 60 * 60;

    oDiff.minutes = Math.floor(nTotalDiff / 1000 / 60);
    nTotalDiff -= oDiff.minutes * 1000 * 60;

    oDiff.seconds = Math.floor(nTotalDiff / 1000);

    return oDiff;
};

 // Helper to parse times like "10:00 PM" into js date objects
rsw.parseTime = function (timeString) {
    if (timeString == '') return null;
    var d = new Date();
    var time = timeString.match(/([12]?\d)(\d{2})\s*(?:a?)(p?)/i);
    if (!time) {
        time = timeString.match(/(\d+)(?::(\d\d))?\s*(p?)/i);
    }
    // handle 12 am
    var hours = parseInt(time[1], 10);
    if (hours == 12 && !time[3]) {
        hours = 0;
    }
    d.setHours(hours + ((hours < 12 && time[3]) ? 12 : 0));
    d.setMinutes(parseInt(time[2], 10) || 0);
    d.setSeconds(0, 0);
    return d;
}

$(function () {
    $('.settingsLink').click(function () {
        $('#accountSettings').slideToggle('fast', function () { });
    });
});

var mouse_is_inside = false;

$(function () {
    $('#accountSettings, .settingsLink').hover(function () {
        mouse_is_inside = true;
    }, function () {
        mouse_is_inside = false;
    });

    $(document).mouseup(function () {
        if (!mouse_is_inside) $('#accountSettings').slideUp('fast');
    });
});
