/* Minification failed. Returning unminified contents.
(823,59-60): run-time error JS1195: Expected expression: >
(824,70-71): run-time error JS1195: Expected expression: >
(826,30-31): run-time error JS1195: Expected expression: )
(828,26-27): run-time error JS1006: Expected ')': ;
(831,21-22): run-time error JS1002: Syntax error: }
(833,54-55): run-time error JS1195: Expected expression: >
(835,25-26): run-time error JS1002: Syntax error: }
(839,44-45): run-time error JS1195: Expected expression: )
(839,46-47): run-time error JS1004: Expected ';': {
(1097,14-15): run-time error JS1195: Expected expression: ,
(1098,50-51): run-time error JS1004: Expected ';': {
(1105,58-59): run-time error JS1195: Expected expression: >
(1105,95-96): run-time error JS1004: Expected ';': )
(1107,17-18): run-time error JS1002: Syntax error: }
(1109,37-38): run-time error JS1004: Expected ';': {
(1113,50-51): run-time error JS1195: Expected expression: >
(1113,80-81): run-time error JS1004: Expected ';': )
(1114,19-23): run-time error JS1034: Unmatched 'else'; no 'if' defined: else
(1121,50-51): run-time error JS1195: Expected expression: >
(1121,81-82): run-time error JS1004: Expected ';': )
(1130,39-40): run-time error JS1004: Expected ';': {
(1184,5-6): run-time error JS1002: Syntax error: }
(1184,7-8): run-time error JS1197: Too many errors. The file might not be a JavaScript file: )
(1183,9-23): run-time error JS1018: 'return' statement outside of function: return exports
(1131,13,1133,61): run-time error JS1018: 'return' statement outside of function: return (s)
                ? this.before(s).remove()
                : $("<p>").append(this.eq(0).clone()).html()
(834,29-71): run-time error JS1018: 'return' statement outside of function: return selectNodeIds.indexOf(node.id) >= 0
(833,25-52): run-time error JS1018: 'return' statement outside of function: return allNodes.filter(node
(830,25-68): run-time error JS1018: 'return' statement outside of function: return filterArray(allNodes, selectNodeIds)
 */
Sys.Application.add_load(function() {
    $(function() {
        $('[data-toggle="tooltip"]').tooltip();
    });

    stickyTableHeaderInit()
});

$(document).on('enabledStickiness.stickyTableHeaders', function(event) {
    $(window).on('resize', function(event) {
        stickyTableHeaderInit();

        $('.table-responsive').each(stickyRepaint);
    });
    var offsetLeft = $(event.target).offset().left;
    var twidth = $(event.target).parent('.table-responsive').width();
    $(event.target)
        .find('.tableFloatingHeaderOriginal')
        .css({ left: offsetLeft, right: $(window).width() - offsetLeft - twidth, clip: 'rect(0px, ' + (twidth + 2) + 'px, 300px, 0px)' });

    $(event.target)
        .parent('.table-responsive')
        .scroll(stickyRepaint);
});

function stickyTableHeaderInit() {
    var stickyHeaders = document.querySelector('.js-sticky-headers');

    if (stickyHeaders) {
        var navbarFixedTop = document.querySelector('.navbar-fixed-top');

        $(stickyHeaders).each(function() {
            var table = $(this);
            if (table.parents('.page-fullscreen-capable').length || table.parents('.is-fullscreen').length) {
                table.stickyTableHeaders({scrollableArea: table.parents('.panel-block')});
            } else {
                if ($(navbarFixedTop).length && $(navbarFixedTop).css('position') === 'fixed') {
                    table.stickyTableHeaders({ fixedOffset: $(navbarFixedTop) });
                } else {
                    table.stickyTableHeaders();
                }
            }
        });
    }
}

function stickyRepaint() {
    var twidth = $(this).width();
    var scroll = $(this).scrollLeft();
    var offsetLeft = $(this).offset().left;
    $('.tableFloatingHeaderOriginal').css({
        left: offsetLeft - scroll,
        clip: 'rect(0px, ' + (scroll + twidth) + 'px, 300px, ' + scroll + 'px)'
    });
}
;
(function ($) {
    window.Rock = window.Rock || {};

    Rock.dialogs = (function () {
        var _dialogs = {},
            exports = {
                // Presents a bootstrap style alert box with the specified message
                // then executes the callback function(result)
                alert: function (msg) {
                    bootbox.dialog({
                        message: msg,
                        buttons: {
                            ok: {
                                label: 'OK',
                                className: 'btn-primary'
                            }
                        }
                    });
                },

                // Presents a bootstrap style alert box with the specified message
                // then executes the callback function(result)
                confirm: function (msg, callback) {
                    bootbox.dialog({
                        message: msg,
                        buttons: {
                            ok: {
                                label: 'OK',
                                className: 'btn-primary',
                                callback: function () {
                                    callback(true);
                                }
                            },
                            cancel: {
                                label: 'Cancel',
                                className: 'btn-default',
                                callback: function () {
                                    callback(false);
                                }
                            }
                        }
                    });
                },

                // Presents a bootstrap style alert box which prevents on "Cancel" and continues on "Ok"
                confirmPreventOnCancel: function (e, msg) {
                    // make sure the element that triggered this event isn't disabled
                    if (e.currentTarget && e.currentTarget.disabled) {
                        return false;
                    }

                    e.preventDefault();
                    bootbox.dialog({
                        message: msg,
                        buttons: {
                            ok: {
                                label: 'OK',
                                className: 'btn-primary',
                                callback: function () {
                                    var postbackJs = e.target.href ? e.target.href : e.target.parentElement.href;
                                    window.location = postbackJs;
                                }
                            },
                            cancel: {
                                label: 'Cancel',
                                className: 'btn-default'
                            }
                        }
                    });
                },

                // Presents a bootstrap style alert box with a 'Are you sure you want to delete this ...' message
                // Returns true if the user selects OK
                confirmDelete: function (e, nameText, additionalMsg)
                {
                    var msg = 'Are you sure you want to delete this ' + nameText + '?';
                    if (additionalMsg)
                    {
                        msg += ' ' + additionalMsg;
                    }
                    this.confirmPreventOnCancel(e, msg);
                },

                // Updates the modal so that scrolling works
                updateModalScrollBar: function (controlId) {
                    Rock.controls.modal.updateSize(controlId);
                }
            }

        return exports;
    }());
}(jQuery));
;
(function () {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.settings = (function () {
        var _settings = {},
            exports = {
                initialize: function (options) {
                    if (typeof options === 'object') {
                        _settings = options;
                    }
                },
                get: function (key) {
                    return _settings[key];
                },
                insert: function (key, value) {
                    _settings[key] = value;
                },
                remove: function (key) {
                    if (_settings[key]) {
                        delete _settings[key];
                    }
                }
            };
 
        return exports;
    }());
}());;
(function () {
    'use strict';
    window.Rock = window.Rock || {};

    Rock.utility = (function () {
        var _utility = {},
            exports = {

                setContext: function (restController, entityId) {
                    // Get the current block instance object
                    $.ajax({
                        type: 'PUT',
                        url: Rock.settings.get('baseUrl') + 'api/' + restController + '/SetContext/' + entityId,
                        success: function (getData, status, xhr) {
                        },
                        error: function (xhr, status, error) {
                            alert(status + ' [' + error + ']: ' + xhr.responseText);
                        }
                    });
                },
                uuidv4: function () {
                    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                        var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
                        return v.toString(16);
                    });
                },
                dashify: function (str) {
                    return str.replace(/\W+/g, '-').replace(/\-$/, '').toLowerCase();
                }
            };

        return exports;
    }());
}());
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.accountPicker = (function () {
        var AccountPicker = function (options) {
            this.options = options;
            // set a flag so that the picker only auto-scrolls to a selected item once. This prevents it from scrolling at unwanted times
            this.alreadyScrolledToSelected = false;
            this.iScroll = null;
        },
            exports;

        AccountPicker.prototype = {
            constructor: AccountPicker,
            initialize: function () {
                var $control = $('#' + this.options.controlId),
                    $tree = $control.find('.treeview'),
                    treeOptions = {
                        customDataItems: this.options.customDataItems,
                        enhanceForLongLists: this.options.enhanceForLongLists,
                        multiselect: this.options.allowMultiSelect,
                        categorySelection: this.options.allowCategorySelection,
                        categoryPrefix: this.options.categoryPrefix,
                        restUrl: this.options.restUrl,
                        searchRestUrl: this.options.searchRestUrl,
                        restParams: this.options.restParams,
                        expandedIds: this.options.expandedIds,
                        expandedCategoryIds: this.options.expandedCategoryIds,
                        showSelectChildren: this.options.showSelectChildren,
                        id: this.options.startingId
                    },

                    // used server-side on postback to display the selected nodes
                    $hfItemIds = $control.find('.js-item-id-value'),
                    // used server-side on postback to display the expanded nodes
                    $hfExpandedIds = $control.find('.js-initial-item-parent-ids-value'),
                    $hfExpandedCategoryIds = $control.find('.js-expanded-category-ids');

                // Custom mapping override to add features items
                this.options.mapItems = function (arr, treeView) {

                    return $.map(arr, function (item) {

                        var node = {
                            id: item.Guid || item.Id,
                            name: item.Name || item.Title,
                            iconCssClass: item.IconCssClass,
                            parentId: item.ParentId,
                            hasChildren: item.HasChildren,
                            isActive: item.IsActive,
                            countInfo: item.CountInfo,
                            isCategory: item.IsCategory,
                            path: item.Path,
                            totalCount: item.TotalCount
                        };

                        // Custom node properties passed in from the *Picker.cs using the ItemPicker base CustomDataItems property
                        if (treeView.options.customDataItems && treeView.options.customDataItems.length > 0) {
                            treeView.options.customDataItems.forEach(function (dataItem, idx) {
                                if (!node.hasOwnProperty(dataItem)) {
                                    node['' + dataItem.itemKey + ''] = item['' + dataItem.itemValueKey + ''];
                                }
                            });
                        }

                        if (node.parentId === null) {
                            node.parentId = '0';
                        }

                        if (item.Children && typeof item.Children.length === 'number') {

                            // traverse using the _mapArrayDefault in rockTree.js
                            node.children = _mapArrayDefault(item.Children, treeView);
                        }

                        if (node.isCategory) {
                            node.id = treeView.options.categoryPrefix + node.id;
                        }

                        return node;
                    });
                }

                if (typeof this.options.mapItems === 'function') {
                    treeOptions.mapping = {
                        mapData: this.options.mapItems
                    };
                }

                // clean up the tree (in case it was initialized already, but we are rebuilding it)
                var rockTree = $tree.data('rockTree');

                if (rockTree) {
                    rockTree.nodes = [];
                }
                $tree.empty();

                var $scrollContainer = $control.find('.scroll-container .viewport');
                var $scrollIndicator = $control.find('.track');
                this.iScroll = new IScroll($scrollContainer[0], {
                    mouseWheel: true,
                    indicators: {
                        el: $scrollIndicator[0],
                        interactive: true,
                        resize: false,
                        listenY: true,
                        listenX: false,
                    },
                    click: false,
                    preventDefaultException: { tagName: /.*/ }
                });

                // Since some handlers are "live" events, they need to be bound before tree is initialized
                this.initializeEventHandlers();

                if ($hfItemIds.val() && $hfItemIds.val() !== '0') {
                    treeOptions.selectedIds = $hfItemIds.val().split(',');
                }

                if ($hfExpandedIds.val()) {
                    treeOptions.expandedIds = $hfExpandedIds.val().split(',');
                }

                if ($hfExpandedCategoryIds.val()) {
                    treeOptions.expandedCategoryIds = $hfExpandedCategoryIds.val().split(',');
                }

                // Initialize the rockTree and pass the tree options also makes http fetches
                $tree.rockTree(treeOptions);

                $control.find('.picker-preview').hide();
                $control.find('.picker-treeview').hide();

                if (treeOptions.allowMultiSelect) {
                    $control.find('.picker-preview').remove();
                    $control.find('.picker-treeview').remove();
                }

                this.updateScrollbar();
            },
            initializeEventHandlers: function () {
                var self = this,
                    $control = $('#' + this.options.controlId),
                    $spanNames = $control.find('.selected-names'),
                    $hfItemIds = $control.find('.js-item-id-value'),
                    $hfExpandedIds = $control.find('.js-initial-item-parent-ids-value'),
                    $hfItemNames = $control.find('.js-item-name-value'),
                    $searchValueField = $control.find('.js-existing-search-value');

                // Bind tree events
                $control.find('.treeview')
                    .on('rockTree:selected', function (e) {
                        var rockTree = $control.find('.treeview').data('rockTree');

                        if (rockTree.selectedNodes) {
                            var ids = rockTree.selectedNodes.map(function (v) { return v.id }).join(',');
                            $hfItemIds.val(ids);
                        }

                        self.togglePickerElements();
                    })
                    .on('rockTree:itemClicked', function (e, data) {
                        // make sure it doesn't auto-scroll after something has been manually clicked
                        self.alreadyScrolledToSelected = true;
                        if (!self.options.allowMultiSelect) {
                            $control.find('.picker-btn').trigger('click');
                        }
                    })
                    .on('rockTree:expand rockTree:collapse rockTree:dataBound', function (evt, data) {
                        self.updateScrollbar();
                        // Get any node item so we can read to total count
                        const rockTree = $control.find('.treeview').data('rockTree');
                        const firstNode = rockTree.nodes[0];
                        if (firstNode && firstNode.totalCount > 1000) {
                            $control.find('.js-select-all').hide();
                        }

                        if (self.selectAll) {
                            self.toggleSelectAll(rockTree);
                        }
                    })
                    .on('rockTree:rendered', function (evt, data) {
                        var rockTree = $control.find('.treeview').data('rockTree');
                        self.createSearchControl();
                        self.showActiveMenu();
                        self.scrollToSelectedItem();

                        if ($hfItemIds && $hfItemIds.val().length > 0) {
                            var itemIds = $hfItemIds.val().split(',');

                            rockTree.setSelected(itemIds);
                        }

                        self.togglePickerElements();

                        if (self.getViewMode() === 'selecting') {
                            self.setViewMode('clear');
                            $control.find('.picker-btn').trigger('click', [$hfItemIds.val()]);
                        }

                        if (self.getViewMode() === 'selected') {
                            if ($hfItemIds.val() === '0' && !$control.find('.picker-menu').is(':visible') ) {
                                return;
                            }

                            $control.find('.picker-cancel').trigger('click');
                        }
                    })
                    .on('rockTree:fetchCompleted', function (evt, data) {
                        // intentionally empty
                    })

                $control.find('.picker-label').on('click', function (e) {
                    e.preventDefault();
                    $(this).toggleClass("active");
                    $control.find('.picker-menu').first().toggle(0, function () {
                        self.scrollToSelectedItem();
                        $control.find('.js-search-panel input').trigger('focus');
                    });
                });

                $control.find('.picker-cancel').on('click', function () {
                    $(this).toggleClass("active");
                    $control.find('.picker-menu').hide(0, function () {
                        self.updateScrollbar();
                    });
                    $(this).closest('.picker-label').toggleClass("active");

                    //cleanup
                    self.setViewMode('clear');
                    self.setActiveMenu(false);
                    //doPostBack();
                });

                // Preview Selection link click
                $control.find('.picker-preview').on('click', function () {
                    $control.find('.js-search-panel').hide();
                    $control.find('.picker-preview').hide();
                    $control.find('.picker-treeview').show();
                    $control.find('.js-select-all').hide();

                    self.setViewMode('preview');
                    self.togglePickerElements();

                    var rockTree = $control.find('.treeview').data('rockTree');

                    // Get all of the current rendered items so we can revert if user leaves search
                    self.currentTreeView = $control.find('.treeview').html();

                    var $viewport = $control.find('.viewport');

                    if (rockTree.selectedNodes && rockTree.selectedNodes.length > 0) {

                        var listHtml = '';
                        rockTree.selectedNodes.forEach(function (node) {
                            if (!node.path) {
                                node.path = 'Top-Level';
                            }

                            listHtml +=
                                '<div id="preview-item-' + node.id + '" class="d-flex align-items-center preview-item js-preview-item">' +
                                '         <div class="flex-fill">' +
                                '              <span class="text-color d-block">' + node.name + '</span>' +
                                '              <span class="text-muted text-sm">' + node.path.replaceAll('^', '<i class="fa fa-chevron-right pl-1 pr-1" aria-hidden="true"></i>') + '</span>' +
                                '         </div>' +
                                '         <a id="lnk-remove-preview-' + node.id + '" title="Remove From Preview" class="btn btn-link text-color btn-xs btn-square ml-auto js-remove-preview" data-id="' + node.id + '"><i class="fa fa-times"></i></a>' +
                                '</div>';
                        });

                        // Display preview list
                        var listHtmlView = '<div>' +
                            listHtml +
                            '</div>';

                        $control.find('.scroll-container').addClass('scroll-container-native').html(listHtmlView);
                        // Wire up remove event and remove from dataset

                        $control.find('.js-remove-preview').on('click', function (e) {
                            var nodeId = $(this).attr('data-id');

                            $control.find('#preview-item-' + nodeId).remove();

                            var newSelected = rockTree.selectedNodes.filter(function (fNode) {
                                return fNode.id !== nodeId;
                            });

                            rockTree.selectedNodes = newSelected;

                            // After removing a selection we need to update the hf value
                            var newIds = newSelected.map(function (n) { return n.id });

                            if (newIds && newIds.length > 0) {
                                $hfItemIds.val(newIds.join(','));
                            }
                            else {
                                $hfItemIds.val('0');
                            }

                            if ($control.find('.js-preview-item').length === 0) {
                                $control.find('.picker-treeview').trigger('click');
                            }
                        });
                    }
                });

                // Tree View link click
                $control.find('.picker-treeview').on('click', function () {
                    $searchValueField.val('');
                    self.setActiveMenu(true);
                    self.setViewMode('clear');

                    if ($hfItemIds && $hfItemIds.length > 0) {
                        var restUrl = self.options.getParentIdsUrl
                        var nodeIds = $hfItemIds.val().split(',');
                        var restUrlParams = nodeIds.map(function (id) { return 'ids=' + id }).join('&');

                        restUrl = restUrl + '?' + restUrlParams;

                        // Get the ancestor ids so the tree will expand
                        $.getJSON(restUrl, function (data, status) {
                            if (data && status === 'success') {
                                var selectedIds = [];
                                var expandedIds = [];

                                $.each(data, function (key, value) {
                                    selectedIds.push(key);

                                    value.forEach(function (kval) {
                                        if (!expandedIds.find(function (expVal) {
                                            return expVal === kval
                                        })) {
                                            expandedIds.push(kval);
                                        }
                                    });
                                });

                                if (expandedIds && expandedIds.length > 0) {
                                    $hfExpandedIds.val(expandedIds.join(','));
                                }

                                if (selectedIds && selectedIds.length > 0) {
                                    $hfItemIds.val(selectedIds.join(','));
                                }

                                doPostBack();
                            }
                        });
                    }

                });

                // have the X appear on hover if something is selected
                if ($hfItemIds.val() && $hfItemIds.val() !== '0') {
                    $control.find('.picker-select-none').addClass('rollover-item');
                    $control.find('.picker-select-none').show();
                }

                // [Select] button click
                $control.find('.picker-btn').on('click', function (el) {

                    if (self.getViewMode() === 'preview' || self.getViewMode() === 'search') {
                        self.setViewMode('selecting');
                        $('#tbSearch_' + self.options.controlId).val('');
                        $control.find('.js-existing-search-value').val('');

                        if ($hfItemIds && $hfItemIds.length > 0) {
                            var restUrl = self.options.getParentIdsUrl
                            var nodeIds = $hfItemIds.val().split(',');
                            var restUrlParams = nodeIds.map(function (id) { return 'ids=' + id }).join('&');

                            restUrl = restUrl + '?' + restUrlParams;

                            // Get the ancestor ids so the tree will expand
                            $.getJSON(restUrl, function (data, status) {
                                if (data && status === 'success') {
                                    var selectedIds = [];
                                    var expandedIds = [];

                                    $.each(data, function (key, value) {
                                        selectedIds.push(key);

                                        value.forEach(function (kval) {
                                            if (!expandedIds.find(function (expVal) {
                                                return expVal === kval
                                            })) {
                                                expandedIds.push(kval);
                                            }
                                        });
                                    });

                                    if (expandedIds && expandedIds.length > 0) {
                                        $hfExpandedIds.val(expandedIds.join(','));
                                    }

                                    if (selectedIds && selectedIds.length > 0) {
                                        $hfItemIds.val(selectedIds.join(','));
                                    }

                                    doPostBack();
                                }
                            });
                        }

                        return;
                    }

                    var rockTree = $control.find('.treeview').data('rockTree'),
                        selectedNodes = rockTree.selectedNodes,
                        selectedIds = [],
                        selectedNames = [];

                    $.each(selectedNodes, function (index, node) {
                        var nodeName = $("<textarea/>").html(node.name).text();
                        selectedNames.push(nodeName);
                        if (!selectedIds.includes(node.id)) {
                            selectedIds.push(node.id);
                        }
                    });

                    // .trigger('change') is used to cause jQuery to fire any "onchange" event handlers for this hidden field.
                    $hfItemIds.val(selectedIds.join(',')).trigger('change');
                    $hfItemNames.val(selectedNames.join(','));

                    // have the X appear on hover. something is selected
                    $control.find('.picker-select-none').addClass('rollover-item');
                    $control.find('.picker-select-none').show();

                    $spanNames.text(selectedNames.join(', '));
                    $spanNames.attr('title', $spanNames.text());

                    $control.find('.picker-label').toggleClass("active");
                    $control.find('.picker-menu').hide(0, function () {
                        self.updateScrollbar();
                    });

                    self.setViewMode('selected');

                    if (!(el && el.originalEvent && el.originalEvent.srcElement === this)) {
                        // if this event was called by something other than the button itself, make sure the execute the href (which is probably javascript)
                        var jsPostback = $(this).attr('href');
                        if (jsPostback) {
                            window.location = jsPostback;
                        }
                    }
                });

                $control.find('.picker-select-none').on("click", function (e) {
                    e.preventDefault();
                    e.stopImmediatePropagation();

                    $hfItemIds.val('0').trigger('change'); // .trigger('change') is used to cause jQuery to fire any "onchange" event handlers for this hidden field.
                    $hfItemNames.val('');

                    var rockTree = $control.find('.treeview').data('rockTree');
                    rockTree.clear();

                    // don't have the X appear on hover. nothing is selected
                    $control.find('.picker-select-none').removeClass('rollover-item').hide();

                    $control.siblings('.js-hide-on-select-none').hide();

                    $spanNames.text(self.options.defaultText);
                    $spanNames.attr('title', $spanNames.text());
                    doPostBack();
                });

                // clicking on the 'select all' btn
                $control.on('click', '.js-select-all', function (e){
                    const $tree = $control.find('.treeview');
                    const rockTree = $tree.data('rockTree');
                    self.selectAll = !self.selectAll;

                    e.preventDefault();
                    e.stopPropagation();

                    let isChildrenLoaded = true;
                    for (const node of rockTree.nodes) {
                        if (node.hasChildren && !node.children) {
                            isChildrenLoaded = false;
                        }
                    }

                    if (!isChildrenLoaded) {
                        rockTree.nodes = [];
                        rockTree.render();
                        let treeOptions = {
                            customDataItems: self.options.customDataItems,
                            enhanceForLongLists: self.options.enhanceForLongLists,
                            multiselect: self.options.allowMultiSelect,
                            categorySelection: self.options.allowCategorySelection,
                            categoryPrefix: self.options.categoryPrefix,
                            restUrl: self.options.restUrl,
                            searchRestUrl: self.options.searchRestUrl,
                            restParams: self.options.restParams + '&loadChildren=true',
                            expandedIds: self.options.expandedIds,
                            expandedCategoryIds: self.options.expandedCategoryIds,
                            showSelectChildren: self.options.showSelectChildren,
                            id: self.options.startingId
                        };
                        $tree.rockTree(treeOptions);
                    } else {
                        self.toggleSelectAll(rockTree);
                    }
                });
            },
            updateScrollbar: function (sPosition) {
                var self = this;
                // first, update this control's scrollbar, then the modal's
                var $container = $('#' + this.options.controlId).find('.scroll-container');

                if ($container.is(':visible')) {
                    if (!sPosition) {
                        sPosition = 'relative'
                    }
                    if (self.iScroll) {
                        self.iScroll.refresh();
                    }
                }

                // update the outer modal
                Rock.dialogs.updateModalScrollBar(this.options.controlId);
            },
            scrollToSelectedItem: function () {
                var $selectedItem = $('#' + this.options.controlId + ' [class^="picker-menu"]').find('.selected').first();
                if ($selectedItem.length && (!this.alreadyScrolledToSelected)) {
                    this.updateScrollbar();
                    this.iScroll.scrollToElement('.selected', '0s');
                    this.alreadyScrolledToSelected = true;
                } else {
                    // initialize/update the scrollbar
                    this.updateScrollbar();
                }
            },
            showActiveMenu: function () {
                var self = this;
                var $control = $('#' + this.options.controlId);

                if (self.isMenuActive()) {
                    $control.find('.picker-label').click();
                }
                self.setActiveMenu(false);
            },
            setActiveMenu: function (value) {
                var $control = $('#' + this.options.controlId);
                if (value === undefined || value === null) {
                    value = true;
                }
                $control.find('.js-picker-showactive-value').val(value.toString());
            },
            isMenuActive: function () {
                var $control = $('#' + this.options.controlId);
                var showPickerActive = $control.find('.js-picker-showactive-value').val();
                var isActive = showPickerActive && showPickerActive === 'true' ? true : false;
                return isActive;
            },
            setViewMode: function (mode) {
                var $control = $('#' + this.options.controlId);
                var $hfViewMode = $control.find('.js-picker-view-mode');

                var clear = mode.toLowerCase() === 'clear';

                if (!clear && clear === false) {
                    $hfViewMode.val(mode);
                }

                if (clear && clear === true) {
                    $hfViewMode.val('');
                }
            },
            getViewMode: function (mode) {
                var $control = $('#' + this.options.controlId);
                var $hfViewMode = $control.find('.js-picker-view-mode');

                return $hfViewMode.val().toLowerCase();
            },
            togglePickerElements: function () {
                var $control = $('#' + this.options.controlId);
                var rockTree = $control.find('.treeview').data('rockTree');

                var hasSelected = rockTree.selectedNodes && rockTree.selectedNodes.length > 0;

                switch (this.getViewMode()) {
                    case 'preview': {
                        $control.find('.picker-treeview').show();
                        $control.find('.picker-preview').hide();
                        $control.find('.js-picker-show-inactive').hide();
                    }
                        break;
                    case 'search': {
                        $control.find('.picker-treeview').show();
                        $control.find('.picker-preview').hide();
                        $control.find('.js-select-all').hide();
                    }
                        break;
                    default: {
                        if (!hasSelected) {
                            $control.find('.picker-preview').hide();
                            $control.find('.picker-treeview').hide();
                        }
                        else {
                            $control.find('.picker-preview').show();
                        }
                    }

                }
            },
            findNodes: function (allNodes, selectNodeIds) {
                if (selectNodeIds) {
                    if ($.isArray(selectNodeIds)) {
                        const filterArray = (nodes, ids) => {
                            const filteredNodes = nodes.filter(node => {
                                return ids.indexOf(node.id) >= 0;
                            });
                            return filteredNodes;
                        };

                        return filterArray(allNodes, selectNodeIds);
                    }
                    else {
                        return allNodes.filter(node => {
                            return selectNodeIds.indexOf(node.id) >= 0;
                        });
                    }
                }
            },
            createSearchControl: function () {
                var self = this;
                var controlId = self.options.controlId;

                var $control = $('#' + controlId);

                var rockTree = $control.find('.treeview').data('rockTree');

                if (self.options.enhanceForLongLists === true) {

                    // A hidden value to store the current search criteria to be read on post-backs
                    var $searchValueField = $control.find('.js-existing-search-value');

                    var $searchControl =
                        $('	<div id="pnlSearch_' + controlId + '" class="input-group input-group-sm js-search-panel mb-2" > ' +
                            '		<input id="tbSearch_' + controlId + '" type="text" placeholder="Quick Find" class="form-control" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false" />' +
                            '		<span class="input-group-btn">' +
                            '			<a id="btnSearch_' + controlId + '" class="btn btn-default btn-sm"><i class="fa fa-search"></i></a>' +
                            '		</span>' +
                            '	</div>');

                    // Get all of the current rendered items so we can revert if user leaves search
                    var $pickerMenu = $control.find('.picker-menu');
                    var $treeView = $control.find('.treeview');
                    var $overview = $control.find('.overview');
                    var $hfItemIds = $control.find('.js-item-id-value');

                    // Added this check to prevent rendering call from duping the element
                    if ($pickerMenu.find('.js-search-panel').length === 0) {
                        // Add the search control after rendering
                        $pickerMenu.prepend($searchControl);
                    }

                    var $searchInputControl = $('#tbSearch_' + controlId);

                    $('#btnSearch_' + controlId).off('click').on('click', function () {

                        var currentSelectedNodeIds = rockTree.selectedNodes.map(function (v) { return v.id; });

                        $hfItemIds = $control.find('.js-item-id-value');

                        var searchKeyword = $searchInputControl.val();

                        if (searchKeyword && searchKeyword.length > 0) {

                            self.setViewMode('search');

                            var searchRestUrl = self.options.searchRestUrl;
                            var restUrlParams = self.options.restParams + '&searchTerm=' + searchKeyword;

                            searchRestUrl += restUrlParams;

                            $.getJSON(searchRestUrl, function (data, status) {

                                if (data && status === 'success') {
                                    $treeView.html('');
                                }
                                else {
                                    $overview.html(treeView);
                                    return;
                                }

                                // Create the search results node object
                                var nodes = [];
                                for (var i = 0; i < data.length; i++) {
                                    var obj = data[i];
                                    var node = {
                                        id: obj.Id,
                                        parentId: obj.ParentId,
                                        glcode: obj.GlCode,
                                        title: obj.Name + (obj.GlCode ? ' (' + obj.GlCode + ')' : ''),
                                        name: obj.Name,
                                        hasChildren: obj.HasChildren,
                                        isActive: obj.IsActive,
                                        path: obj.Path
                                    };

                                    nodes.push(node);
                                }

                                if (nodes) {

                                    var listHtml = '';
                                    nodes.forEach(function (node, idx) {

                                        var disabledCheck = '';
                                        var mutedText = '';
                                        if (!node.isActive || node.isActive === false) {
                                            disabledCheck = ' disabled';
                                            mutedText = ' text-muted';
                                        }

                                        var inputHtml = '<input type="radio" data-id="' + node.id + '" class="checkbox js-opt-search"' + disabledCheck + '>';
                                        var inputType = 'radio';
                                        if (self.options.allowMultiSelect) {
                                            inputHtml = '<input type="checkbox" data-id="' + node.id + '" class="checkbox js-chk-search"' + disabledCheck + '>';
                                            inputType = 'checkbox';
                                        }

                                        if (node.path === '') {
                                            node.path = "Top-Level";
                                        }

                                        listHtml +=

                                            '<div id="divSearchItem-' + node.id + '" class="' + inputType + ' search-item js-search-item">' +
                                            '      <label>' +
                                            inputHtml +
                                            '        <span class="label-text">' +
                                            '              <span class="text-color d-block' + mutedText + '">' + node.title + '</span>' +
                                            '              <span class="text-muted text-sm">' + node.path.replaceAll('^', '<i class="fa fa-chevron-right pl-1 pr-1" aria-hidden="true"></i>') + '</span>' +
                                            '        </span>' +
                                            '     </label>' +
                                            '</div>';
                                    });

                                    // add the results to the panel
                                    $treeView.html(listHtml);

                                    self.updateScrollbar();

                                    $control.find('.js-chk-search').off('change').on('change', function () {
                                        var itemIds = [];
                                        var $allChecked = $control.find('.js-chk-search:checked');
                                        var checkedVals = $allChecked.map(function () {
                                            return $(this).attr('data-id');
                                        }).get();

                                        if (checkedVals && checkedVals.length > 0) {
                                            $control.find('.picker-treeview').show();
                                            var checkedNodes = self.findNodes(nodes, checkedVals);

                                            if (checkedNodes && checkedNodes.length) {
                                                checkedNodes.forEach(function (n) {
                                                    if (n.length === 0) { return; }
                                                    if (!itemIds.find(function (i) { return n.id === i; })) {
                                                        itemIds.push(n.id);
                                                    }

                                                });
                                            }
                                        }

                                        // We need to reselect any selection that were set in the tree but not part of the search results
                                        if (currentSelectedNodeIds && currentSelectedNodeIds.length > 0) {
                                            nodes.forEach(function (node) {
                                                currentSelectedNodeIds = currentSelectedNodeIds.filter(function (current) { return current !== node.id });
                                            });

                                            if (currentSelectedNodeIds && currentSelectedNodeIds.length > 0) {
                                                itemIds.push(currentSelectedNodeIds);
                                            }
                                        }

                                        if (itemIds && itemIds.length) {
                                            // set the selected items on the server control to handle on postback
                                            $hfItemIds.val(itemIds.join(','));
                                        }
                                    });

                                    // Handle multi item check selection
                                    $control.find('.js-search-item').off('click').on('click', function (e) {

                                        var $chkBox = $(this).find('.js-chk-search').first();
                                        if ($chkBox.length > 0) {
                                            if (!$chkBox.prop('checked')) {
                                                $chkBox.prop('checked', true);
                                                $control.find('.js-chk-search').trigger('change');
                                            }
                                            else {
                                                $chkBox.prop('checked', false);
                                            }
                                        }
                                    });


                                    // Handle single item radio selection
                                    $control.find('.js-opt-search').off('change').on('change', function () {
                                        var thisNodeId = $(this).attr('data-id');
                                        var itemIds = [];
                                        //prevent multi select
                                        $control.find('.js-opt-search:not([data-id=' + thisNodeId + '])').prop('checked', false);

                                        var $allChecked = $control.find('.js-opt-search:checked');
                                        var checkedVals = $allChecked.map(function () {
                                            return $(this).attr('data-id');
                                        }).get();

                                        if (checkedVals && checkedVals.length > 0) {
                                            var checkedNodes = self.findNodes(nodes, checkedVals);

                                            if (checkedNodes && checkedNodes.length) {
                                                checkedNodes.forEach(function (n) {
                                                    if (n.length === 0) { return; }
                                                    if (!itemIds.find(function (i) { return n.id === i; })) {
                                                        itemIds.push(n.id);
                                                    }
                                                });
                                            }
                                        }

                                        if (itemIds && itemIds.length) {
                                            // set the selected items on the server control to handle on postback
                                            $hfItemIds.val(itemIds.join(','));
                                        }
                                    });
                                }

                                //select current selected nodes in search
                                if (rockTree.selectedNodes && rockTree.selectedNodes.length) {
                                    rockTree.selectedNodes.forEach(function (selectedNode) {
                                        if (self.options.allowMultiSelect && self.options.allowMultiSelect === true) {
                                            $control.find('[data-id=' + selectedNode.id + '].js-chk-search').prop('checked', true);
                                            var checkedNode = selectedNode;
                                            checkedNode.id = selectedNode.id;
                                            $control.find('[data-id=' + selectedNode.id + '].js-chk-search').trigger('change');
                                        }
                                        else {
                                            $control.find('[data-id=' + selectedNode.id + '].js-opt-search').prop('checked', true);
                                            var enabledNode = selectedNode;
                                            enabledNode.id = selectedNode.id;
                                            $control.find('[data-id=' + selectedNode.id + '].js-opt-search').trigger('change');
                                        }
                                    });
                                }
                            });
                        }

                    });

                    // If we have an existing search value on postback
                    if ($searchValueField.length > 0 && $searchValueField.val().length > 0) {
                        $searchInputControl.val($searchValueField.val());
                        $('#btnSearch_' + controlId).click();
                    }

                    // Handle the input searching
                    $searchInputControl.keyup(function (keyEvent) {
                        keyEvent.preventDefault();

                        var searchKeyword = $searchInputControl.val();

                        if (!searchKeyword || searchKeyword.length === 0) {
                            $control.find('.picker-treeview').trigger('click');
                        }

                        self.togglePickerElements();
                    }).keydown(function (keyEvent) {

                        var searchKeyword = $searchInputControl.val();
                        $searchValueField.val(searchKeyword);

                        if (keyEvent.which === 13) {
                            keyEvent.preventDefault();
                            $('#btnSearch_' + controlId).click();
                        }
                    });
                }
            },
            toggleSelectAll: function (rockTree) {

                const $itemNameNodes = rockTree.$el.find('.rocktree-name');

                const setNodeOpenState = function (node, isOpen) {
                    if (node.hasChildren && node.children) {
                        node.isOpen = isOpen;
                        node.children.forEach(childNode => setNodeOpenState(childNode, isOpen));
                    }
                }

                if (this.selectAll) {
                    // mark them all as unselected (just in case some are selected already), then click them to select them
                    $itemNameNodes.removeClass('selected');
                    $itemNameNodes.trigger('click');
                    rockTree.nodes.forEach(node => setNodeOpenState(node, true));
                } else {
                    // if all were already selected, toggle them to unselected
                    rockTree.setSelected([]);
                    const $control = $('#' + this.options.controlId);
                    const $hfItemIds = $control.find('.js-item-id-value');
                    $hfItemIds.val('0');
                    $itemNameNodes.removeClass('selected');
                    rockTree.nodes.forEach(node => setNodeOpenState(node, false));
                }

                rockTree.render();
            },
            selectAll: false
        }

        // jquery function to ensure HTML is state remains the same each time it is executed
        $.fn.outerHTML = function (s) {
            return (s)
                ? this.before(s).remove()
                : $("<p>").append(this.eq(0).clone()).html();
        }

        exports = {
            defaults: {
                id: 0,
                controlId: null,
                restUrl: null,
                searchRestUrl: null,
                restParams: null,
                allowCategorySelection: false,
                categoryPrefix: '',
                allowMultiSelect: false,
                defaultText: '',
                selectedIds: null,
                expandedIds: null,
                expandedCategoryIds: null,
                showSelectChildren: false,
                enhanceForLongLists: false,
                customDataItems: []
            },
            controls: {},
            initialize: function (options) {
                var settings,
                    accountPicker;

                if (!options.controlId) {
                    throw 'controlId must be set';
                }

                if (!options.restUrl) {
                    throw 'restUrl must be set';
                }

                if (options.enhanceForLongLists === true && !options.searchRestUrl) {
                    throw 'searchRestUrl must be set';
                }

                settings = $.extend({}, exports.defaults, options);

                if (!settings.defaultText) {
                    settings.defaultText = exports.defaults.defaultText;
                }

                accountPicker = new AccountPicker(settings);
                exports.controls[settings.controlId] = accountPicker;
                accountPicker.initialize();
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.addressControl = (function () {
        var exports = {
            initialize: function (options) {
                if (!options.id) {
                    throw 'id is required';
                }
            },
            clientValidate: function (validator, args) {
                var $addressControl = $(validator).closest('.js-addressControl');
                var $addressFieldControls = $addressControl.find('.js-address-field.required');
                var addressControlLabelText = $addressControl.attr('data-itemlabel');
                var isValid = true;
                var invalidFieldList = [];

                $addressFieldControls.each(function (index, fieldControl) {
                    var $fieldControl = $(fieldControl);
                    if (typeof fieldControl == 'undefined' || $fieldControl.val().length > 0) {
                        $fieldControl.parent('div.form-group').removeClass('has-error');
                    }
                    else {
                        $fieldControl.parent('div.form-group').addClass('has-error');
                        invalidFieldList.push($fieldControl.attr('field-name'));
                        isValid = false;
                    }
                });

                if (!isValid) {
                    validator.errormessage = addressControlLabelText + " field is required: " + invalidFieldList.join(", ");
                }

                args.IsValid = isValid;
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.assetManager = (function () {
        var exports;

        exports = {
            initialize: function (options) {
                var self = this;

                var $assetBrowser = $('#' + options.controlId);
                var $folderTreeView = $assetBrowser.find('.js-folder-treeview .treeview');
                var $selectFolder = $assetBrowser.find('.js-selectfolder');
                var $expandedFolders = $assetBrowser.find('.js-expandedFolders');
                var $assetStorageId = $assetBrowser.find('.js-assetstorage-id');
                var $treePort = $assetBrowser.find('.js-treeviewport');
                var $treeTrack = $assetBrowser.find('.js-treetrack');
                var $isRoot = $assetBrowser.find('.js-isroot');

                var $createFolder = $assetBrowser.find('.js-createfolder');
                var $createFolderDiv = $assetBrowser.find('.js-createfolder-div');
                var $createFolderInput = $assetBrowser.find('.js-createfolder-input');
                var $createFolderCancel = $assetBrowser.find('.js-createfolder-cancel');
                var $createFolderAccept = $assetBrowser.find('.js-createfolder-accept');
                var $deleteFolder = $assetBrowser.find('.js-deletefolder');
                var $createFolderNotification = $assetBrowser.find('.js-createfolder-notification');


                // 2020-09-07 MDP
                // Asset manager can be used in 3 ways.
                // 1) An ItemFromBlockPicker control (An Asset attribute).
                // 2) Within the HTML Editor as a summernote plugin.
                // 3) In Home > CMS Configuration > Asset Manager.
                // The OK/Save button is the parent modal (but different types of modals) in case 1 and 2, and without an OK/Save button in case 3.
                // So to find all the js-singleselect items in cases 1 and 2, we'll have to look in the modal so that the OK/Save button is found
                var $assetManagerModal = $assetBrowser.closest('.js-AssetManager-modal');
                var $singleSelectControls;
                if ($assetManagerModal.length) {
                    // this would be cases #1 and #2
                    $singleSelectControls = $assetManagerModal.find('.js-singleselect');
                } else {
                    // this would be case #3
                    $singleSelectControls = $assetBrowser.find('.js-singleselect');
                }

                var $minSelectControls = $assetBrowser.find('.js-minselect');
                var $fileCheckboxes = $assetBrowser.find('.js-checkbox');

                var $renameFile = $assetBrowser.find('.js-renamefile');
                var $renameFileDiv = $assetBrowser.find('.js-renamefile-div');
                var $renameFileInput = $assetBrowser.find('.js-renamefile-input');
                var $renameFileCancel = $assetBrowser.find('.js-renamefile-cancel');
                var $renameFileAccept = $assetBrowser.find('.js-renamefile-accept');
                var $renamefilenotification = $assetBrowser.find('.js-renamefile-notification');

                if ($folderTreeView.length === 0) {
                    return;
                }

                // Some buttons need an asset selected in order to work
                if ($assetStorageId.val() === "-1") {
                    $('.js-assetselect').addClass('aspNetDisabled');
                }

                $fileCheckboxes.removeAttr('checked');

                // Can only add if either an asset or folder is selected
                if ($selectFolder.val() === "" && $assetStorageId.val() === "-1") {
                    $createFolder.addClass('aspNetDisabled');
                }
                else {
                    $createFolder.removeClass('aspNetDisabled');
                }

                // Can delete folder only if a folder is selected
                if ($selectFolder.val() === "" || $isRoot.val() === "True") {
                    $deleteFolder.addClass('aspNetDisabled');
                }
                else {
                    $deleteFolder.removeClass('aspNetDisabled');
                }

                var folderTreeData = $folderTreeView.data('rockTree');

                if (!folderTreeData) {
                    var selectedFolders = [encodeURIComponent($assetStorageId.val() + ',' + $selectFolder.val())];
                    var expandedFolders = $expandedFolders.val().split('||');
                    expandedFolders.forEach(function (part, i, array) {
                        array[i] = encodeURIComponent(array[i]);
                    });

                    $folderTreeView.rockTree({
                        restUrl: options.restUrl,
                        selectedIds: selectedFolders,
                        expandedIds: expandedFolders
                    });

                    var treePortIScroll = new IScroll($treePort[0], {
                        mouseWheel: false,
                        scrollX: true,
                        scrollY: false,
                        indicators: {
                            el: '#' + $treeTrack.attr('id'),
                            interactive: true,
                            resize: false,
                            listenY: false,
                            listenX: true
                        },
                        click: false,
                        preventDefaultException: { tagName: /.*/ }
                    });

                    // resize scrollbar when the window resizes
                    $(document).ready(function () {
                        $(window).on('resize', function () {
                            resizeScrollAreaHeight();
                        });
                    });

                    $folderTreeView.on('rockTree:dataBound rockTree:rendered', function (evt) {
                        resizeScrollAreaHeight();
                    });

                    $folderTreeView.on('rockTree:expand rockTree:collapse', function (evt, data) {
                        resizeScrollAreaHeight();

                        // get the data-id values of rock-tree items that are showing visible children (in other words, Expanded Nodes)
                        var expandedDataIds = $(evt.currentTarget).find('.rocktree-children').filter(":visible").closest('.rocktree-item').map(function () {
                            var dataId = $(this).attr('data-id');
                            if (dataId !== data) {
                                return decodeURIComponent(dataId);
                            }
                        }).get().join('||');

                        $expandedFolders.val(expandedDataIds);
                    });
                }

                function resizeScrollAreaHeight() {
                    var overviewHeight = $folderTreeView.closest('.overview').height();
                    $treePort.height(overviewHeight);

                    if (treePortIScroll) {
                        treePortIScroll.refresh();
                    }
                }

                function isValidName(name) {
                    var regex = new RegExp("^[^*/><?\\\\|:,~]+$");
                    return regex.test(name);
                }

                $folderTreeView.off('rockTree:selected').on('rockTree:selected', function (e, data) {
                    var assetFolderIdParts = unescape(data).split(",");
                    var storageId = assetFolderIdParts[0] || "";
                    var folder = assetFolderIdParts[1] || "";
                    var isRoot = assetFolderIdParts[2] || false;
                    var postbackArg;
                    var expandedFolders = encodeURIComponent($expandedFolders.val());

                    // Can only add if either an asset or folder is selected
                    if ($selectFolder.val() === "" && $assetStorageId.val() === "-1") {
                        $createFolder.addClass('aspNetDisabled');
                    }
                    else {
                        $createFolder.removeClass('aspNetDisabled');
                    }

                    // Can delete folder only if a folder is selected
                    if ($selectFolder.val() === "" || $isRoot.val() === "True") {
                        $deleteFolder.addClass('aspNetDisabled');
                    }
                    else {
                        $deleteFolder.removeClass('aspNetDisabled');
                    }

                    if ($selectFolder.val() !== folder || $assetStorageId.val() !== storageId) {
                        $selectFolder.val(folder);
                        $assetStorageId.val(storageId);
                        $isRoot.val(isRoot);
                        postbackArg = 'storage-id:' + storageId + '?folder-selected:' + folder.replace(/\\/g, "/").replace("'", "\\'") + '?expanded-folders:' + expandedFolders.replace("'", "\\'") + '?isRoot:' + isRoot;

                        var jsPostback = "javascript:__doPostBack('" + options.filesUpdatePanelId + "','" + postbackArg + "');";
                        window.location = jsPostback;
                    }
                });

                $fileCheckboxes.off('click').on('click', function () {
                    var numberChecked = $fileCheckboxes.filter(':checked').length;

                    if (numberChecked === 0) {
                        $singleSelectControls.addClass('aspNetDisabled');
                        $('.js-minselect').addClass('aspNetDisabled');
                    }
                    else if (numberChecked === 1) {
                        $singleSelectControls.removeClass('aspNetDisabled');
                        $minSelectControls.removeClass('aspNetDisabled');
                    }
                    else {
                        $singleSelectControls.addClass('aspNetDisabled');
                        $minSelectControls.removeClass('aspNetDisabled');
                    }
                });

                $createFolder.off('click').on('click', function () {
                    $createFolderDiv.fadeToggle();
                    $createFolderInput.val('');
                });

                $createFolderCancel.off('click').on('click', function () {
                    $createFolderDiv.fadeOut();
                    $createFolderInput.val('');
                    $createFolderNotification.hide().text('');
                });

                $createFolderAccept.off('click').on('click', function (e) {
                    var name = $createFolderInput.val();

                    if (name === "") {
                        $createFolderNotification.show().text('Folder name is required.');
                        e.preventDefault();
                    }

                    if (!isValidName(name)) {
                        $createFolderNotification.show().text('Invalid characters in path');
                        e.preventDefault();
                    }
                });

                $renameFile.off('click').on('click', function () {
                    $renameFileDiv.fadeToggle();
                    $renameFileInput.val('');
                });

                $renameFileCancel.off('click').on('click', function () {
                    $renameFileDiv.fadeOut();
                    $renameFileInput.val('');
                    $renamefilenotification.hide().text('');
                });

                $renameFileAccept.off('click').on('click', function (e) {
                    var name = $renameFileInput.val();

                    if (name === "") {
                        $renamefilenotification.show().text('File name is required.');
                        e.preventDefault();
                    }

                    if (!isValidName(name)) {
                        $renamefilenotification.show().text('Invalid characters in file name.');
                        e.preventDefault();
                    }
                });

                $deleteFolder.off('click').on('click', function (e) {
                    if ($(this).attr('disabled') === 'disabled') {
                        return false;
                    }

                    Rock.dialogs.confirmDelete(e, 'folder and all its contents');
                });
            }
        };

        return exports;

    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.autoCompleteDropDown = (function () {
        var exports,
            AutoCompleteDropDown = function (options) {
                this.controlId = options.controlId;
                this.valueControlId = options.valueControlId;
                this.$el = $('#' + this.controlId);
                this.name = options.name;
                this.url = options.url;
                this.limit = options.limit;
                this.idProperty = options.idProperty;
                this.valuekey = options.valuekey;
                this.template = options.template;
                this.header = options.header;
                this.footer = options.footer;
            };

        AutoCompleteDropDown.prototype = {
            constructor: AutoCompleteDropDown,
            initialize: function () {
                var self = this,
                    Liquid = require('liquid'),
                    liquidEngine = {
                        compile: function (template) {
                            var parsedTemplate = Liquid.Template.parse(template);
                            return {
                                render: function (context) {
                                    return parsedTemplate.render(context);
                                }
                            };
                        }
                    };

                this.$el.typeahead({
                    name: this.name,
                    limit: this.limit,
                    valueKey: this.valuekey,
                    template: this.template,
                    header: this.header,
                    footer: this.footer,
                    engine: liquidEngine,
                    remote: {
                        url: Rock.settings.get('baseUrl') + this.url,
                        filter: function (response) {
                            response.forEach(function (item) {
                                item['tokens'] = item[self.valuekey].replace(/,/g, '').split(" ");
                            });
                            return response;
                        }
                    }
                });

                this.initializeEventHandlers();
            },

            initializeEventHandlers: function () {
                var self = this,
                    idProperty = this.idProperty,
                    setValue = function (datum) {
                        $('#' + self.valueControlId).val(datum[idProperty]);
                    };

                // Listen for typeahead's custom events and trigger search when hit
                this.$el.on('typeahead:selected typeahead:autocompleted', function (e, obj, name) {
                    setValue(obj);
                    if (typeof self.onSelected === "function") {
                        self.onSelected(obj);
                    }
                });

            }
        };

        exports = {
            defaults: {
                controlId: null,
                name: 'autoComplete',
                valueControlId: null,
                limit: 5,
                idProperty: 'Id',
                valuekey: 'value',
                template: '<p>{{value}}</p>',
                header: '',
                footer: ''
            },
            initialize: function (options) {
                var autoCompleteDropDown,
                    settings = $.extend({}, exports.defaults, options);

                if (!settings.controlId) throw 'controlId is required';
                if (!settings.valueControlId) throw 'valueControlId is required';
                if (!settings.url) throw 'url is required';

                autoCompleteDropDown = new AutoCompleteDropDown(settings);

                // Delay initialization until after the DOM is ready
                $(function () {
                    autoCompleteDropDown.initialize();
                });

                return autoCompleteDropDown;
            }
        };

        return exports;
    }());
}(jQuery));
;
/*!
 * Bootstrap Colorpicker v2.5.3
 * https://itsjavi.com/bootstrap-colorpicker/
 */
!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("jquery")):jQuery&&!jQuery.fn.colorpicker&&b(jQuery)}(this,function(a){"use strict";var b=function(c,d,e,f,g){this.fallbackValue=e?"string"==typeof e?this.parse(e):e:null,this.fallbackFormat=f?f:"rgba",this.hexNumberSignPrefix=g===!0,this.value=this.fallbackValue,this.origFormat=null,this.predefinedColors=d?d:{},this.colors=a.extend({},b.webColors,this.predefinedColors),c&&("undefined"!=typeof c.h?this.value=c:this.setColor(String(c))),this.value||(this.value={h:0,s:0,b:0,a:1})};b.webColors={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"00ffff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000000",blanchedalmond:"ffebcd",blue:"0000ff",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"00ffff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"ff00ff",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgrey:"d3d3d3",lightgreen:"90ee90",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"778899",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"00ff00",limegreen:"32cd32",linen:"faf0e6",magenta:"ff00ff",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370d8",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"d87093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",red:"ff0000",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"ffffff",whitesmoke:"f5f5f5",yellow:"ffff00",yellowgreen:"9acd32",transparent:"transparent"},b.prototype={constructor:b,colors:{},predefinedColors:{},getValue:function(){return this.value},setValue:function(a){this.value=a},_sanitizeNumber:function(a){return"number"==typeof a?a:isNaN(a)||null===a||""===a||void 0===a?1:""===a?0:"undefined"!=typeof a.toLowerCase?(a.match(/^\./)&&(a="0"+a),Math.ceil(100*parseFloat(a))/100):1},isTransparent:function(a){return!(!a||!("string"==typeof a||a instanceof String))&&(a=a.toLowerCase().trim(),"transparent"===a||a.match(/#?00000000/)||a.match(/(rgba|hsla)\(0,0,0,0?\.?0\)/))},rgbaIsTransparent:function(a){return 0===a.r&&0===a.g&&0===a.b&&0===a.a},setColor:function(a){if(a=a.toLowerCase().trim()){if(this.isTransparent(a))return this.value={h:0,s:0,b:0,a:0},!0;var b=this.parse(a);b?(this.value=this.value={h:b.h,s:b.s,b:b.b,a:b.a},this.origFormat||(this.origFormat=b.format)):this.fallbackValue&&(this.value=this.fallbackValue)}return!1},setHue:function(a){this.value.h=1-a},setSaturation:function(a){this.value.s=a},setBrightness:function(a){this.value.b=1-a},setAlpha:function(a){this.value.a=Math.round(parseInt(100*(1-a),10)/100*100)/100},toRGB:function(a,b,c,d){0===arguments.length&&(a=this.value.h,b=this.value.s,c=this.value.b,d=this.value.a),a*=360;var e,f,g,h,i;return a=a%360/60,i=c*b,h=i*(1-Math.abs(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a],{r:Math.round(255*e),g:Math.round(255*f),b:Math.round(255*g),a:d}},toHex:function(a,b,c,d,e){arguments.length<=1&&(b=this.value.h,c=this.value.s,d=this.value.b,e=this.value.a);var f="#",g=this.toRGB(b,c,d,e);if(this.rgbaIsTransparent(g))return"transparent";a||(f=this.hexNumberSignPrefix?"#":"");var h=f+((1<<24)+(parseInt(g.r)<<16)+(parseInt(g.g)<<8)+parseInt(g.b)).toString(16).slice(1);return h},toHSL:function(a,b,c,d){0===arguments.length&&(a=this.value.h,b=this.value.s,c=this.value.b,d=this.value.a);var e=a,f=(2-b)*c,g=b*c;return g/=f>0&&f<=1?f:2-f,f/=2,g>1&&(g=1),{h:isNaN(e)?0:e,s:isNaN(g)?0:g,l:isNaN(f)?0:f,a:isNaN(d)?0:d}},toAlias:function(a,b,c,d){var e,f=0===arguments.length?this.toHex(!0):this.toHex(!0,a,b,c,d),g="alias"===this.origFormat?f:this.toString(!1,this.origFormat);for(var h in this.colors)if(e=this.colors[h].toLowerCase().trim(),e===f||e===g)return h;return!1},RGBtoHSB:function(a,b,c,d){a/=255,b/=255,c/=255;var e,f,g,h;return g=Math.max(a,b,c),h=g-Math.min(a,b,c),e=0===h?null:g===a?(b-c)/h:g===b?(c-a)/h+2:(a-b)/h+4,e=(e+360)%6*60/360,f=0===h?0:h/g,{h:this._sanitizeNumber(e),s:f,b:g,a:this._sanitizeNumber(d)}},HueToRGB:function(a,b,c){return c<0?c+=1:c>1&&(c-=1),6*c<1?a+(b-a)*c*6:2*c<1?b:3*c<2?a+(b-a)*(2/3-c)*6:a},HSLtoRGB:function(a,b,c,d){b<0&&(b=0);var e;e=c<=.5?c*(1+b):c+b-c*b;var f=2*c-e,g=a+1/3,h=a,i=a-1/3,j=Math.round(255*this.HueToRGB(f,e,g)),k=Math.round(255*this.HueToRGB(f,e,h)),l=Math.round(255*this.HueToRGB(f,e,i));return[j,k,l,this._sanitizeNumber(d)]},parse:function(b){if("string"!=typeof b)return this.fallbackValue;if(0===arguments.length)return!1;var c,d,e=this,f=!1,g="undefined"!=typeof this.colors[b];return g&&(b=this.colors[b].toLowerCase().trim()),a.each(this.stringParsers,function(a,h){var i=h.re.exec(b);return c=i&&h.parse.apply(e,[i]),!c||(f={},d=g?"alias":h.format?h.format:e.getValidFallbackFormat(),f=d.match(/hsla?/)?e.RGBtoHSB.apply(e,e.HSLtoRGB.apply(e,c)):e.RGBtoHSB.apply(e,c),f instanceof Object&&(f.format=d),!1)}),f},getValidFallbackFormat:function(){var a=["rgba","rgb","hex","hsla","hsl"];return this.origFormat&&a.indexOf(this.origFormat)!==-1?this.origFormat:this.fallbackFormat&&a.indexOf(this.fallbackFormat)!==-1?this.fallbackFormat:"rgba"},toString:function(a,c,d){c=c||this.origFormat||this.fallbackFormat,d=d||!1;var e=!1;switch(c){case"rgb":return e=this.toRGB(),this.rgbaIsTransparent(e)?"transparent":"rgb("+e.r+","+e.g+","+e.b+")";case"rgba":return e=this.toRGB(),"rgba("+e.r+","+e.g+","+e.b+","+e.a+")";case"hsl":return e=this.toHSL(),"hsl("+Math.round(360*e.h)+","+Math.round(100*e.s)+"%,"+Math.round(100*e.l)+"%)";case"hsla":return e=this.toHSL(),"hsla("+Math.round(360*e.h)+","+Math.round(100*e.s)+"%,"+Math.round(100*e.l)+"%,"+e.a+")";case"hex":return this.toHex(a);case"alias":return e=this.toAlias(),e===!1?this.toString(a,this.getValidFallbackFormat()):d&&!(e in b.webColors)&&e in this.predefinedColors?this.predefinedColors[e]:e;default:return e}},stringParsers:[{re:/rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*?\)/,format:"rgb",parse:function(a){return[a[1],a[2],a[3],1]}},{re:/rgb\(\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*?\)/,format:"rgb",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],1]}},{re:/rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[a[1],a[2],a[3],a[4]]}},{re:/rgba\(\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],a[4]]}},{re:/hsl\(\s*(\d*(?:\.\d+)?)\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*?\)/,format:"hsl",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/hsla\(\s*(\d*(?:\.\d+)?)\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/,format:"hsla",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/#?([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,format:"hex",parse:function(a){return[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],16),1]}},{re:/#?([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,format:"hex",parse:function(a){return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16),1]}}],colorNameToHex:function(a){return"undefined"!=typeof this.colors[a.toLowerCase()]&&this.colors[a.toLowerCase()]}};var c={horizontal:!1,inline:!1,color:!1,format:!1,input:"input",container:!1,component:".add-on, .input-group-addon",fallbackColor:!1,fallbackFormat:"hex",hexNumberSignPrefix:!0,sliders:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setHue"},alpha:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setAlpha"}},slidersHorz:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:100,maxTop:0,callLeft:"setHue",callTop:!1},alpha:{maxLeft:100,maxTop:0,callLeft:"setAlpha",callTop:!1}},template:'<div class="colorpicker dropdown-menu"><div class="colorpicker-saturation"><i><b></b></i></div><div class="colorpicker-hue"><i></i></div><div class="colorpicker-alpha"><i></i></div><div class="colorpicker-color"><div /></div><div class="colorpicker-selectors"></div></div>',align:"right",customClass:null,colorSelectors:null},d=function(b,d){this.element=a(b).addClass("colorpicker-element"),this.options=a.extend(!0,{},c,this.element.data(),d),this.component=this.options.component,this.component=this.component!==!1&&this.element.find(this.component),this.component&&0===this.component.length&&(this.component=!1),this.container=this.options.container===!0?this.element:this.options.container,this.container=this.container!==!1&&a(this.container),this.input=this.element.is("input")?this.element:!!this.options.input&&this.element.find(this.options.input),this.input&&0===this.input.length&&(this.input=!1),this.color=this.createColor(this.options.color!==!1?this.options.color:this.getValue()),this.format=this.options.format!==!1?this.options.format:this.color.origFormat,this.options.color!==!1&&(this.updateInput(this.color),this.updateData(this.color)),this.disabled=!1;var e=this.picker=a(this.options.template);if(this.options.customClass&&e.addClass(this.options.customClass),this.options.inline?e.addClass("colorpicker-inline colorpicker-visible"):e.addClass("colorpicker-hidden"),this.options.horizontal&&e.addClass("colorpicker-horizontal"),["rgba","hsla","alias"].indexOf(this.format)===-1&&this.options.format!==!1&&"transparent"!==this.getValue()||e.addClass("colorpicker-with-alpha"),"right"===this.options.align&&e.addClass("colorpicker-right"),this.options.inline===!0&&e.addClass("colorpicker-no-arrow"),this.options.colorSelectors){var f=this,g=f.picker.find(".colorpicker-selectors");g.length>0&&(a.each(this.options.colorSelectors,function(b,c){var d=a("<i />").addClass("colorpicker-selectors-color").css("background-color",c).data("class",b).data("alias",b);d.on("mousedown.colorpicker touchstart.colorpicker",function(b){b.preventDefault(),f.setValue("alias"===f.format?a(this).data("alias"):a(this).css("background-color"))}),g.append(d)}),g.show().addClass("colorpicker-visible"))}e.on("mousedown.colorpicker touchstart.colorpicker",a.proxy(function(a){a.target===a.currentTarget&&a.preventDefault()},this)),e.find(".colorpicker-saturation, .colorpicker-hue, .colorpicker-alpha").on("mousedown.colorpicker touchstart.colorpicker",a.proxy(this.mousedown,this)),e.appendTo(this.container?this.container:a("body")),this.input!==!1&&(this.input.on({"keyup.colorpicker":a.proxy(this.keyup,this)}),this.input.on({"input.colorpicker":a.proxy(this.change,this)}),this.component===!1&&this.element.on({"focus.colorpicker":a.proxy(this.show,this)}),this.options.inline===!1&&this.element.on({"focusout.colorpicker":a.proxy(this.hide,this)})),this.component!==!1&&this.component.on({"click.colorpicker":a.proxy(this.show,this)}),this.input===!1&&this.component===!1&&this.element.on({"click.colorpicker":a.proxy(this.show,this)}),this.input!==!1&&this.component!==!1&&"color"===this.input.attr("type")&&this.input.on({"click.colorpicker":a.proxy(this.show,this),"focus.colorpicker":a.proxy(this.show,this)}),this.update(),a(a.proxy(function(){this.element.trigger("create")},this))};d.Color=b,d.prototype={constructor:d,destroy:function(){this.picker.remove(),this.element.removeData("colorpicker","color").off(".colorpicker"),this.input!==!1&&this.input.off(".colorpicker"),this.component!==!1&&this.component.off(".colorpicker"),this.element.removeClass("colorpicker-element"),this.element.trigger({type:"destroy"})},reposition:function(){if(this.options.inline!==!1||this.options.container)return!1;var a=this.container&&this.container[0]!==window.document.body?"position":"offset",b=this.component||this.element,c=b[a]();"right"===this.options.align&&(c.left-=this.picker.outerWidth()-b.outerWidth()),this.picker.css({top:c.top+b.outerHeight(),left:c.left})},show:function(b){this.isDisabled()||(this.picker.addClass("colorpicker-visible").removeClass("colorpicker-hidden"),this.reposition(),a(window).on("resize.colorpicker",a.proxy(this.reposition,this)),!b||this.hasInput()&&"color"!==this.input.attr("type")||b.stopPropagation&&b.preventDefault&&(b.stopPropagation(),b.preventDefault()),!this.component&&this.input||this.options.inline!==!1||a(window.document).on({"mousedown.colorpicker":a.proxy(this.hide,this)}),this.element.trigger({type:"showPicker",color:this.color}))},hide:function(b){return("undefined"==typeof b||!b.target||!(a(b.currentTarget).parents(".colorpicker").length>0||a(b.target).parents(".colorpicker").length>0))&&(this.picker.addClass("colorpicker-hidden").removeClass("colorpicker-visible"),a(window).off("resize.colorpicker",this.reposition),a(window.document).off({"mousedown.colorpicker":this.hide}),this.update(),void this.element.trigger({type:"hidePicker",color:this.color}))},updateData:function(a){return a=a||this.color.toString(!1,this.format),this.element.data("color",a),a},updateInput:function(a){return a=a||this.color.toString(!1,this.format),this.input!==!1&&(this.input.prop("value",a),this.input.trigger("change")),a},updatePicker:function(a){"undefined"!=typeof a&&(this.color=this.createColor(a));var b=this.options.horizontal===!1?this.options.sliders:this.options.slidersHorz,c=this.picker.find("i");if(0!==c.length)return this.options.horizontal===!1?(b=this.options.sliders,c.eq(1).css("top",b.hue.maxTop*(1-this.color.value.h)).end().eq(2).css("top",b.alpha.maxTop*(1-this.color.value.a))):(b=this.options.slidersHorz,c.eq(1).css("left",b.hue.maxLeft*(1-this.color.value.h)).end().eq(2).css("left",b.alpha.maxLeft*(1-this.color.value.a))),c.eq(0).css({top:b.saturation.maxTop-this.color.value.b*b.saturation.maxTop,left:this.color.value.s*b.saturation.maxLeft}),this.picker.find(".colorpicker-saturation").css("backgroundColor",this.color.toHex(!0,this.color.value.h,1,1,1)),this.picker.find(".colorpicker-alpha").css("backgroundColor",this.color.toHex(!0)),this.picker.find(".colorpicker-color, .colorpicker-color div").css("backgroundColor",this.color.toString(!0,this.format)),a},updateComponent:function(a){var b;if(b="undefined"!=typeof a?this.createColor(a):this.color,this.component!==!1){var c=this.component.find("i").eq(0);c.length>0?c.css({backgroundColor:b.toString(!0,this.format)}):this.component.css({backgroundColor:b.toString(!0,this.format)})}return b.toString(!1,this.format)},update:function(a){var b;return this.getValue(!1)===!1&&a!==!0||(b=this.updateComponent(),this.updateInput(b),this.updateData(b),this.updatePicker()),b},setValue:function(a){this.color=this.createColor(a),this.update(!0),this.element.trigger({type:"changeColor",color:this.color,value:a})},createColor:function(a){return new b(a?a:null,this.options.colorSelectors,this.options.fallbackColor?this.options.fallbackColor:this.color,this.options.fallbackFormat,this.options.hexNumberSignPrefix)},getValue:function(a){a="undefined"==typeof a?this.options.fallbackColor:a;var b;return b=this.hasInput()?this.input.val():this.element.data("color"),void 0!==b&&""!==b&&null!==b||(b=a),b},hasInput:function(){return this.input!==!1},isDisabled:function(){return this.disabled},disable:function(){return this.hasInput()&&this.input.prop("disabled",!0),this.disabled=!0,this.element.trigger({type:"disable",color:this.color,value:this.getValue()}),!0},enable:function(){return this.hasInput()&&this.input.prop("disabled",!1),this.disabled=!1,this.element.trigger({type:"enable",color:this.color,value:this.getValue()}),!0},currentSlider:null,mousePointer:{left:0,top:0},mousedown:function(b){!b.pageX&&!b.pageY&&b.originalEvent&&b.originalEvent.touches&&(b.pageX=b.originalEvent.touches[0].pageX,b.pageY=b.originalEvent.touches[0].pageY),b.stopPropagation(),b.preventDefault();var c=a(b.target),d=c.closest("div"),e=this.options.horizontal?this.options.slidersHorz:this.options.sliders;if(!d.is(".colorpicker")){if(d.is(".colorpicker-saturation"))this.currentSlider=a.extend({},e.saturation);else if(d.is(".colorpicker-hue"))this.currentSlider=a.extend({},e.hue);else{if(!d.is(".colorpicker-alpha"))return!1;this.currentSlider=a.extend({},e.alpha)}var f=d.offset();this.currentSlider.guide=d.find("i")[0].style,this.currentSlider.left=b.pageX-f.left,this.currentSlider.top=b.pageY-f.top,this.mousePointer={left:b.pageX,top:b.pageY},a(window.document).on({"mousemove.colorpicker":a.proxy(this.mousemove,this),"touchmove.colorpicker":a.proxy(this.mousemove,this),"mouseup.colorpicker":a.proxy(this.mouseup,this),"touchend.colorpicker":a.proxy(this.mouseup,this)}).trigger("mousemove")}return!1},mousemove:function(a){!a.pageX&&!a.pageY&&a.originalEvent&&a.originalEvent.touches&&(a.pageX=a.originalEvent.touches[0].pageX,a.pageY=a.originalEvent.touches[0].pageY),a.stopPropagation(),a.preventDefault();var b=Math.max(0,Math.min(this.currentSlider.maxLeft,this.currentSlider.left+((a.pageX||this.mousePointer.left)-this.mousePointer.left))),c=Math.max(0,Math.min(this.currentSlider.maxTop,this.currentSlider.top+((a.pageY||this.mousePointer.top)-this.mousePointer.top)));return this.currentSlider.guide.left=b+"px",this.currentSlider.guide.top=c+"px",this.currentSlider.callLeft&&this.color[this.currentSlider.callLeft].call(this.color,b/this.currentSlider.maxLeft),this.currentSlider.callTop&&this.color[this.currentSlider.callTop].call(this.color,c/this.currentSlider.maxTop),this.options.format!==!1||"setAlpha"!==this.currentSlider.callTop&&"setAlpha"!==this.currentSlider.callLeft||(1!==this.color.value.a?(this.format="rgba",this.color.origFormat="rgba"):(this.format="hex",this.color.origFormat="hex")),this.update(!0),this.element.trigger({type:"changeColor",color:this.color}),!1},mouseup:function(b){return b.stopPropagation(),b.preventDefault(),a(window.document).off({"mousemove.colorpicker":this.mousemove,"touchmove.colorpicker":this.mousemove,"mouseup.colorpicker":this.mouseup,"touchend.colorpicker":this.mouseup}),!1},change:function(a){this.color=this.createColor(this.input.val()),this.color.origFormat&&this.options.format===!1&&(this.format=this.color.origFormat),this.getValue(!1)!==!1&&(this.updateData(),this.updateComponent(),this.updatePicker()),this.element.trigger({type:"changeColor",color:this.color,value:this.input.val()})},keyup:function(a){38===a.keyCode?(this.color.value.a<1&&(this.color.value.a=Math.round(100*(this.color.value.a+.01))/100),this.update(!0)):40===a.keyCode&&(this.color.value.a>0&&(this.color.value.a=Math.round(100*(this.color.value.a-.01))/100),this.update(!0)),this.element.trigger({type:"changeColor",color:this.color,value:this.input.val()})}},a.colorpicker=d,a.fn.colorpicker=function(b){var c=Array.prototype.slice.call(arguments,1),e=1===this.length,f=null,g=this.each(function(){var e=a(this),g=e.data("colorpicker"),h="object"==typeof b?b:{};g||(g=new d(this,h),e.data("colorpicker",g)),"string"==typeof b?a.isFunction(g[b])?f=g[b].apply(g,c):(c.length&&(g[b]=c[0]),f=g[b]):f=e});return e?f:g},a.fn.colorpicker.constructor=d});
;
(function ($) {

  $.fn.rating = function () {

    var element;

    // A private function to highlight a star corresponding to a given value
    function _paintValue(ratingInput, value, active_icon, inactive_icon) {
      var selectedStar = $(ratingInput).find('[data-value="' + value + '"]');
      selectedStar.removeClass(inactive_icon).addClass(active_icon);
      selectedStar.prevAll('[data-value]').removeClass(inactive_icon).addClass(active_icon);
      selectedStar.nextAll('[data-value]').removeClass(active_icon).addClass(inactive_icon);
    }

    // A private function to remove the highlight for a selected rating
    function _clearValue(ratingInput, active_icon, inactive_icon) {
      var self = $(ratingInput);
      self.find('[data-value]').removeClass(active_icon).addClass(inactive_icon);
    }

    // A private function to change the actual value to the hidden field
    function _updateValue(input, val) {
      input.val(val).trigger('change');
      if (val === input.data('empty-value')) {
        input.siblings('.rating-clear').hide();
      } else {
        input.siblings('.rating-clear').show();
      }
    }


    // Iterate and transform all selected inputs
    for (element = this.length - 1; element >= 0; element--) {

      var el, i,
        originalInput = $(this[element]),
        max = originalInput.data('max') || 5,
        min = originalInput.data('min') || 0,
        def_val = originalInput.val() || 0,
        lib = originalInput.data('icon-lib') || 'glyphicon',
        active = originalInput.data('active-icon') || 'glyphicon-star',
        inactive = originalInput.data('inactive-icon') || 'glyphicon-star-empty',
        clearable = originalInput.data('clearable') || null,
        clearable_i = originalInput.data('clearable-icon') || 'glyphicon-remove',
        stars = '';

      // HTML element construction
      for (i = min; i <= max; i++) {
        // Create <max> empty stars
        if(i  <= def_val){
          stars += ['<i class="',lib, ' ', active, '" data-value="', i, '"></i>'].join('');
          }
        else{
            stars += ['<i class="',lib, ' ', inactive, '" data-value="', i, '"></i>'].join('')
            }
      }
      // Add a clear link if clearable option is set
      if (clearable) {
          stars += [
          ' <a class="rating-clear" style="display:none;" href="javascript:void">',
          '<span class="',lib,' ',clearable_i,'"></span> ',
          clearable,
          '</a>'].join('');
      }

      // Clone with data and events the original input to preserve any additional data and event bindings.
      var newInput = originalInput.clone(true)
        .addClass('hidden')
        .data('max', max)
        .data('min', min)
        .data('icon-lib', lib)
        .data('active-icon', active)
        .data('inactive-icon', inactive);

      // Rating widget is wrapped inside a div
      el = [
        '<div class="rating-input">',
        stars,
        '</div>'].join('');

      // Replace original inputs HTML with the new one
      if (originalInput.parents('.rating-input').length <= 0) {
        originalInput.replaceWith($(el).append(newInput));
      }

    }

    // Give live to the newly generated widgets
    $('.rating-input')
      // Highlight stars on hovering
      .on('mouseenter', '[data-value]', function () {
        var self = $(this);
         input = self.siblings('input');
        _paintValue(self.closest('.rating-input'), self.data('value'), input.data('active-icon'), input.data('inactive-icon'));
      })
      // View current value while mouse is out
      .on('mouseleave', '[data-value]', function () {
        var self = $(this),
          input = self.siblings('input'),
          val = input.val(),
          min = input.data('min'),
          max = input.data('max'),
          active = input.data('active-icon'),
          inactive = input.data('inactive-icon');
        if (val >= min && val <= max) {
          _paintValue(self.closest('.rating-input'), val, active, inactive);
        } else {
          _clearValue(self.closest('.rating-input'), active, inactive);
        }
      })
      // Set the selected value to the hidden field
      .on('click', '[data-value]', function (e) {
        var self = $(this),
          val = self.data('value'),
          input = self.siblings('input');
        _updateValue(input,val);
        e.preventDefault();
        return false;
      })
      // Remove value on clear
      .on('click', '.rating-clear', function (e) {
        var self = $(this),
          input = self.siblings('input'),
          active = input.data('active-icon'),
          inactive = input.data('inactive-icon');
        _updateValue(input, input.data('empty-value'));
        _clearValue(self.closest('.rating-input'), active, inactive);
        e.preventDefault();
        return false;
      })
      // Initialize view with default value
      .each(function () {
        var input = $(this).find('input'),
          val = input.val(),
          min = input.data('min'),
          max = input.data('max');
        if (val !== "" && +val >= min && +val <= max) {
          _paintValue(this, val);
          $(this).find('.rating-clear').show();
        }
        else {
          input.val(input.data('empty-value'));
          _clearValue(this);
        }
      });

  };

  // Auto apply conversion of number fields with class 'rating' into rating-fields
  $(function () {
    if ($('input.rating[type=number]').length > 0) {
      $('input.rating[type=number]').rating();
    }
  });

}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.bootstrapButton = (function () {

        var exports = {

            showLoading: function (btn) {

                if (typeof (Page_ClientValidate) === 'function') {

                    if (typeof Page_IsValid === 'undefined' || Page_IsValid) {
                        // make sure page really is valid
                        Page_ClientValidate();
                    }
                }

                var $btn = $(btn);

                if (typeof Page_IsValid === 'undefined' || Page_IsValid) {
                    setTimeout(function () {
                        $btn.prop('disabled', true);
                        $btn.attr('disabled', 'disabled');
                        $btn.addClass('disabled');
                        $btn.html($btn.attr('data-loading-text'));
                    }, 0)
                }

                return true;
            },

            onCompleted: function (btn) {
                var $btn = $(btn);
                $btn.prop('disabled', true);
                $btn.attr('disabled', 'disabled');
                $btn.addClass('disabled');
                var completedTxt = $btn.attr('data-completed-text');
                if (completedTxt && completedTxt !== '') {
                    $btn.html(completedTxt);
                } else {
                    $btn.html($btn.attr('data-init-text'));
                }

                var completedMessage = $btn.attr('data-completed-message');
                if (completedMessage && completedMessage !== '') {
                    var id = $btn.attr("id") + "_msg";
                    var $span = $('<span />').attr('id', id).html(completedMessage);
                    $btn.after( $span );
                }

                var timeout = Number($btn.attr('data-timeout-text'));
                if ( timeout && !isNaN(timeout)) {
                    timeout = timeout * 1000;
                    setTimeout(function () {
                        $btn.prop('disabled', false);
                        $btn.removeAttr('disabled');
                        $btn.removeClass('disabled');
                        $btn.html($btn.attr('data-init-text'));
                        var id = $btn.attr("id") + "_msg";
                        var $span = $('#' + id);
                        if ($span){
                            $span.remove();
                        }
                    }, timeout);
                }
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.buttonDropDownList = (function () {
        var exports = {
            initialize: function (options) {
                if (!options.controlId) {
                    throw 'id is required';
                }

                var $control = $('#' + options.controlId);
                var $selectBtn = $control.find('.js-buttondropdown-btn-select');
                var $selectedId = $control.find('.js-buttondropdown-selected-id');
                var checkmarksEnabled = $control.attr('data-checkmarks-enabled') == 1;

                $('.dropdown-menu a', $control).on('click', function (e) {
                    var $el = $(this);
                    var text = $el.html();
                    var textHtml = $el.html() + " <span class='fa fa-caret-down' ></span >";
                    var idValue = $el.attr('data-id');
                    var postbackScript = $el.attr('data-postback-script');

                    if (checkmarksEnabled) {
                        $el.closest('.dropdown-menu').find('.js-selectionicon').removeClass('fa-check');
                        $el.find('.js-selectionicon').addClass('fa-check');
                    }
                    else {
                        $selectBtn.html(textHtml);
                    }

                    $selectedId.val(idValue);

                    if (postbackScript) {
                        window.location = postbackScript;
                    }
                });

            }
        };

        return exports;
    }());
}(jQuery));



;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.buttonGroup = (function () {
        var exports = {
            initialize: function (options) {
                if (!options.id) {
                    throw 'id is required';
                }

                var $buttonGroup = $('#' + options.id);
                var $buttonGroupItems = $buttonGroup.find('.js-buttongroup-item');
                var selectedItemClass = $buttonGroup.attr('data-selecteditemclass');
                var unselectedItemClass = $buttonGroup.attr('data-unselecteditemclass');

                $buttonGroupItems.on('click', function () {
                    var $selectedItem = $(this);
                    var $unselectedItems = $buttonGroupItems.not($selectedItem);
                    $unselectedItems.removeClass(selectedItemClass).addClass(unselectedItemClass);
                    $selectedItem.removeClass(unselectedItemClass).addClass(selectedItemClass);

                    // make sure the input elements onclick get executed so that the postback gets fired (if there is one)
                    var $input = $selectedItem.find('input');
                    var clickFunction = $input.prop('onclick');
                    if (clickFunction) {
                        clickFunction();
                    }
                });

            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    // Cloudflare Turnstile docs:
    // https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/

    Rock.controls.captcha = (function () {
        // Keep track of the last-rendered Turnstile widget ID for each Rock Captcha
        // control, so we can manage multiple Turnstile widgets per page.
        var turnstileIds = {};

        /**
         * Gets whether the Turnstile module is ready.
         * 
         * @returns Whether the Turnstile module is ready.
         */
        function isTurnstileReady() {
            return typeof turnstile !== 'undefined';
        }

        /**
         * Tries to render a Turnstile widget for a given Rock Captcha control instance.
         * 
         * @param {any} config The configuration info for this Rock Captcha control instance.
         */
        function tryRenderTurnstile(config) {
            if (!isTurnstileReady()) {
                return;
            }

            // Remove any previous widget for this Rock Captcha control to prevent
            // Turnstile from logging warnings/errors to the console.
            tryRemoveTurnstile(config.id);

            // Keep track of the last-rendered Turnstile widget's ID, as we'll
            // need to intstruct the Turnstile library not to track it anymore
            // (and we'll create a new one) for each request. Note that once the
            // challenge has been solved, it's up to the Rock block or parent
            // control to dictate that a Turnstile should no longer be rendered,
            // so our job here is to simply keep rendering a new widget on every
            // request, regardless of whether this is a full or partial postback.
            var turnstileId = turnstile.render('#' + config.id, {
                siteKey: config.key,
                callback: function (token) {
                    var $hfToken = $('#' + config.id + '_hfToken');
                    $hfToken.val(token);

                    if (token) {
                        // Allow time for the success animation to be displayed.
                        setTimeout(function () {
                            if (config.postBackScript) {
                                window.location = "javascript:" + config.postBackScript;
                            }
                        }, 750);
                    }
                },
                'error-callback': function (errorCode) {
                    if (errorCode === '300030') {
                        // This can happen if a Turnstile widget was loaded into
                        // the DOM and is no longer available (i.e. after a
                        // partial postback results in the Rock Captcha
                        // control no longer being visible).
                        tryRemoveTurnstile(config.id);

                        // Return true to prevent Turnstile from repeatedly logging to the console.
                        // https://developers.cloudflare.com/turnstile/troubleshooting/client-side-errors/#error-handling
                        return true;

                        // However, there is an issue where an already-queued
                        // `.reset()` invocation will fail and still log an
                        // error to the console; hopefully this scenario is
                        // not common.
                        // https://community.cloudflare.com/t/window-turnstile-remove-with-retry-set-to-auto-doesnt-cleartimeout/525530
                    }
                }
            });

            turnstileIds[config.id] = turnstileId;
        }

        /**
         * Tries to remove a Turnstile widget for the provided Rock Captcha control ID.
         * 
         * @param {any} rockCaptchaId The Rock Captcha control ID.
         */
        function tryRemoveTurnstile(rockCaptchaId) {
            // Try to find this Rock Captcha control's last-rendered Turnstile widget.
            var turnstileId = turnstileIds[rockCaptchaId];
            if (turnstileId) {
                // Inform the Turnstile library that the old Turnstile widget
                // should no longer be tracked.
                turnstile.remove(turnstileId);
                delete turnstileIds[rockCaptchaId];
            }
        }

        var exports = {
            initialize: function (config) {
                if (!config) {
                    return;
                }

                if (!config.id) {
                    throw 'id is required';
                }

                if (!config.key) {
                    throw 'key is required';
                }

                // Ensure Cloudflare's Turnstile script is added to the page.
                if (!$('#rockCloudflareTurnstile').length && !isTurnstileReady()) {
                    // By default, jQuery adds a cache-busting parameter on dynamically-added scripts.
                    // Set `cache: true` to avoid this, and ensure we only load the script once.
                    $.ajaxSetup({ cache: true });
                    $.getScript('https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit')
                        .done(function () {
                            tryRenderTurnstile(config);
                        })
                        .fail(function () {
                            console.error("Unable to load Captcha library.");
                        });

                    return;
                }

                // The Turnstile script was already added by the C# control;
                // just wait for the DOM to finish loading.
                $(document).ready(function () {
                    tryRenderTurnstile(config);
                });
            },
            clientValidate: function (validator, args) {
                var $validator = $(validator);
                var isRequired = $validator.data('required');

                var rockCaptchaId = $validator.data('captcha-id');
                var $formGroup = $('#' + rockCaptchaId).closest('.form-group');

                if (isRequired && isTurnstileReady()) {
                    // Get this validator control's related Rock Captcha control ID and
                    // try to find the underlying Turnstile widget.
                    var turnstileId = turnstileIds[rockCaptchaId];
                    if (turnstileId) {
                        var turnstileResponse = turnstile.getResponse(turnstileId);
                    }
                }

                var isValid = !!(!isRequired || turnstileResponse);

                if (isValid) {
                    args.IsValid = true;
                    $formGroup.removeClass('has-error');
                }
                else {
                    args.IsValid = false;
                    $formGroup.addClass('has-error');
                }
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};

    Rock.controls.charts = (function () {
        var exports = {

            ///
            /// handles putting chartData into a Line/Bar/Points chart
            ///
            plotChartData: function (chartData, chartOptions, plotSelector, yaxisLabelText, getSeriesPartitionNameUrl, combineValues) {

                var chartSeriesLookup = {};
                var chartSeriesList = [];

                var chartGoalPoints = {
                    label: yaxisLabelText + ' Goal',
                    chartData: [],
                    data: []
                }

                if (chartOptions.customSettings) {
                    chartGoalPoints.color = chartOptions.customSettings.goalSeriesColor;
                }

                // populate the chartMeasurePoints data array with data from the REST result
                for (var i = 0; i < chartData.length; i++) {
                    if (chartData[i].MetricValueType && chartData[i].MetricValueType == 1) {
                        // if the chartdata is marked as a Metric Goal Value Type, populate the chartGoalPoints
                        chartGoalPoints.data.push([chartData[i].DateTimeStamp, chartData[i].YValue]);
                        chartGoalPoints.chartData.push(chartData[i]);
                    }
                    else {
                        var lookupKey = chartData[i].MetricValuePartitionEntityIds;
                        if (!lookupKey || lookupKey == '') {
                            lookupKey = chartData[i].SeriesName;
                        }

                        if (!chartSeriesLookup[lookupKey]) {

                            // If SeriesName is specified, that can be the name of the series if MetricValuePartitionEntityIds is blank
                            var seriesName = chartData[i].SeriesName;

                            // if we aren't combining values, we'll have to lookup the series name based on MetricValuePartitionEntityIds
                            if (chartData[i].MetricValuePartitionEntityIds && chartData[i].MetricValuePartitionEntityIds != '' && !combineValues)
                            {
                                // MetricValuePartitionEntityIds is not blank so get the seriesName from the getSeriesPartitionNameUrl
                                if (getSeriesPartitionNameUrl) {
                                    var metricValuePartitionEntityIdDataJSON = JSON.stringify(chartData[i].MetricValuePartitionEntityIds.split(','));

                                    $.ajax({
                                        type: 'POST',
                                        url: getSeriesPartitionNameUrl,
                                        data: metricValuePartitionEntityIdDataJSON,
                                        async: false,
                                        success: null,
                                        contentType: 'application/json'
                                    })
                                    .done(function (data) {
                                        seriesName = data;
                                    });
                                }
                            }

                            // either either seriesName, yaxisLabelText or just 'value' if the seriesname isn't defined
                            seriesName = seriesName || yaxisLabelText || 'value';

                            chartSeriesLookup[lookupKey] = {
                                label: seriesName,
                                chartData: [],
                                data: []
                            };
                        }

                        chartSeriesLookup[lookupKey].data.push([chartData[i].DateTimeStamp, chartData[i].YValue]);
                        chartSeriesLookup[lookupKey].chartData.push(chartData[i]);
                    }
                }

                // setup the series list (goal points last, if they exist)
                for (var chartSeriesKey in chartSeriesLookup) {
                    var chartMeasurePoints = chartSeriesLookup[chartSeriesKey];
                    if (chartMeasurePoints.data.length) {
                        chartSeriesList.push(chartMeasurePoints);
                    }
                }

                if (combineValues && chartSeriesList.length != 1) {

                    var combinedDataLookup = {};
                    var chartMeasurePoints = null;

                    for (var chartMeasurePointsId in chartSeriesList) {

                        chartMeasurePoints = chartSeriesList[chartMeasurePointsId];

                        $.each(chartMeasurePoints.data, function (indexInArray, dataPair) {
                            var dateTimeIndex = dataPair[0];
                            var combinedItem = combinedDataLookup[dateTimeIndex];
                            if (combinedItem) {
                                // sum the YValue of the .data
                                combinedItem.chartData.YValue += chartMeasurePoints.chartData[indexInArray].YValue;
                                combinedItem.data[1] += dataPair[1];
                            }
                            else {
                                combinedDataLookup[dateTimeIndex] = {
                                    chartData: chartMeasurePoints.chartData[indexInArray],
                                    data: dataPair
                                };
                            }
                        });
                    }

                    var combinedData = [];
                    var combinedChartData = [];

                    for (var dataLookupId in combinedDataLookup) {
                        combinedData.push(combinedDataLookup[dataLookupId].data);
                        combinedChartData.push(combinedDataLookup[dataLookupId].chartData);
                    }

                    chartSeriesList = [];

                    // sort data after combining
                    combinedChartData.sort(function (item1, item2) { return item2.DateTimeStamp - item1.DateTimeStamp });
                    combinedData.sort(function (item1, item2) { return item2[0] - item1[0]; });

                    var chartCombinedMeasurePoints = {
                        label: yaxisLabelText + ' Total',
                        chartData: combinedChartData,
                        data: combinedData
                    };


                    chartSeriesList.push(chartCombinedMeasurePoints);
                }

                if (chartGoalPoints.data.length) {
                    chartSeriesList.push(chartGoalPoints);
                }

                // plot the chart
                if (chartSeriesList.length > 0) {
                    $.plot(plotSelector, chartSeriesList, chartOptions);
                }
                else {
                    $(plotSelector).html('<div class="alert alert-info">No Data Found</div>');
                }
            },

            ///
            /// handles putting chart data into a piechart
            ///
            plotPieChartData: function (chartData, chartOptions, plotSelector, getSeriesPartitionNameUrl) {
                var pieData = [];

                // populate the chartMeasurePoints data array with data from the REST result for pie data
                for (var i = 0; i < chartData.length; i++) {

                    pieData.push({
                        label: chartData[i].MetricTitle,
                        data: chartData[i].YValueTotal,
                        chartData: [chartData[i]]
                    });
                }

                if (pieData.length > 0) {
                    // plot the pie chart
                    $.plot(plotSelector, pieData, chartOptions);
                }
                else {
                    $(plotSelector).html('<div class="alert alert-info">No Data Found</div>');
                }
            },

            ///
            /// handles putting chart data into a bar chart
            ///
            plotBarChartData: function (chartData, chartOptions, plotSelector, getSeriesPartitionNameUrl) {
                var barData = [];
                var combinedData = {};
                var seriesLabels = [];
                var seriesNameLookup = {};

                // populate the chartMeasurePoints data array with data from the REST result for bar chart data
                for (var i = 0; i < chartData.length; i++) {

                    var seriesCategory = chartData[i].SeriesName;
                    if (chartData[i].MetricValuePartitionEntityIds)
                    {
                        if (!seriesNameLookup[chartData[i].MetricValuePartitionEntityIds])
                        {
                            // MetricValuePartitionEntityIds is not blank so get the seriesName from the getSeriesPartitionNameUrl
                            if (getSeriesPartitionNameUrl) {
                                var metricValuePartitionEntityIdDataJSON = JSON.stringify(chartData[i].MetricValuePartitionEntityIds.split(','));

                                $.ajax({
                                    type: 'POST',
                                    url: getSeriesPartitionNameUrl,
                                    data: metricValuePartitionEntityIdDataJSON,
                                    async: false,
                                    success: null,
                                    contentType: 'application/json'
                                })
                                .done(function (data) {
                                    seriesNameLookup[chartData[i].MetricValuePartitionEntityIds] = data;
                                });
                            }
                        }

                        seriesCategory = seriesNameLookup[chartData[i].MetricValuePartitionEntityIds] || seriesCategory;
                    }

                    if (!combinedData[seriesCategory])
                    {
                        combinedData[seriesCategory] = 0;
                    }

                    combinedData[seriesCategory] += chartData[i].YValue;
                }

                var seriesId = 0;
                for (var combinedDataKey in combinedData) {

                    barData.push([combinedDataKey, combinedData[combinedDataKey]]);
                    seriesId++;
                    seriesLabels.push(combinedDataKey);
                }

                seriesLabels.push(seriesCategory);

                if (barData.length > 0) {
                    // plot the bar chart
                    chartOptions.series.chartData = chartData;
                    chartOptions.series.labels = seriesLabels;
                    $.plot(plotSelector, [barData], chartOptions);
                }
                else {
                    $(plotSelector).html('<div class="alert alert-info">No Data Found</div>');
                }
            },

            ///
            /// attaches a bootstrap style tooltip to the 'plothover' of a chart
            ///
            bindTooltip: function (plotContainerId, tooltipFormatter) {
                // setup of bootstrap tooltip which we'll show on the plothover event
                var toolTipId = 'tooltip_' + plotContainerId;
                var chartContainer = '#' + plotContainerId;

                $("<div id=" + toolTipId + " class='tooltip top'><div class='tooltip-inner'></div><div class='tooltip-arrow'></div></div>").css({
                    position: 'absolute',
                    display: 'none',
                }).appendTo('body');

                var $toolTip = $('#' + toolTipId);

                $(chartContainer).bind('plothover', function (event, pos, item) {

                    if (item) {

                        var tooltipText = "";

                        // if a tooltipFormatter is specified, use that
                        if (tooltipFormatter) {
                            tooltipText = tooltipFormatter(item);
                        }
                        else {
                            if (item.series.chartData) {
                                if (item.series.chartData[item.dataIndex].DateTimeStamp) {
                                    tooltipText = new Date(item.series.chartData[item.dataIndex].DateTimeStamp).toLocaleDateString();
                                };

                                if (item.series.chartData[item.dataIndex].StartDateTimeStamp) {
                                    tooltipText = new Date(item.series.chartData[item.dataIndex].StartDateTimeStamp).toLocaleDateString();
                                }

                                if (item.series.chartData[item.dataIndex].EndDateTimeStamp) {
                                    tooltipText += " to " + new Date(item.series.chartData[item.dataIndex].EndDateTimeStamp).toLocaleDateString();
                                }

                                if (item.series.chartData[item.dataIndex].MetricTitle) {
                                    tooltipText = item.series.chartData[item.dataIndex].MetricTitle;
                                }
                            }

                            if (tooltipText) {
                                tooltipText += '<br />';
                            }

                            if (item.series.label) {
                                tooltipText += item.series.label;
                            }

                            if (item.series.chartData) {
                                var pointValue = item.series.chartData[item.dataIndex].YValueFormatted || item.series.chartData[item.dataIndex].YValue || item.series.chartData[item.dataIndex].YValueTotal || '';

                                tooltipText += ': ' + pointValue;

                                if (item.series.chartData[item.dataIndex].Note) {
                                    tooltipText += '<br />' + item.series.chartData[item.dataIndex].Note;
                                }
                            }
                        }

                        $toolTip.find('.tooltip-inner').html(tooltipText);

                        var tipTop = pos.pageY - $toolTip.height() - 10;

                        var windowWidth = $(window).width();
                        var tooltipWidth = $toolTip.width();
                        var tipLeft = pos.pageX - ( tooltipWidth / 2);
                        if (tipLeft + tooltipWidth + 10 >= windowWidth) {
                            tipLeft = tipLeft - (tooltipWidth / 2);
                            $toolTip.removeClass("top");
                            $toolTip.addClass("left");
                        }
                        else {
                            $toolTip.removeClass("left");
                            $toolTip.addClass("top");
                        }

                        $toolTip.css({ top: tipTop, left: tipLeft, opacity: 1 });
                        $toolTip.show();
                    }
                    else {
                        $toolTip.hide();
                    }
                });

            }
        };

        return exports;
    }());
}(jQuery));


;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.colorSelector = (function () {

        // #region Utilities

        var ColorUtilities = (function () {
            /**
             * Detects if two RGB colors are similar.
             * @param r1 {number} Red 1 color (0-255).
             * @param g1 {number} Green 1 color (0-255).
             * @param b1 {number} Blue 1 color (0-255).
             * @param r2 {number} Red 2 color (0-255).
             * @param g2 {number} Green 2 color (0-255).
             * @param b2 {number} Blue 2 color (0-255).
             * @param percentSimilar {number|undefined} The percent (0 - 1) that determines if the colors are similar. 
             * @returns {Boolean} `true` if the numbers are similar; otherwise, `false`.
             */
            function areColorsSimilar(r1, g1, b1, r2, g2, b2, percentSimilar) {
                if (typeof percentSimilar === "undefined") {
                    percentSimilar = 0.99;
                }

                var actualDistance = getColorDistance(r1, g1, b1, r2, g2, b2);

                var actualPercent = (maxLumaDistance - actualDistance) / maxLumaDistance;

                // Use the specified percent to determine if the colors are similar.
                return actualPercent >= percentSimilar;
            }

            /**
             * Gets the distance between two RGB colors.
             * @param r1 {number} Red 1 color (0-255).
             * @param g1 {number} Green 1 color (0-255).
             * @param b1 {number} Blue 1 color (0-255).
             * @param r2 {number} Red 2 color (0-255).
             * @param g2 {number} Green 2 color (0-255).
             * @param b2 {number} Blue 2 color (0-255).
             * @returns {number} The distance between two RGB colors.
             */
            function getColorDistance(r1, g1, b1, r2, g2, b2) {
                var redDiffSquared = Math.pow(r2 - r1, 2);
                var greenDiffSquared = Math.pow(g2 - g1, 2);
                var blueDiffSquared = Math.pow(b2 - b1, 2);
                var redAverage = (r2 + r1) / 2;

                return Math.sqrt(
                    (2 * redDiffSquared) +
                    (4 * greenDiffSquared) +
                    (3 * blueDiffSquared) +
                    (redAverage * (redDiffSquared - blueDiffSquared) / 256)
                );
            }

            /**
             * Converts a hex color (#FFFFFF) to an RGB color. 
             * @param {string} hexValue The hex color to convert.
             * @returns {Number[]} The converted RGB color.
             */
            function hexToRgba(hexValue) {
                var hex3Or4 = /^#[0-9a-fA-F]{3}([0-9a-fA-F])?$/;
                if (hex3Or4.test(hexValue)) {
                    var rHex = hexValue[1];
                    var gHex = hexValue[2];
                    var bHex = hexValue[3];
                    var aHex = hexValue[4] || "f";
                    // A 3-digit hex color #123 is equivalent to the 6-digit hex color #112233,
                    // where each color channel (r, g, and b) is repeated.
                    return [
                        parseInt(rHex + rHex, 16),
                        parseInt(gHex + gHex, 16),
                        parseInt(bHex + bHex, 16),
                        parseInt(aHex + aHex, 16)
                    ];
                }

                var hex6Or8 = /^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/;
                if (hex6Or8.test(hexValue)) {
                    var rHex = hexValue.substring(1, 3);
                    var gHex = hexValue.substring(3, 5);
                    var bHex = hexValue.substring(5, 7);
                    var aHex = hexValue.substring(7) || "ff";
                    return [
                        parseInt(rHex, 16),
                        parseInt(gHex, 16),
                        parseInt(bHex, 16),
                        Math.round(parseInt(aHex, 16) / 255)
                    ];
                }

                // Return transparent if not a valid hex color.
                return [0, 0, 0, 0];
            }

            /**
             * Returns the perceived brightness of an RGB color.
             * @param r {number} Red color (0-255).
             * @param g {number} Green color (0-255).
             * @param b {number} Blue color (0-255).
             * @returns {Number[]}
             */
            function rgbToLuma(r, g, b) {
                /**
                 * To get a color's perceived brightness,
                 * each color component is multiplied by a different constant.
                 * The constants are weighted differently based on the perceived brightness by the human eye
                 * --green is perceived as the brightest color, then red, then blue (they all add up to 1).
                 *
                 * The result will be a color in the color space:
                 * [0, 0, 0] to [0.2126 * 255, 0.7152 * 255, 0.0722 * 255]
                 * or...
                 * [0, 0, 0] to [54.213, 182.376, 18.411]
                 */

                return [0.2126 * r, 0.7152 * g, 0.0722 * b];
            }

            /**
             * Returns the RGBA number array from a CSS "rgba(r, g, b, a)" string.
             * @param rgbFunc {string} The "rgb(r, g, b)" string.
             * @returns {Number[]} An RGBA array [r, b, b, a].
             */
            function rgbFuncToRgba(rgbFunc) {
                var rgb = /^rgb[a]?\s*\(\s*(\d+)[, ]\s*(\d+)[, ]\s*(\d+)\)$/;
                var rgbMatches = rgbFunc.match(rgb);
                if (rgbMatches && rgbMatches.length) {
                    return [
                        parseInt(rgbMatches[1]),
                        parseInt(rgbMatches[2]),
                        parseInt(rgbMatches[3]),
                        1
                    ];
                }

                var rgba = /^rgb[a]?\s*\(\s*(\d+)[, ]\s*(\d+)[, ]\s*(\d+)[, ]\s*(\d*(\.\d+)?)\)$/;
                var rgbaMatches = rgbFunc.match(rgba);
                if (rgbaMatches && rgbMatches.length) {
                    return [
                        parseInt(rgbaMatches[1]),
                        parseInt(rgbaMatches[2]),
                        parseInt(rgbaMatches[3]),
                        Number(rgbaMatches[4])
                    ];
                }

                // Return transparent by default.
                return [0, 0, 0, 0];
            }

            return {
                areColorsSimilar,
                getColorDistance,
                hexToRgba,
                rgbToLuma,
                rgbFuncToRgba
            }
        })();

        // #endregion

        // #region Types

        /**
         * The ColorSelector initialization options.
         * @typedef {Object} ColorSelectorInitOptions
         * @property {string} controlId The control ID for the color selector (without leading #).
         * @property {boolean} allowMultiple When true, the Color Selector will behave like a checkbox list; otherwise, it will behave like a radiobutton list.
         */

        // #endregion 

        // #region Constants

        var colorSimilarityPercentage = Object.freeze(0.95);
        var maxLuma = Object.freeze(ColorUtilities.rgbToLuma(255, 255, 255));
        var maxLumaDistance = Object.freeze(ColorUtilities.getColorDistance(maxLuma[0], maxLuma[1], maxLuma[2], 0, 0, 0));
        var camouflagedLightClass = Object.freeze("camouflaged-light");
        var camouflagedDarkClass = Object.freeze("camouflaged-dark");

        // #endregion

        // #region Classes

        // #region Constructor

        /**
         * Creates a new instance of the ColorSelector class.
         * @param {ColorSelectorInitOptions} options
         */
        function ColorSelector(options) {
            if (!options.controlId) {
                throw "`controlId` is required.";
            }

            this.allowMultiple = !!options.allowMultiple;
            this.controlId = options.controlId;

            // Set JQuery selectors.
            this.$colorSelector = $("#" + this.controlId);
            this.$colorSelectorItemContainers = this.$colorSelector.children(".checkbox");

            // Set defaults for background color and perceived brightness.
            // These will be overwritten during initialization.
            this.backgroundColor = [255, 255, 255];
            this.backgroundLuma = maxLuma;
        }

        // #endregion

        // #region Methods

        /**
         * Initializes the color selector.
         */
        ColorSelector.prototype.initialize = function () {
            var colorSelectorElement = this.$colorSelector[0];

            if (!colorSelectorElement) {
                throw "Unable to find color selector element with control ID " + this.controlId;
            }

            // Set the background color of the color selector.
            this.backgroundColor = getInheritedBackgroundColor(colorSelectorElement);

            // Set the perceived brightness of the color picker.
            // The perceived brightness is better for determining
            // when two colors are similar to the human eye
            // instead of comparing RGB values.
            this.backgroundLuma = ColorUtilities.rgbToLuma(this.backgroundColor[0], this.backgroundColor[1], this.backgroundColor[2]);

            var $colorSelectorItemContainers = this.$colorSelectorItemContainers;

            // Initialize the color items.
            var checkedFound = false;
            for (var i = 0; i < $colorSelectorItemContainers.length; i++) {
                var $colorSelectorItemContainer = $($colorSelectorItemContainers[i]);

                // If only one selection is allowed, then only leave the first checked input as checked,
                // and uncheck the others.
                if (!this.allowMultiple) {
                    var $input = findInput($colorSelectorItemContainer);

                    if ($input.prop("checked")) {
                        if (checkedFound) {
                            // A checked input was already found so mark this input as unchecked.
                            $input.prop("checked", false);
                        }

                        checkedFound = true;
                    }
                }

                initializeItem(this, $colorSelectorItemContainer);
            }
        };

        // #endregion

        // #region Private Functions

        /**
         * Finds the checkbox input for a color selector item container.
         * @param {JQuery<HTMLElement>} $colorSelectorItemContainer
         * @returns {JQuery<HTMLInputElement>}
         */
        function findInput($colorSelectorItemContainer) {
            return $colorSelectorItemContainer.find('input[type="checkbox"]');
        }

        /**
         * Initializes the color items.
         * @param {ColorSelector} colorSelector
         * @param {JQuery<HTMLElement>} $colorSelectorItemContainer
         */
        function initializeItem(colorSelector, $colorSelectorItemContainer) {
            $colorSelectorItemContainer.addClass("color-selector-item-container");

            var $checkboxLabel = $colorSelectorItemContainer.find("label");
            $checkboxLabel.addClass("color-selector-item");

            var $checkboxInput = findInput($colorSelectorItemContainer);
            $checkboxInput.addClass("color-selector-item-checkbox");

            styleInput($checkboxInput);

            // Update the checked state whenever it changes.
            $checkboxInput.on("click", function () {
                onCheckboxCheckChanged(colorSelector, $(this));
            });

            // Remove the .checkbox CSS class as we don't want any checkbox styling.
            $colorSelectorItemContainer.removeClass("checkbox");

            // Set the label background color to the color found in the label text.
            var $labelText = $checkboxLabel.find(".label-text");
            var itemHexColor = $labelText.text();
            $checkboxLabel.css("background-color", itemHexColor);

            // Remove the label text so just the color is displayed.
            $labelText.remove();

            // Determine if the item needs a border to separate it from the background.
            var itemRgb = ColorUtilities.hexToRgba(itemHexColor);
            var itemLuma = ColorUtilities.rgbToLuma(itemRgb[0], itemRgb[1], itemRgb[2]);

            if (ColorUtilities.areColorsSimilar(colorSelector.backgroundLuma[0], colorSelector.backgroundLuma[1], colorSelector.backgroundLuma[2], itemLuma[0], itemLuma[1], itemLuma[2], colorSimilarityPercentage)) {
                $checkboxLabel.addClass(getCamouflagedClass(itemLuma[0], itemLuma[1], itemLuma[2]));
            }
        }

        /**
         * Handles the color item checkbox changed event.
         * @param {ColorSelector} colorSelector The color selector.
         * @param {JQuery<HTMLInputElement>} $checkboxInput The checkbox input.
         */
        function onCheckboxCheckChanged(colorSelector, $checkboxInput) {
            styleInput($checkboxInput);

            if (!colorSelector.allowMultiple) {
                // For single select (radio) bevahior, clicking a checkbox should always check it.
                // If the checkbox is already checked, then return false to prevent the default behavior of unchecking it.

                // Uncheck all other checkboxes.
                var $colorSelectorItemContainers = colorSelector.$colorSelectorItemContainers;

                for (var i = 0; i < $colorSelectorItemContainers.length; i++) {
                    var $otherItemInput = findInput($($colorSelectorItemContainers[i]));

                    if ($otherItemInput[0] !== $checkboxInput[0]) {
                        $otherItemInput.prop("checked", false);
                        styleInput($otherItemInput);
                    }
                }
            }
        }

        /**
         * 
         * @param {JQuery<HTMLInputElement>} $input
         * @param {JQuery<HTMLElement | undefined>} $label
         * @param {JQuery<HTMLElement | undefined} $container
         */
        function styleInput($input, $label, $container) {
            if (!$label) {
                $label = $input.parent("label");
            }

            if (!$container) {
                $container = $input.closest(".color-selector-item-container");
            }

            if ($input.prop("checked")) {
                $label.addClass("checked");
                $container.addClass("checked");
            }
            else {
                $label.removeClass("checked");
                $container.removeClass("checked");
            }
        }

        /**
         * Gets the camouflaged CSS class for a luma color (rL, gL, bL).
         * @param {number} rLuma The R luma color.
         * @param {number} gLuma The G luma color.
         * @param {number} bLuma The B luma color.
         * @returns {string} The camouflaged CSS class for a luma color.
         */
        function getCamouflagedClass(rLuma, gLuma, bLuma) {
            var luma = rLuma + gLuma + bLuma;
            if (luma > 128) { // Max is 255
                return camouflagedLightClass;
            }
            else {
                return camouflagedDarkClass;
            }
        }

        /**
         * Gets the first RGBA background color that isn't the default,
         * starting from the element and working its way up the
         * parent element chain.
         * @param {Element} el
         * @returns {Number[]} The first RGBA background color that isn't the default; otherwise, the default color is returned.
         */
        function getInheritedBackgroundColor(el) {

            function getDefaultBackground() {
                var div = document.createElement("div");
                document.head.appendChild(div);
                var bg = window.getComputedStyle(div).backgroundColor;
                document.head.removeChild(div);
                return bg;
            }

            // Get default style for current browser.
            var defaultStyle = getDefaultBackground(); // typically "rgba(0, 0, 0, 0)"

            // Start with the supplied element.
            var elementsToProcess = [
                el
            ];

            while (elementsToProcess.length) {
                var elementToProcess = elementsToProcess.shift();

                if (!elementToProcess) {
                    // Skip to the next element if the current one is nullish.
                    continue;
                }

                var backgroundColor = window.getComputedStyle(elementToProcess).backgroundColor;

                // If we got a real value, return it.
                if (backgroundColor != defaultStyle) {
                    return ColorUtilities.rgbFuncToRgba(backgroundColor);
                }
                // Otherwise, process the next parent element.
                else if (elementToProcess.parentElement) {
                    elementsToProcess.push(elementToProcess.parentElement);
                }
            }

            // If we have reached the top parent element without getting an explicit color,
            // return the default style.
            return ColorUtilities.rgbFuncToRgba(defaultStyle);
        }

        // #endregion

        // #endregion

        // Keeps track of ColorSelector instances. 
        var colorSelectors = {};

        var exports = {
            /**
             * Initializes a new ColorSelector instance.
             * @param {ColorSelectorInitOptions} options
             * @returns {ColorSelector}
             */
            initialize: function (options) {
                var cs = new ColorSelector(options);
                cs.initialize();

                colorSelectors[options.controlId] = cs;

                return cs;
            },

            /**
             * Gets a ColorSelector instance by control ID.
             * @param {string} controlId The control ID.
             * @returns {ColorSelector}
             */
            get: function(controlId) {
                return colorSelectors[controlId];
            },

            /**
             * Sets the camouflaged class if the element color is similar to the target element (or parent if not set).
             * @param {string} elementSelector The selector of the element to compare.
             * @param {string} targetSelector (Optional) The selector of the target element with which to compare colors. Defaults to the parent element.
             */
            setCamouflagedClass: function (elementSelector, targetSelector) {
                var element = document.querySelector(elementSelector);
                var target = targetSelector ? document.querySelector(targetSelector) : element.parentElement;

                var elementColor = getInheritedBackgroundColor(element);
                var targetColor = getInheritedBackgroundColor(target);

                var elementLuma = ColorUtilities.rgbToLuma(elementColor[0], elementColor[1], elementColor[2]);
                var targetLuma = ColorUtilities.rgbToLuma(targetColor[0], targetColor[1], targetColor[2]);

                var isSamePerceivedBrightness = (
                    elementLuma[0] === targetLuma[0]
                    && elementLuma[1] === targetLuma[1]
                    && elementLuma[2] === targetLuma[2]
                );

                // Short-circuit if elements have the same color.
                if (isSamePerceivedBrightness
                    || ColorUtilities.areColorsSimilar(elementLuma[0], elementLuma[1], elementLuma[2], targetLuma[0], targetLuma[1], targetLuma[2], colorSimilarityPercentage)) {
                    element.classList.add(getCamouflagedClass(elementLuma[0], elementLuma[1], elementLuma[2]));
                }
                else {
                    element.classList.remove(camouflagedLightClass);
                    element.classList.remove(camouflagedDarkClass);
                }
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($)
{
  'use strict';
  window.Rock = window.Rock || {};
  Rock.controls = Rock.controls || {};

  Rock.controls.datePartsPicker = (function ()
  {
    var exports = {
      initialize: function (options)
      {
        if (!options.id) {
          throw 'id is required';
        }

      },
      clientValidate: function (validator, args)
      {
        var $datePartsPicker = $(validator).closest('.js-datepartspicker');
        var monthNumber = Number($datePartsPicker.find('.js-month').val());
        var dayNumber = Number($datePartsPicker.find('.js-day').val());
        var yearNumber = Number($datePartsPicker.find('.js-year').val());
        var required = $datePartsPicker.attr('data-required') == 'true';
        var requireYear = $datePartsPicker.attr('data-requireyear') == 'true';
        var allowFuture = $datePartsPicker.attr('data-allowFuture') == 'true';
        var itemLabelText = $datePartsPicker.attr('data-itemlabel');

        var isValid = true;

        if (!allowFuture) {
          if (monthNumber && dayNumber && yearNumber) {
            // NOTE: Javascript Date Contructor's month parameter is zero based!
            // see http://stackoverflow.com/questions/2552483/why-does-the-month-argument-range-from-0-to-11-in-javascripts-date-constructor
            var bDate = new Date(yearNumber, monthNumber-1, dayNumber);
            var now = new Date();
            if (bDate > now) {
              isValid = false;
              validator.errormessage = itemLabelText + ' cannot be a future date.'
            }
          }
        }

        if (monthNumber && dayNumber && (yearNumber || !requireYear)) {
          // month, day and (conditionally) year are all set, it's OK
        }
        else if (monthNumber || dayNumber || yearNumber) {
          // at least one of them is set, but some are not, so it is invalid
          isValid = false;
          validator.errormessage = itemLabelText + ' must be a valid value.';
        }
        else if (required) {
          // nothing is set but it is a required field.
          isValid = false;
          validator.errormessage = itemLabelText + ' is required.';
        }

        var control = $datePartsPicker
        if (isValid) {
          control.removeClass('has-error');
        } else {
          control.addClass('has-error');
        }

        args.IsValid = isValid;
      }
    };

    return exports;
  }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.datePicker = (function () {
        var exports = {
            initialize: function (options) {
                if (!options.id) {
                    throw 'id is required';
                }
                var dateFormat = 'mm/dd/yyyy';
                if (options.format) {
                    dateFormat = options.format;
                }

                var $textBox = $('#' + options.id);

                var $datePickerContainer = $textBox.closest('.js-date-picker-container');
                var $datePickerInputGroup = $textBox.closest('.input-group.js-date-picker');

                // uses https://github.com/uxsolutions/bootstrap-datepicker
                var datePicker = $datePickerInputGroup.datepicker({
                    format: dateFormat,
                    assumeNearbyYear: 10,
                    autoclose: true,
                    container: options.container || document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || "body",
                    orientation: options.orientation || "auto",
                    todayBtn: "linked",
                    forceParse: options.forceParse,
                    startDate: options.startDate,
                    endDate: options.endDate || new Date(8640000000000000),
                    startView: options.startView,
                    showOnFocus: options.showOnFocus,
                    todayHighlight: options.todayHighlight,
                    zIndexOffset: 1050
                });

                // Listen to actions from external code.
                if (options.actions) {
                    // Update the bootstrap datepicker when external code sets the date.
                    options.actions.onSetLocalDate = (localDate) => {
                        $datePickerInputGroup.data("datepicker")
                            .setDate(localDate) // Set the date.
                            .fill();            // Update the UI.
                    };
                }

                // note: using 'change' instead of datePicker's 'changeDate' so that both manual entry and picking from calender works
                datePicker.on('change', function (e) {
                    if (options.postbackScript) {
                        window.location = "javascript:" + options.postbackScript;
                    }

                    if (options.onChangeScript) {
                        options.onChangeScript();
                    }
                });

                // if the guest clicks the addon select all the text in the input
                $datePickerInputGroup.find('.input-group-addon').on('click', function () {
                    $(this).siblings('.form-control').select();
                });

                $datePickerContainer.find('.js-current-date-checkbox').on('click', function (a, b, c) {
                    var $dateOffsetBox = $datePickerContainer.find('.js-current-date-offset');
                    var $dateOffsetlabel = $("label[for='" + $dateOffsetBox.attr('id') + "']")
                    if ($(this).is(':checked')) {
                        $dateOffsetlabel.show();
                        $dateOffsetBox.show();

                        // set textbox val to something instead of empty string so that validation doesn't complain
                        $textBox.data("last-value", $textBox.val()).val('Current').prop('disabled', true).addClass('aspNetDisabled');

                    } else {
                        $dateOffsetlabel.hide();
                        $dateOffsetBox.hide();

                        // set textbox val to last value so that validation will work again (if it is enabled)
                        $textBox.val($textBox.data('last-value')).prop('disabled', false).removeClass('aspNetDisabled');
                    }
                });
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($)
{
  'use strict';
  window.Rock = window.Rock || {};
  Rock.controls = Rock.controls || {};

  Rock.controls.dateRangePicker = (function ()
  {
    var exports = {
      initialize: function (options)
      {
        if (!options.id) {
          throw 'id is required';
        }
      },
      clientValidate: function (validator, args)
      {
        var $dateRangePicker = $(validator).closest('.js-daterangepicker');
        var lowerValue = $dateRangePicker.find('.js-lower input').val();
        var upperValue = $dateRangePicker.find('.js-upper input').val();
        var required = $dateRangePicker.attr('data-required') == 'true';
        var itemLabelText = $dateRangePicker.attr('data-itemlabel');

        var isValid = true;

        if (required) {
            // if required, then make sure that the date range has a start and/or end date (can't both be blank)
            if (lowerValue.length == 0 && upperValue.length == 0) {
                isValid = false;
                validator.errormessage = itemLabelText + " is required";
            }
        }

        var control = $dateRangePicker;
        if (isValid)
        {
          control.removeClass('has-error');
        }
        else
        {
          control.addClass('has-error');
        }

        args.IsValid = isValid;
      }
    };

    return exports;
  }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.dateTimePicker = (function () {
        var exports = {
            initialize: function (options) {
                if (!options.id) {
                    throw 'id is required';
                }
                var dateFormat = 'mm/dd/yyyy';
                if (options.format) {
                    dateFormat = options.format;
                }

                var $dp = $('#' + options.id + " .js-datetime-date");

                var $dateTimePickerContainer = $dp.closest('.js-datetime-picker-container');
                var $dateTimePickerInputGroup = $dp.closest('.input-group.date');

                // uses https://github.com/uxsolutions/bootstrap-datepicker
                $dateTimePickerInputGroup.datepicker({
                    format: dateFormat,
                    assumeNearbyYear: 10,
                    autoclose: true,
                    todayBtn: "linked",
                    startView: options.startView || 'month',
                    todayHighlight: options.todayHighlight || true,
                    zIndexOffset: 1050
                });

                // if the guest clicks the addon select all the text in the input
                $dateTimePickerInputGroup.find('.input-group-addon').on('click', function ()
                {
                  $(this).siblings('.form-control').select();
                });

                var $tp = $('#' + options.id + " .js-datetime-time");
                if ($tp) {
                    var $tpid = $tp.attr('id');
                    if ($tpid) {
                        Rock.controls.timePicker.initialize({
                            id: $tpid
                        });
                    }
                }

                $dateTimePickerContainer.find('.js-current-datetime-checkbox').on('click', function (a, b, c) {
                    var $dateTimeOffsetBox = $dateTimePickerContainer.find('.js-current-datetime-offset');
                    var $dateOffsetlabel = $("label[for='" + $dateTimeOffsetBox.attr('id') + "']")
                    if ($(this).is(':checked')) {
                        $dateOffsetlabel.removeClass('aspNetDisabled').show();
                        $dateTimeOffsetBox.show();
                        $dateTimeOffsetBox.prop('disabled', false).removeClass('aspNetDisabled').val( $dateTimeOffsetBox.data('last-value') );
                        $dp.data( "last-value", $dp.val()).val('').prop('disabled', true).addClass('aspNetDisabled').prop('placeholder', 'Current');
                        $tp.data( "last-value", $tp.val()).val('').prop('disabled', true).addClass('aspNetDisabled');
                    } else {
                        $dateOffsetlabel.addClass('aspNetDisabled').hide();
                        $dateTimeOffsetBox.data( "last-value", $dateTimeOffsetBox.val()).hide();
                        $dateTimeOffsetBox.val('').prop('disabled', true).addClass('aspNetDisabled');
                        $dp.prop('disabled', false).removeClass('aspNetDisabled').prop('placeholder', '').val($dp.data('last-value'));
                        $tp.prop('disabled', false).removeClass('aspNetDisabled').val($tp.data('last-value'));
                    }
                });
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.electronicSignatureControl = (function () {
        var exports = {
            /**
             * 
             * @param {any} options
             */
            initialize: function (options) {
                if (!options.controlId) {
                    throw 'id is required';
                }

                var self = this;

                self.ImageMimeType = options.imageMimeType || "image/png";

                var $control = $('#' + options.controlId);

                if ($control.length == 0) {
                    return;
                }

                self.$signatureControl = $control;
                self.$signatureTyped = $('.js-signature-typed', $control);

                self.$signatureEntryDrawn = $('.js-signature-entry-drawn', $control);
                self.$signaturePadCanvas = $('.js-signature-pad-canvas', $control);
                self.$signatureImageDataURL = $('.js-signature-data', $control);
                
                self.$clearSignature = $('.js-clear-signature', $control);
                self.$saveSignature = $('.js-save-signature', $control);

                self.initializeSignaturePad();

                self.$saveSignature.click(function () {
                    if (!self.signatureIsValid(self.$signatureControl)) {
                        return false;
                    }

                    if (self.$signaturePadCanvas.length) {
                        var signaturePad = self.$signaturePadCanvas.data('signatureComponent');

                        if (signaturePad) {
                            var signatureImageDataUrl = signaturePad.toDataURL(self.ImageMimeType);
                            self.$signatureImageDataURL.val(signatureImageDataUrl);
                        }
                    }
                });
            },

            /** */
            initializeSignaturePad: function () {
                var self = this;
                if (!self.$signaturePadCanvas.length) {
                    return
                }

                var signaturePadOptions = {};
                if (self.ImageMimeType == "image/jpeg") {
                    // NOTE That if we use jpeg, we'll have to deal with this https://github.com/szimek/signature_pad/issues/584
                    signaturePadOptions = {
                        penColor: 'black',
                        backgroundColor: 'white'
                    }
                };

                var signaturePad = new SignaturePad(self.$signaturePadCanvas[ 0 ], signaturePadOptions);

                self.$signaturePadCanvas.data('signatureComponent', signaturePad);

                self.$clearSignature.click(function () {
                    signaturePad.clear();
                })

                window.addEventListener("resize", function () {
                    self.resizeSignatureCanvas(self)
                });

                self.resizeSignatureCanvas(self);
            },

            /** */
            resizeSignatureCanvas: function (signatureControl) {
                if (!signatureControl.$signaturePadCanvas.length) {
                    return;
                }

                // If the window is resized, that'll affect the drawing canvas
                // also, if there is an existing signature, it'll get messed up, so clear it and
                // make them sign it again. See additional details why 
                // https://github.com/szimek/signature_pad
                var signaturePadCanvas = signatureControl.$signaturePadCanvas[ 0 ];
                var containerWidth = signatureControl.$signatureEntryDrawn.width();
                if (!containerWidth || containerWidth == 0) {
                    containerWidth = 400;
                }

                // Note the suggestion  https://github.com/szimek/signature_pad#handling-high-dpi-screens
                // to re-calculate the ratio based on window.devicePixelRatio isn't needed. 
                // We can just use the width() of the container and use fixed height of 100.
                const ratio = 1;
                signaturePadCanvas.width = containerWidth * ratio;
                signaturePadCanvas.height = 100 * ratio;
                signaturePadCanvas.getContext("2d").scale(ratio, ratio);

                var signaturePad = signatureControl.$signaturePadCanvas.data('signatureComponent');
                signaturePad.clear();
            },

            signatureIsValid: function ($signatureControl) {
                var $signatureCanvas = $signatureControl.find('.js-signature-pad-canvas')
                var $signatureTyped = $signatureControl.find('.js-signature-typed');

                if ($signatureCanvas.length == 0 && $signatureTyped.length == 0) {
                    return;
                }

                var isValid = true;

                if ($signatureCanvas.length) {
                    var signatureCanvas = $signatureCanvas.data('signatureComponent');

                    if (signatureCanvas.isEmpty()) {
                        isValid = false;
                    }
                }
                else {
                    if ($signatureTyped.val().trim() == '') {
                        isValid = false;
                    }
                }

                return isValid;
            },

            /** */
            clientValidate: function (validator, args) {
                var $signatureControl = $(validator).closest('.js-electronic-signature-control');

                var isValid = this.signatureIsValid($signatureControl);
                
                if (!isValid) {
                    validator.errormessage = "Signature is required.";
                }

                args.IsValid = isValid;
            }
        };

        return exports;
    }());
}(jQuery));
;
(function (Sys) {
    'use strict';
    Sys.Application.add_load(function () {

        var preventDoublePostback = function () {
            var inputOnChange = $(this).attr('onchange');
            var inputOnClick = $(this).attr('onclick');
            if (inputOnChange && inputOnChange.indexOf('__doPostBack') > 0 || inputOnClick && inputOnClick.indexOf('__doPostBack') > 0) {
                var $blockButtons = $(this).closest('.js-block-instance').find('a');
                $blockButtons.attr('disabled', 'disabled')
                    .prop('disabled', true)
                    .attr('disabled', 'disabled')
                    .addClass('disabled');
            }
        };

        // since some of the edit controls do a postback to show/hide other controls, we want to disable buttons to prevent a double postback
        $('select.js-prevent-double-postback,input.js-prevent-double-postback').on('change', preventDoublePostback);
        $('.js-prevent-double-postback input[type="radio"]').on('click', preventDoublePostback);
    });
}(Sys));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.fileUploader = (function () {
        var _configure = function (options) {
            options.isBinaryFile = options.isBinaryFile || 'T';
            options.uploadUrl = options.uploadUrl || 'FileUploader.ashx';

            var wsUrl = Rock.settings.get('baseUrl')
                        + options.uploadUrl + '?'
                        + 'isBinaryFile=' + options.isBinaryFile;

            if (options.isBinaryFile === 'T') {
                wsUrl += '&fileId=' + options.fileId
                    + '&fileTypeGuid=' + options.fileTypeGuid;
            }
            else {
                // note rootFolder is encrypted to prevent direct access to filesystem via the URL
                wsUrl += '&rootFolder=' + (encodeURIComponent(options.rootFolder) || '');
            }

            if (options.isTemporary === 'F') {
              wsUrl += '&IsTemporary=False';
            }

            if (options.parentEntityTypeId !== 'null') {
                wsUrl += '&ParentEntityTypeId=' + options.parentEntityTypeId;
            }

            if (options.parentEntityId !== 'null') {
                wsUrl += '&ParentEntityId=' + options.parentEntityId;
            }

            // uses https://github.com/blueimp/jQuery-File-Upload
            $('#' + options.controlId).fileupload({
                url: wsUrl,
                dataType: 'json',
                dropZone: $('#' + options.controlId).closest('.fileupload-dropzone'),
                autoUpload: true,
                submit: options.submitFunction,
                sequentialUploads: true,
                start: function (e, data) {
                    var $el = $('#' + options.controlId).closest('.fileupload-group');
                    $el.find('.js-upload-progress').rockFadeIn();
                },
                progressall: function (e, data) {
                    try {
                        if (data.total > 0) {
                            var $el = $('#' + options.controlId).closest('.fileupload-group');
                            var $progressPercent = $el.find('.js-upload-progress-percent');
                            if (!$progressPercent.length) {
                                return;
                            }

                            var percent = (data.loaded * 100 / data.total).toFixed(0);
                            if (percent > 1 && percent < 99) {
                                $progressPercent.text(percent + "%"); 
                            }
                            else {
                                $progressPercent.text("");
                            }
                        }
                    }
                    catch (ex) {
                        // ignore if any exception occurs
                    }
                },
                stop: function (e) {
                    var $el = $('#' + options.controlId).closest('.fileupload-group');
                    $el.find('.js-upload-progress').hide();
                    $el.find('.fileupload-dropzone').show();
                },
                done: function (e, data) {
                    var $el = $('#' + options.aFileName);

                    if ((options.isBinaryFile || 'T') === 'F') {
                        $('#' + options.hfFileId).val(data.response().result.FileName);
                    }
                    else {
                        $('#' + options.hfFileId).val(data.response().result.Id);
                    }

                    var getFileUrl = Rock.settings.get('baseUrl')
                        + 'GetFile.ashx?'
                        + 'isBinaryFile=' + (options.isBinaryFile || 'T')
                        // note rootFolder is encrypted to prevent direct access to filesystem via the URL
                        + '&rootFolder=' + (encodeURIComponent(options.rootFolder) || '');

                    if (options.disablePredictableIds) {
                        getFileUrl += '&guid=' + data.response().result.Guid;
                    }
                    else {
                        getFileUrl += '&id=' + data.response().result.Id
                    }

                    getFileUrl += '&fileName=' + data.response().result.FileName;

                    $el.text(data.response().result.FileName).attr('href', getFileUrl);
                    $('#' + options.aRemove).show();
                    if (options.postbackScript) {
                        window.location = "javascript:" +  options.postbackScript;
                    }

                    if (options.doneFunction) {
                        options.doneFunction(e, data);
                    }
                },
                fail: function (e, data) {
                    var $el = $('#' + options.controlId).closest('.fileupload-group');
                    $el.siblings('.js-rockupload-alert').remove();
                    var $warning = $('<div class="alert alert-warning alert-dismissable js-rockupload-alert"/>');

                    var msg = "unable to upload";
                    if (data.response().jqXHR && data.response().jqXHR.status == 406) {
                        msg = "file type not allowed";
                    } else if (data.response().jqXHR && data.response().jqXHR.responseText) {
                        msg = data.response().jqXHR.responseText;
                    }

                    if (options.maxUploadBytes && data.total) {
                        if (data.total >= options.maxUploadBytes) {
                            msg = "file size is limited to " + (options.maxUploadBytes / 1024 / 1024) + "MB";
                        }
                    }

                    $warning.append('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>')
                        .append('<strong><i class="fa fa-exclamation-triangle"></i> Warning </strong>')
                        .append(msg);
                    $warning.insertBefore($el);
                }
            });

            $('#' + options.aRemove).on('click', function () {
                $(this).hide();
                var $el = $('#' + options.aFileName);
                $el.attr('href', '#');
                $el.text('');
                $el.removeClass('file-exists');

                if (options.postbackRemovedScript) {
                    window.location = "javascript:" + options.postbackRemovedScript;
                } else {
                    $('#' + options.hfFileId).val('0');
                }

                return false;
            });
        },
        exports = {
            initialize: function (options) {
                if (!options.controlId) throw 'Control ID must be set.';
                _configure(options);
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.firstNameTextBox = (function () {
        const exports = {
            initialize: function (options) {
                if (!options.id) {
                    throw new Error('id is required');
                }
                this.options = options;
            },
            clientValidate: function (validator, args) {
                const firstNameTextBox = document.getElementById(this.options.id);
                const notAllowedStrings = this.options.notAllowedStrings;
                let firstName = undefined;
                if (firstNameTextBox) {
                    firstName = firstNameTextBox.value.toUpperCase();
                }

                let isValid = true;
                let invalidString;

                if (firstName) {
                    for (const value of notAllowedStrings) {
                        if (firstName.includes(value.toUpperCase())) {
                            isValid = false;
                            invalidString = value;
                            break;
                        }
                    }
                }
                if (!isValid) {
                    const labelText = firstNameTextBox.dataset.itemLabel;
                    validator.errormessage = `${labelText} cannot contain: ${invalidString}`;

                    if (this.options.displayInlineValidationError) {
                        validator.innerHTML = validator.errormessage;
                    }
                }

                args.IsValid = isValid;
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.followingsToggler = (function () {

        var exports = {
            // initialize a script that will set the following status via REST
            // entityTypeId is the EntityType, and entityId is the .Id for the associated entity
            // personId and personAliasId are the person that is doing the following/un-following
            // callback is optional and called on follow or unfollow events with a param indicating
            // if the person is now following
            initialize: function ($followingDiv, entityTypeId, entityId, purposeKey, personId, personAliasId, callback) {
                var hasCallback = typeof callback === 'function';

                $followingDiv.on('click', function () {
                    if ($followingDiv.hasClass('following')) {

                        $.ajax({
                            type: 'DELETE',
                            url: Rock.settings.get('baseUrl') + 'api/followings/' + entityTypeId + '/' + entityId + '/' + personId + '?purposeKey=' + encodeURIComponent(purposeKey),
                            success: function (data, status, xhr) {
                                $followingDiv.removeClass('following');

                                // update the tooltip (if one was configured)
                                if ($followingDiv.attr('data-original-title')) {
                                    $followingDiv.attr('data-original-title', 'Click to follow');
                                }

                                if (hasCallback) {
                                    callback(false, $followingDiv, entityTypeId, entityId, personId, personAliasId);
                                }
                            },
                        });

                    } else {
                        var following = {
                            EntityTypeId: entityTypeId,
                            EntityId: entityId,
                            PersonAliasId: personAliasId,
                            PurposeKey: purposeKey
                        };

                        $.ajax({
                            type: 'POST',
                            contentType: 'application/json',
                            data: JSON.stringify(following),
                            url: Rock.settings.get('baseUrl') + 'api/followings',
                            statusCode: {
                                201: function () {
                                    $followingDiv.addClass('following');

                                    // update the tooltip (if one was configured)
                                    if ($followingDiv.attr('data-original-title')) {
                                        $followingDiv.attr('data-original-title', 'Currently following');
                                    }

                                    if (hasCallback) {
                                        callback(true, $followingDiv, entityTypeId, entityId, personId, personAliasId);
                                    }
                                }
                            }
                        });
                    }
                })
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.fullScreen = (function () {
        var exports = {
            initialize: function (el) {
                $('.js-fullscreen-trigger').on('click', function (e) {
                    e.preventDefault();
                    var elem = $(this).closest('.block-instance')[0] || document.documentElement
                    if (el) {
                        // If Requesting Fullscreen page, verify layout is fullscreen capable.
                        if (el === 'body' && $(this).parents('.page-fullscreen-capable').length < 1) {
                            elem = $(this).closest('.block-instance')[0]
                        } else {
                            elem = $(el)[0];
                        }
                    }
                    Rock.controls.fullScreen.toggleFullscreen(elem);
                });
            },
            toggleFullscreen: function (elem) {
                elem = elem || document.documentElement;
                if (!document.fullscreenElement && !document.mozFullScreenElement &&
                    !document.webkitFullscreenElement && !document.msFullscreenElement) {
                    if (elem.requestFullscreen) {
                    elem.requestFullscreen();
                    document.addEventListener('fullscreenchange', this.exitHandler, false);
                    } else if (elem.msRequestFullscreen) {
                    elem.msRequestFullscreen();
                    document.addEventListener('MSFullscreenChange', this.exitHandler, false);
                    } else if (elem.mozRequestFullScreen) {
                    elem.mozRequestFullScreen();
                    document.addEventListener('mozfullscreenchange', this.exitHandler, false);
                    } else if (elem.webkitRequestFullscreen) {
                    elem.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
                    document.addEventListener('webkitfullscreenchange', this.exitHandler, false);
                    }
                    $(elem).addClass('is-fullscreen');
                } else {
                    if (document.exitFullscreen) {
                    document.exitFullscreen();
                    } else if (document.msExitFullscreen) {
                    document.msExitFullscreen();
                    } else if (document.mozCancelFullScreen) {
                    document.mozCancelFullScreen();
                    } else if (document.webkitExitFullscreen) {
                    document.webkitExitFullscreen();
                    }
                    $(elem).removeClass('is-fullscreen');
                }
            },
            exitHandler: function (elem) {
                if (!document.webkitIsFullScreen && !document.mozFullScreen && !document.msFullscreenElement) {
                    $(elem.target).removeClass('is-fullscreen');
                    document.dispatchEvent(new Event('RockExitFullscreen'));
                }
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.geoPicker = (function () {

        var GeoPicker = function (options) {
            var obj = this;
            obj.controlId = options.controlId;
            obj.restUrl = options.restUrl;
            obj.path = options.path;
            obj.centerAddress = options.centerAddress;                      // used when nothing is on map
            obj.centerLatitude = options.centerLatitude || "33.590795";     // used when nothing is on map
            obj.centerLongitude = options.centerLongitude || "-112.126459"; // used when nothing is on map
            obj.drawingMode = options.drawingMode || "Polygon" || "Point";  // the available modes
            obj.strokeColor = options.strokeColor || "#0088cc";
            obj.fillColor = options.fillColor || "#0088cc";

            // An array of styles that controls the look of the Google Map
            // http://gmaps-samples-v3.googlecode.com/svn/trunk/styledmaps/wizard/index.html
            obj.styles = options.mapStyle;

            // This is used to temporarily store in case of user cancel.
            obj.pathTemp = null;

            // the selected polygon or point
            obj.selectedShape = null;

            // these are used to set the map's viewport boundary
            obj.minLat = null;
            obj.maxLat = null;
            obj.minLng = null;
            obj.maxLng = null;

            // our google Map
            obj.map = null;

            // an instance of the Google Maps drawing manager
            obj.drawingManager = null;

            // An array of styles that controls the look of the Google Map
            // http://gmaps-samples-v3.googlecode.com/svn/trunk/styledmaps/wizard/index.html
            //obj.styles = [
            //  {
            //      "stylers": [
            //        { "visibility": "simplified" },
            //        { "saturation": -100 }
            //      ]
            //  }, {
            //      "featureType": "road",
            //      "elementType": "labels",
            //      "stylers": [
            //        { "visibility": "on" }
            //      ]
            //  }, {
            //      "featureType": "poi",
            //      "stylers": [
            //        { "visibility": "off" }
            //      ]
            //  }, {
            //      "featureType": "landscape",
            //      "stylers": [
            //        { "visibility": "off" }
            //      ]
            //  }, {
            //      "featureType": "administrative.province",
            //      "stylers": [
            //        { "visibility": "on" }
            //      ]
            //  }, {
            //      "featureType": "administrative.locality",
            //      "stylers": [
            //        { "visibility": "on" }
            //      ]
            //  }
            //];

            /**
            * Initializes the map viewport boundary coordinates.
            */
            this.initMinMaxLatLng = function () {
                obj.minLat = null;
                obj.maxLat = null;
                obj.minLng = null;
                obj.maxLng = null;
            }

            /**
            * Unselects the selected shape (if selected) and disables the delete button.
            */
            this.clearSelection = function () {
                if (obj.selectedShape) {
                    if (obj.drawingMode == "Polygon") {
                        obj.selectedShape.setEditable(false);
                    }
                    obj.selectedShape = null;
                }
                $('#gmnoprint-delete-button_' + obj.controlId).attr('disabled', '')
                    .find('.fa-times').css("color", "#aaa");
            }

            /**
            * Stores the given shape's path points into the path property as a
            * pipe (|) delimited list of lat,long coordinates.  This method also
            * re-enables the delete button so that the polygon can be deleted.
            */
            this.setSelection = function (shape, type) {
                obj.clearSelection();

                // enable delete button
                $('#gmnoprint-delete-button_' + obj.controlId).prop("disabled", false)
                    .find('.fa-times').css("color", "");

                obj.selectedShape = shape;

                if (type == "polygon") {
                    shape.setEditable(true);
                    var coordinates = new Array();
                    var vertices = shape.getPaths().getAt(0);
                    // Iterate over the vertices of the shape's path
                    for (var i = 0; i < vertices.length; i++) {
                        var xy = vertices.getAt(i);
                        coordinates[i] = xy.toUrlValue();
                    }

                    // if the last vertex is not already the first, then
                    // add the first vertex to the end of the path.
                    if (vertices.getAt(0).toUrlValue() != coordinates[coordinates.length - 1]) {
                        obj.path = coordinates.join('|') + '|' + vertices.getAt(0).toUrlValue();
                    }
                    else {
                        obj.path = coordinates.join('|');
                    }
                }
                else if (type == "marker") {
                    obj.path = shape.getPosition().toUrlValue();
                }
            }

            /**
            * Delete the selected shape and enable the drawing controls
            * if they were deleted.  Also removes the polygon from the hidden variable.
            */
            this.deleteSelectedShape = function () {
                if (obj.selectedShape && confirm("Delete selected shape?")) {
                    obj.selectedShape.setMap(null);
                    obj.clearSelection();

                    // delete the path
                    obj.path = null;

                    // enable the drawing controls again
                    obj.drawingManager.setOptions({
                        drawingControlOptions: {
                            drawingModes: obj.getDrawingModes()
                        }
                    });
                }
            }

            /**
            * Returns the appropriate mode array for use with the map's DrawingManager
            * drawing control options.
            */
            this.getDrawingModes = function getDrawingModes() {

                if (obj.drawingMode == "Polygon") {
                    return [google.maps.drawing.OverlayType.POLYGON];
                }
                else if (obj.drawingMode == "Point") {
                    return [google.maps.drawing.OverlayType.MARKER];
                }
            }

            /**
            * Returns a marker image styled according to the stroke color.
            */
            this.getMarkerImage = function getMarkerImage() {
                return {
                    path: 'M 0,0 C -2,-20 -10,-22 -10,-30 A 10,10 0 1,1 10,-30 C 10,-22 2,-20 0,0 z',
                    fillColor: '#FE7569',
                    fillOpacity: 1,
                    strokeColor: '#000',
                    strokeWeight: 1,
                    scale: 1,
                    labelOrigin: new google.maps.Point(0, -28)
                }
            }

            /**
            * Finds the point/polygon boundary and sets the map viewport to fit
            */
            this.fitBounds = function () {
                if (! obj.path) {
                    // if no path, then set the center using the options
                    var newLatLng = new google.maps.LatLng(
                        parseFloat(obj.centerLatitude),
                        parseFloat(obj.centerLongitude));
                    obj.map.setCenter(newLatLng);
                    return;
                }

                var coords = obj.path.split('|');
                var pathArray = new Array();
                // find the most southWest and northEast points of the path.
                for (var i = 0; i < coords.length ; i++) {
                    var latLng = coords[i].split(',');
                    var lat = parseFloat(latLng[0]);
                    var lng = parseFloat(latLng[1]);
                    // find the most southWest and northEast points of the path.
                    obj.findBounds(lat, lng);
                    pathArray.push(new google.maps.LatLng(lat, lng));
                }

                // Set the viewport to contain the given bounds.
                var southWest = new google.maps.LatLng(obj.minLat, obj.minLng);
                var northEast = new google.maps.LatLng(obj.maxLat, obj.maxLng);
                var bounds = new google.maps.LatLngBounds(southWest, northEast);
                obj.map.fitBounds(bounds);

                if (obj.drawingMode == "Point") {
                    obj.map.setZoom(16);
                }
            }

            /**
            * Utility method to determine the most SouthWestern
            * and NorthEastern lat and long.
            */
            this.findBounds = function (lat, lng) {
                if (!obj.minLat || lat < obj.minLat) {
                    obj.minLat = lat;
                }
                if (!obj.maxLat || lat > obj.maxLat) {
                    obj.maxLat = lat;
                }
                if (!obj.minLng || lng < obj.minLng) {
                    obj.minLng = lng;
                }
                if (!obj.maxLng || lng > obj.maxLng) {
                    obj.maxLng = lng;
                }
            }

            /**
            * Disables the drawing manager so they cannot add anything to the map.
            */
            this.disableDrawingManager = function () {
                // Switch back to non-drawing mode after drawing a shape.
                if (!obj.drawingManager) {
                    return;
                }

                obj.drawingManager.setDrawingMode(null);

                // disable the drawing controls so we only get one polygon
                // and we'll add it back on deleting the existing polygon.
                obj.drawingManager.setOptions({
                    drawingControlOptions: {
                        drawingModes: [
                        ]
                    }
                });
            }

            /**
            * Takes the path data stored in the path and plots it on the map.
            */
            this.plotPath = function (map) {
                obj.initMinMaxLatLng();
                // only try this if we have a path
                if (obj.path) {
                    var coords = obj.path.split('|');
                    var pathArray = new Array();
                    // put the polygon coordinates into a path array
                    for (var i = 0; i < coords.length ; i++) {
                        var latLng = coords[i].split(',');
                        var lat = parseFloat(latLng[0]);
                        var lng = parseFloat(latLng[1]);
                        pathArray.push(new google.maps.LatLng(lat, lng));
                    }

                    // reverse geocode the first point to an address and display.
                    var $label = $('#selectedGeographyLabel_' + this.controlId);
                    obj.toAddress(pathArray[0], $label);

                    if (coords.length > 0) {
                        var polygon;

                        if (obj.drawingMode == "Polygon") {

                            var polygon = new google.maps.Polygon({
                                path: pathArray,
                                clickable: true,
                                editable: true,
                                strokeColor: obj.strokeColor,
                                fillColor: obj.fillColor,
                                strokeWeight: 2
                            });
                            polygon.setMap(map);

                            // Select the polygon
                            obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON);

                            // Disable the drawing manager
                            obj.disableDrawingManager();

                            // add listener for moving polygon points.
                            google.maps.event.addListener(polygon.getPath(), 'set_at', function (e) {
                                obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON);
                            });

                            // add listener for adding new points.
                            google.maps.event.addListener(polygon.getPath(), 'insert_at', function (e) {
                                obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON);
                            });

                            // Add an event listener to implement right-click to delete node
                            google.maps.event.addListener(polygon, 'rightclick', function (ev) {
                                if (ev.vertex != null) {
                                    polygon.getPath().removeAt(ev.vertex);
                                }
                                obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON);
                            });

                            // add listener for "selecting" the polygon
                            // because that's where we stuff the coordinates into the hidden variable
                            google.maps.event.addListener(polygon, 'click', function () {
                                obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON);
                            });
                        }
                        else if (obj.drawingMode == "Point") {

                            var point = new google.maps.Marker({
                                position: pathArray[0],
                                map: map,
                                clickable: true,
                                icon: obj.getMarkerImage()
                            });

                            // Select the point
                            obj.setSelection(point, google.maps.drawing.OverlayType.MARKER);

                            // Disable the drawing manager
                            obj.disableDrawingManager();

                            // add listener for "selecting" the point
                            // because that's where we stuff the coordinates into the hidden variable
                            google.maps.event.addListener(point, 'click', function () {
                                obj.setSelection(point, google.maps.drawing.OverlayType.MARKER);
                            });
                        }
                    }
                }
            }

            /**
            * Take the given lat and long and try to reverse-geocode to an Address
            * then stuff that address as the value of the given element.
            */
            this.toAddress = function (latlng, $labelElement) {
                // only try if we have a valid latlng
                if (!latlng || isNaN(latlng.lat()) || isNaN(latlng.lng())) {
                    $labelElement.text('');
                    return;
                }
                var geocoder = new google.maps.Geocoder();
                geocoder = geocoder.geocode({ "latLng": latlng }, function (results, status) {
                    if (status == google.maps.GeocoderStatus.OK) {
                        if (results[0]) {
                            $labelElement.text('near ' + results[0].formatted_address);
                        }
                    }
                    else
                    {
                        $labelElement.html('<i>selected</i>');
                        console.log('Geocoder failed due to: ' + status);
                    }
                });
            }

            /**
            * Geocode an address to a latLng and center the map on that point.
            */
            this.centerMapOnAddress = function () {
                var self = this;
                // only try if a centerAddress is set
                if (!obj.centerAddress) {
                    return;
                }

                var geocoder = new google.maps.Geocoder();

                geocoder.geocode({ 'address': obj.centerAddress }, function (results, status) {
                    if (status == google.maps.GeocoderStatus.OK) {
                        self.map.setCenter(results[0].geometry.location);
                    } else {
                        console.log('Geocode was not successful for the following reason: ' + status);
                    }
                });
            }

            /**
            * Return a latLng for the "first point" of the selected map.
            */
            this.firstPoint = function () {
                if (obj.path) {
                    var coords = obj.path.split('|');
                    if (coords) {
                        var latLng = coords[0].split(',');
                        var lat = parseFloat(latLng[0]);
                        var lng = parseFloat(latLng[1]);
                        return new google.maps.LatLng(lat, lng);
                    }
                }
            }

        }; // sorta end class

        /**
        * initialize our event handlers for the button clicks we're going to be handling.
        */
        GeoPicker.prototype.initializeEventHandlers = function () {
            var controlId = this.controlId,
                $control = $('#' + this.controlId),
                $hiddenField = $('#' + this.controlId + '_hfGeoPath'),
                self = this;

            /**
            * Toggle the picker on and off when the control's link is clicked.
            */
            $('#' + controlId + ' .picker-label').on('click', function (e) {
                e.preventDefault();
                var $control = $('#' + controlId);
                $(this).toggleClass("active");
                $control.find('.picker-menu').first().toggle(0, function () {
                    Rock.dialogs.updateModalScrollBar(controlId);
                });

                if ( $control.find('.picker-menu').first().is(":visible") ) {
                    google.maps.event.trigger(self.map, "resize");
                    // now we can safely fit the map to any polygon boundary
                    self.fitBounds();

                    // Scroll down so you can see the done/cancel buttons
                    $("html,body").animate({
                        scrollTop: $control.offset().top
                    }, 1000);
                }
            });

            /**
            * Hide the expand button if we are in a modal
            */
            if ($control.closest('.modal').length) {
                $('#btnExpandToggle_' + controlId).hide();
            }

            /**
            * Handle the toggle expand fullscreen button click.
            */
            $('#btnExpandToggle_' + controlId).on('click', function () {

                var $myElement = $('#geoPicker_' + self.controlId);

                var isExpaned = $myElement.data("fullscreen");

                $(this).children('i').toggleClass("fa-expand", isExpaned);
                $(this).children('i').toggleClass("fa-compress", ! isExpaned);

                // Shrink to regular size
                if ( isExpaned ) {
                    $myElement.data("fullscreen", false);

                    $(this).closest('.picker-menu').css({
                        position: 'absolute',
                        top: 0,
                        left: 0,
                        height: '',
                        width: 520
                    });
                    // resize the map
                    $myElement.css({
                        height: 300,
                        width: 500
                    });

                    // move the delete button
                    $('#gmnoprint-delete-button_' + self.controlId).css({
                        left: '200px',
                    });

                }
                else {

                    // Expand to fullscreen

                    $myElement.data("fullscreen", true);
                    // resize the container
                    $(this).closest('.picker-menu').css({
                        position: 'fixed',
                        top: 0,
                        left: 0,
                        height: '100%',
                        width: '100%'
                    });

                    // resize the map
                    $myElement.css({
                        height: '85%',
                        width: '100%'
                    });

                    // move the delete button
                    $('#gmnoprint-delete-button_' + self.controlId).css({
                        left: '200px',
                    });
                }

                // tell the map to resize/redraw
                google.maps.event.trigger(self.map, 'resize');
                self.fitBounds();

                Rock.dialogs.updateModalScrollBar(self.controlId);

            });

            /**
            * Handle the Cancel button click by hiding the overlay.
            */
            $('#btnCancel_' + controlId).on('click', function () {
                $(this).closest('.picker-menu').slideUp(function () {
                    Rock.dialogs.updateModalScrollBar(controlId);
                });

                self.path = self.pathTemp;

                if ( self.selectedShape ) {
                    self.selectedShape.setMap(null);
                    self.clearSelection();

                    // enable the drawing controls again
                    self.drawingManager.setOptions({
                        drawingControlOptions: {
                            drawingModes: self.getDrawingModes()
                        }
                    });
                }
                self.plotPath(self.map);
            });

            // have the X appear on hover if something is selected
            if ($hiddenField.val() && $hiddenField.val() !== '0') {
                $control.find('.picker-select-none').addClass('rollover-item').show();
            }

            /**
            * Handle the Select button click by stuffing the RockGoogleGeoPicker's path value into the hidden field.
            */
            $control.find('.js-geopicker-select').on('click', function () {
                var geoInput = $('#' + controlId).find('input:checked'),
                    selectedValue = self.path,
                    selectedGeographyLabel = $('#selectedGeographyLabel_' + controlId);

                $hiddenField.val(self.path);

                // have the X appear on hover. something is selected
                $control.find('.picker-select-none').addClass('rollover-item');
                $control.find('.picker-select-none').show();

                selectedGeographyLabel.val(selectedValue);
                self.toAddress( self.firstPoint(), selectedGeographyLabel);

                //clear out any old map positioning
                self.initMinMaxLatLng();

                $(this).closest('.picker-menu').slideUp(function () {
                    Rock.dialogs.updateModalScrollBar(controlId);
                });

                $(this).closest('form').submit();
            });


            /**
            * Clear the selection when X is clicked
            */
            $control.find('.picker-select-none').on('click', function (e) {
                e.stopImmediatePropagation();
                var selectedGeographyLabel = $('#selectedGeographyLabel_' + controlId);
                $hiddenField.val("");

                // don't have the X appear on hover. nothing is selected
                $control.find('.picker-select-none').removeClass('rollover-item');
                $control.find('.picker-select-none').hide();

                selectedGeographyLabel.val("");
                self.toAddress(null, selectedGeographyLabel);

                //clear out any old map positioning
                self.initMinMaxLatLng();

                return false;
            });
        };

        /**
        * Initialize the GeoPicker.
        */
        GeoPicker.prototype.initialize = function () {
            var self = this;
            var $hiddenField = $('#' + self.controlId + '_hfGeoPath');
            var deleteButtonId = 'gmnoprint-delete-button_' + self.controlId;

            // Pull anything in the hidden field onto this object's path
            self.path = $hiddenField.val();

            // store path into pathTemp in case of user cancel.
            self.pathTemp = self.path;

            // Create a new StyledMapType object, passing it the array of styles,
            // as well as the name to be displayed on the map type control.
            var styledMap = new google.maps.StyledMapType(self.styles, { name: "Styled Map" });

            // WARNING: I though about removing the "center:" from the options here but then the
            // map's controls were different and then our delete button was out of alignment.
            var mapOptions = {
                center: new google.maps.LatLng(
                        parseFloat(self.centerLatitude),
                        parseFloat(self.centerLongitude)),
                zoom: 13,
                streetViewControl: false,
                mapTypeControlOptions: {
                    mapTypeIds: [google.maps.MapTypeId.ROADMAP, 'map_style']
                }
            };
            // center the map on the configured address
            self.centerMapOnAddress();

            self.map = new google.maps.Map(document.getElementById('geoPicker_' + self.controlId), mapOptions);

            //Associate the styled map with the MapTypeId and set it to display.
            self.map.mapTypes.set('map_style', styledMap);
            self.map.setMapTypeId('map_style');

            // If we have coordinates we should plot them here...
            self.plotPath(self.map);

            // Set up the Drawing Manager for creating polygons, circles, etc.
            self.drawingManager = new google.maps.drawing.DrawingManager({
                drawingControl: true,
                drawingControlOptions: {
                    drawingModes: self.getDrawingModes()
                },
                polygonOptions: {
                    editable: true,
                    strokeColor: self.strokeColor,
                    fillColor: self.fillColor,
                    strokeWeight: 2
                },
                markerOptions: {
                    icon: self.getMarkerImage()
                }
            });

            self.drawingManager.setMap(self.map);

            // but disable the drawing manager if we already have a point/polygon selected:
            if (self.path) {
                self.disableDrawingManager();
            }

            // Handle when the polygon shape drawing is "complete"
            google.maps.event.addListener(self.drawingManager, 'overlaycomplete', function (e) {
                if (e.type == google.maps.drawing.OverlayType.POLYGON || e.type == google.maps.drawing.OverlayType.MARKER ) {
                    // Disable the drawing manager once they've drawn an overlay.
                    self.disableDrawingManager();

                    // Add an event listener that selects the newly-drawn shape when the user mouses down on it.
                    var newShape = e.overlay;
                    newShape.type = e.type;
                    google.maps.event.addListener(newShape, 'click', function () {
                        self.setSelection(newShape, e.type);
                    });
                    self.setSelection(newShape, e.type);

                    if (e.type == google.maps.drawing.OverlayType.POLYGON) {
                        // add listener for moving polygon points.
                        google.maps.event.addListener(newShape.getPath(), 'set_at', function (e) {
                            self.setSelection(newShape, google.maps.drawing.OverlayType.POLYGON);
                        });

                        // add listener for adding new points.
                        google.maps.event.addListener(newShape.getPath(), 'insert_at', function (e) {
                            self.setSelection(newShape, google.maps.drawing.OverlayType.POLYGON);
                        });

                        // Add an event listener to implement right-click to delete node
                        google.maps.event.addListener(newShape, 'rightclick', function (ev) {
                            if (ev.vertex != null) {
                                newShape.getPath().removeAt(ev.vertex);
                            }
                            self.setSelection(newShape, google.maps.drawing.OverlayType.POLYGON);
                        });
                    }
                }
            });

            // Clear the current selection when the drawing mode is changed, or when the map is clicked.
            google.maps.event.addListener(self.drawingManager, 'drawingmode_changed', self.clearSelection);
            google.maps.event.addListener(self.map, 'click', self.clearSelection);

            // Move our custom delete button into place once the map is idle.
            // as per http://stackoverflow.com/questions/832692/how-to-check-if-google-maps-is-fully-loaded
            google.maps.event.addListenerOnce(self.map, 'idle', function () {
                $('#' + deleteButtonId).fadeIn();

                // wire up an event handler to the delete button
                document.getElementById(deleteButtonId).addEventListener('click', self.deleteSelectedShape);
            });

            self.initializeEventHandlers();

            Rock.dialogs.updateModalScrollBar(self.controlId);
        };

        var exports = {
            googleMapsLoadCallback: function () {
                // callback for when google maps api is done loading (if it wasn't loaded already)
                $.each(Rock.controls.geoPicker.geoPickerOptions, function (a, options) {
                    var geoPicker = Rock.controls.geoPicker.geoPickers[options.controlId];
                    if (!geoPicker) {

                        geoPicker = new GeoPicker(options);
                        Rock.controls.geoPicker.geoPickers[options.controlId] = geoPicker;
                        geoPicker.initialize();
                    }
                });
            },
            geoPickers: {},
            geoPickerOptions: {},
            findControl: function (controlId) {
                return exports.geoPickers[controlId];
            },
            initialize: function (options) {
                if (!options.controlId) throw '`controlId` is required.';
                exports.geoPickerOptions[options.controlId] = options;

                $(window).on('googleMapsIsLoaded', this.googleMapsLoadCallback);

                // if the google maps api isn't loaded uet, googleMapsLoadCallback will take care of it
                if (typeof (google) != "undefined") {
                    // null it out just in case, to force it to get recreated
                    exports.geoPickers[options.controlId] = null;
                    $(window).trigger('googleMapsIsLoaded');
                }
            }
        };

        return exports;
    }());
}(jQuery));
;
(function (Sys) {
    'use strict';
    Sys.Application.add_load(function () {
        $('a.help').on('click', function (e) {
            e.preventDefault();
            $(this).siblings('div.alert-info').slideToggle(function () {
                Rock.controls.modal.updateSize(this);
            });
            $(this).siblings('a.warning').insertAfter($(this));
            $(this).siblings('div.alert-warning').slideUp();
        });
    });
}(Sys));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};

    Rock.htmlEditor = (function () {
        var exports = {
            // full list of available items is at http://summernote.org/deep-dive/#custom-toolbar-popover

            // the toolbar items to include in the HtmlEditor when Toolbar is set to Light 
            toolbar_RockCustomConfigLight:
                [
                    // [groupName, [list of button]]
                    ['source_group', ['rockcodeeditor']],
                    ['style_group1', ['bold', 'italic', 'strikethrough', 'link', 'color', 'style', 'ol', 'ul']],
                    ['style_group3', ['clear']],
                    ['para', ['paragraph']],
                    ['plugins1', ['rockmergefield']],
                    ['plugins2', ['rockimagebrowser', 'rockfilebrowser', 'rockassetmanager']],
                    ['plugins3', ['rockpastetext', 'rockpastefromword']],
                    ['style_group2', ['undo', 'redo']]
                ],

            // the toolbar items to include in the HtmlEditor when Toolbar is set to Full 
            toolbar_RockCustomConfigFull:
                [
                    // [groupName, [list of button]]
                    ['source_group', ['rockcodeeditor']],
                    ['style_group1', ['bold', 'italic', 'underline', 'strikethrough', 'ol', 'ul', 'link']],
                    ['style_group2', ['undo', 'redo']],
                    ['style_group3', ['clear']],
                    ['style_group4', ['style', 'color']],
                    ['full_toolbar_only', ['fontname', 'fontsize', 'superscript', 'subscript', 'table', 'hr']],
                    ['para', ['paragraph']],
                    ['plugins1', ['rockmergefield']],
                    ['plugins2', ['rockimagebrowser', 'rockfilebrowser', 'rockassetmanager']],
                    ['plugins3', ['rockpastetext', 'rockpastefromword']],
                    ['help_group1', ['help']]
                ]
        };

        return exports;

    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.imageUploader = (function () {
        var _configure = function (options) {
            options.isBinaryFile = options.isBinaryFile || 'T';

            var $contentFileSource = $('#' + options.hfContentFileSource);
            var $binaryFileId = $('#' + options.hfFileId);

            // default setImageUrlOnUpload to true if not specified
            if (options.setImageUrlOnUpload == null) {
                options.setImageUrlOnUpload = true;
            }

            var wsUrl = Rock.settings.get('baseUrl')
                + 'ImageUploader.ashx?'
                + 'isBinaryFile=' + options.isBinaryFile;

            if (options.isBinaryFile == 'T') {
                wsUrl += '&fileId=' + options.fileId
                    + '&fileTypeGuid=' + options.fileTypeGuid;
            }
            else {
                // note rootFolder is encrypted to prevent direct access to filesystem via the URL
                wsUrl += '&rootFolder=' + (encodeURIComponent(options.rootFolder) || '');
            }

            if (options.isTemporary == 'F') {
                wsUrl += '&IsTemporary=False';
            }

            // uses https://github.com/blueimp/jQuery-File-Upload
            $('#' + options.controlId).fileupload({
                url: wsUrl,
                dataType: 'json',
                dropZone: $('#' + options.controlId).closest('.imageupload-dropzone'),
                autoUpload: true,
                submit: options.submitFunction,
                start: function (e, data) {
                    var $el = $('#' + options.controlId).closest('.imageupload-group');
                    $el.find('.js-upload-progress').rockFadeIn();
                },
                progressall: function (e, data) {
                    // var $el = $('#' + options.controlId).closest('.imageupload-group');
                    // implement this to show progress percentage
                },
                stop: function (e) {
                    var $el = $('#' + options.controlId).closest('.imageupload-group');
                    $el.find('.js-upload-progress').hide();
                    $el.find('.imageupload-dropzone').rockFadeIn();
                },
                done: function (e, data) {
                    var $el = $('#' + options.imgThumbnail);

                    if ((options.isBinaryFile || 'T') == 'F') {
                        $contentFileSource.val(data.response().result.FileName);
                    }
                    else {
                        $binaryFileId.val(data.response().result.Id);
                    }

                    var getImageUrl = Rock.settings.get('baseUrl')
                        + 'GetImage.ashx?'
                        + 'isBinaryFile=' + (options.isBinaryFile || 'T');

                    if (options.disablePredictableIds) {
                        getImageUrl += '&guid=' + data.response().result.Guid
                    }
                    else {
                        getImageUrl += '&id=' + data.response().result.Id;
                    }

                    getImageUrl += '&fileName=' + data.response().result.FileName
                        + '&width=500';

                    if (options.rootFolder) {
                        // note rootFolder is encrypted to prevent direct access to filesystem via the URL
                        getImageUrl += '&rootFolder=' + encodeURIComponent(options.rootFolder);
                    }

                    if (options.setImageUrlOnUpload) {
                        if ($el.is('img')) {
                            $el.attr('src', getImageUrl);
                        }
                        else {
                            $el.attr('style', 'background-image:url("' + getImageUrl + '");background-size:cover;background-position:50%');
                        }
                    }

                    $('#' + options.aRemove).show();
                    if (options.postbackScript) {
                        window.location = "javascript:" + options.postbackScript;
                    }

                    if (options.doneFunction) {
                        options.doneFunction(e, data);
                    }
                },
                fail: function (e, data) {
                    var $el = $('#' + options.controlId).closest('.imageupload-group');
                    $el.siblings('.js-rockupload-alert').remove();
                    var $warning = $('<div class="alert alert-warning alert-dismissable js-rockupload-alert"/>');

                    var msg = "unable to upload";
                    if (data.response().jqXHR && data.response().jqXHR.status == 406) {
                        msg = "file type not allowed";
                    } else if (data.response().jqXHR && data.response().jqXHR.responseText) {
                        msg = data.response().jqXHR.responseText;
                    }

                    if (options.maxUploadBytes && data.total) {
                        if (data.total >= options.maxUploadBytes) {
                            msg = "file size is limited to " + (options.maxUploadBytes / 1024 / 1024) + "MB";
                        }
                    }

                    $warning.append('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>')
                        .append('<strong><i class="fa fa-exclamation-triangle"></i> Warning </strong>')
                        .append(msg);
                    $warning.insertBefore($el);
                }
            });

            $('#' + options.aRemove).on('click', function () {
                $(this).hide();
                var $el = $('#' + options.imgThumbnail);
                var noPictureUrl = options.noPictureUrl || Rock.settings.get('baseUrl') + 'Assets/Images/no-picture.svg';
                if ($el.is('img')) {
                    $el.attr('src', noPictureUrl);
                }
                else {
                    $el.attr('style', 'background-image:url(' + noPictureUrl + ');background-size:cover;background-position:50%');
                }

                if (options.deleteFunction) {
                    options.deleteFunction();
                }

                if (options.postbackRemovedScript) {
                    window.location = "javascript:" + options.postbackRemovedScript;
                } else {
                    $binaryFileId.val('0');
                    $contentFileSource.val('');
                }
                return false;
            });
        },
        exports = {
            initialize: function (options) {
                if (!options.controlId) throw 'Control ID must be set.';
                _configure(options);
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.itemPicker = (function () {
        var ItemPicker = function (options) {
            this.options = options;

            // set a flag so that the picker only auto-scrolls to a selected item once. This prevents it from scrolling at unwanted times
            this.alreadyScrolledToSelected = false;
            this.iScroll = null;
        },
            exports;

        ItemPicker.prototype = {
            constructor: ItemPicker,
            initialize: function () {
                var $control = $('#' + this.options.controlId),
                    $tree = $control.find('.treeview'),
                    treeOptions = {
                        multiselect: this.options.allowMultiSelect,
                        categorySelection: this.options.allowCategorySelection,
                        categoryPrefix: this.options.categoryPrefix,
                        restUrl: this.options.restUrl,
                        restParams: this.options.restParams,
                        expandedIds: this.options.expandedIds,
                        expandedCategoryIds: this.options.expandedCategoryIds,
                        showSelectChildren: this.options.showSelectChildren,
                        id: this.options.startingId
                    },
                    $hfItemIds = $control.find('.js-item-id-value'),
                    $hfExpandedIds = $control.find('.js-initial-item-parent-ids-value'),
                    $hfExpandedCategoryIds = $control.find('.js-expanded-category-ids');

                if (typeof this.options.mapItems === 'function') {
                    treeOptions.mapping = {
                        mapData: this.options.mapItems
                    };
                }

                // clean up the tree (in case it was initialized already, but we are rebuilding it)
                var rockTree = $tree.data('rockTree');
                if (rockTree) {
                    rockTree.nodes = [];
                }
                $tree.empty();

                var $scrollContainer = $control.find('.scroll-container .viewport');
                var $scrollIndicator = $control.find('.track');
                this.iScroll = new IScroll($scrollContainer[0], {
                    mouseWheel: true,
                    indicators: {
                        el: $scrollIndicator[0],
                        interactive: true,
                        resize: false,
                        listenY: true,
                        listenX: false,
                    },
                    click: false,
                    preventDefaultException: { tagName: /.*/ }
                });

                // Since some handlers are "live" events, they need to be bound before tree is initialized
                this.initializeEventHandlers();

                if ($hfItemIds.val() && $hfItemIds !== '0') {
                    treeOptions.selectedIds = $hfItemIds.val().split(',');
                }

                if ($hfExpandedIds.val()) {
                    treeOptions.expandedIds = $hfExpandedIds.val().split(',');
                }

                if ($hfExpandedCategoryIds.val()) {
                    treeOptions.expandedCategoryIds = $hfExpandedCategoryIds.val().split(',');
                }

                if (this.options.universalItemPicker) {
                    function mapUniversalItems(data) {
                        return data.map(item => {
                            const treeItem = {
                                id: item.value,
                                name: item.text,
                                iconCssClass: item.iconCssClass,
                                hasChildren: item.hasChildren,
                                isCategory: false,
                                isSelectionDisabled: item.isSelectionDisabled,
                                childrenUrl: item.childrenUrl
                            };

                            if (Array.isArray(item.children)) {
                                treeItem.children = mapUniversalItems(item.children);
                            }

                            return treeItem;
                        });
                    }

                    treeOptions.universalItemPicker = true;
                    treeOptions.expandedIds = (treeOptions.expandedIds || []).filter(id => id !== 0);

                    treeOptions.getNodes = (parentId, parentNode, selectedIds, toExpandIds) => {
                        const req = {
                        };

                        if (!parentId) {
                            req.expandToValues = (selectedIds || []).filter(a => a !== "0");
                        }
                        else {
                            req.parentValue = parentId;
                        }

                        return $.ajax({
                            method: 'POST',
                            data: JSON.stringify(req),
                            url: treeOptions.restUrl,
                            dataType: 'json',
                            contentType: 'application/json'
                        })
                            .then(data => {
                                function checkItemsForExpansion(items, path) {
                                    for (let i = 0; i < items.length; i++) {
                                        if (selectedIds.some(id => id === items[i].value)) {
                                            for (let p = 0; p < path.length; p++) {
                                                if (!toExpandIds.includes(path[p])) {
                                                    toExpandIds.push(path[p]);
                                                }
                                            }
                                        }

                                        if (Array.isArray(items[i].children)) {
                                            checkItemsForExpansion(items[i].children, [...path, items[i].value]);
                                        }
                                    }
                                }

                                checkItemsForExpansion(data, []);

                                return data;
                            });
                    };

                    treeOptions.mapping = {
                        mapData: mapUniversalItems
                    };
                }

                $tree.rockTree(treeOptions);
                this.updateScrollbar();
            },
            initializeEventHandlers: function () {
                var self = this,
                    $control = $('#' + this.options.controlId),
                    $spanNames = $control.find('.selected-names'),
                    $hfItemIds = $control.find('.js-item-id-value'),
                    $hfItemNames = $control.find('.js-item-name-value');

                // Bind tree events
                $control.find('.treeview')
                    .on('rockTree:selected', function () {
                        // intentionally blank
                    })
                    .on('rockTree:itemClicked', function (e) {
                        // make sure it doesn't autoscroll after something has been manually clicked
                        self.alreadyScrolledToSelected = true;
                        if (!self.options.allowMultiSelect) {
                            $control.find('.picker-btn').trigger('click');
                        }
                    })
                    .on('rockTree:expand rockTree:collapse rockTree:dataBound', function (evt) {
                        self.updateScrollbar();
                    })
                    .on('rockTree:rendered', function (evt) {
                        self.scrollToSelectedItem();
                    });

                $control.find('.picker-label').on('click', function (e) {
                    e.preventDefault();
                    $(this).toggleClass("active");
                    $control.find('.picker-menu').first().toggle(0, function () {
                        self.scrollToSelectedItem();
                    });
                });

                $control.find('.picker-cancel').on('click', function () {
                    $(this).toggleClass("active");
                    $(this).closest('.picker-menu').toggle(0, function () {
                        self.updateScrollbar();
                    });
                    $(this).closest('.picker-label').toggleClass("active");
                });

                // have the X appear on hover if something is selected
                if ($hfItemIds.val() && $hfItemIds.val() !== '0') {
                    $control.find('.picker-select-none').addClass('rollover-item');
                    $control.find('.picker-select-none').show();
                }

                $control.find('.picker-btn').on('click', function (el) {

                    var rockTree = $control.find('.treeview').data('rockTree'),
                        selectedNodes = rockTree.selectedNodes,
                        selectedIds = [],
                        selectedNames = [];
                    $.each(selectedNodes, function (index, node) {
                        var nodeName = $("<textarea/>").html(node.name).text();
                        selectedNames.push(nodeName);
                        if (!selectedIds.includes(node.id)) {
                            selectedIds.push(node.id);
                        }
                    });

                    $hfItemIds.val(selectedIds.join(',')).trigger('change'); // .trigger('change') is used to cause jQuery to fire any "onchange" event handlers for this hidden field.
                    $hfItemNames.val(selectedNames.join(','));

                    // have the X appear on hover. something is selected
                    $control.find('.picker-select-none').addClass('rollover-item');
                    $control.find('.picker-select-none').show();

                    $spanNames.text(selectedNames.join(', '));
                    $spanNames.attr('title', $spanNames.text());

                    $(this).closest('.picker-label').toggleClass("active");
                    $(this).closest('.picker-menu').toggle(0, function () {
                        self.updateScrollbar();
                    });

                    $(this).trigger('onclick');
                    if (!(el && el.originalEvent && el.originalEvent.srcElement == this)) {
                        // if this event was called by something other than the button itself, make sure the execute the href (which is probably javascript)
                        var jsPostback = $(this).attr('href');
                        if (jsPostback) {
                            window.location = jsPostback;
                        }
                    }
                });

                $control.find('.picker-select-none').on("click", function (e) {
                    e.preventDefault();
                    e.stopImmediatePropagation();

                    var rockTree = $control.find('.treeview').data('rockTree');
                    rockTree.clear();
                    $hfItemIds.val('0').trigger('change'); // .trigger('change') is used to cause jQuery to fire any "onchange" event handlers for this hidden field.
                    $hfItemNames.val('');

                    // don't have the X appear on hover. nothing is selected
                    $control.find('.picker-select-none').removeClass('rollover-item').hide();

                    $control.siblings('.js-hide-on-select-none').hide();

                    $spanNames.text(self.options.defaultText);
                    $spanNames.attr('title', $spanNames.text());
                    $(this).trigger('onclick');
                });

                // clicking on the 'select all' btn
                $control.on('click', '.js-select-all', function (e)
                {
                  var rockTree = $control.find('.treeview').data('rockTree');

                  e.preventDefault();
                  e.stopPropagation();

                  var $itemNameNodes = rockTree.$el.find('.rocktree-name');

                  var allItemNodesAlreadySelected = true;
                  $itemNameNodes.each(function (a)
                  {
                    if (!$(this).hasClass('selected')) {
                      allItemNodesAlreadySelected = false;
                    }
                  });

                  if (!allItemNodesAlreadySelected) {
                    // mark them all as unselected (just in case some are selected already), then click them to select them
                    $itemNameNodes.removeClass('selected');
                    $itemNameNodes.trigger('click');
                  } else {
                    // if all were already selected, toggle them to unselected
                    rockTree.setSelected([]);
                    $itemNameNodes.removeClass('selected');
                  }
                });
            },
            updateScrollbar: function (sPosition) {
                var self = this;
                // first, update this control's scrollbar, then the modal's
                var $container = $('#' + this.options.controlId).find('.scroll-container');

                if ($container.is(':visible')) {
                    if (!sPosition) {
                        sPosition = 'relative'
                    }
                    if (self.iScroll) {
                        self.iScroll.refresh();
                    }
                }

                // update the outer modal
                Rock.dialogs.updateModalScrollBar(this.options.controlId);
            },
            scrollToSelectedItem: function () {
                var $selectedItem = $('#' + this.options.controlId).find('.picker-menu').find('.selected').first();
                if ($selectedItem.length && (!this.alreadyScrolledToSelected)) {
                    this.updateScrollbar();
                    this.iScroll.scrollToElement('.selected', '0s');
                    this.alreadyScrolledToSelected = true;
                } else {
                    // initialize/update the scrollbar
                    this.updateScrollbar();
                }
            }
        };

        exports = {
            defaults: {
                id: 0,
                controlId: null,
                universalItemPicker: false,
                restUrl: null,
                restParams: null,
                allowCategorySelection: false,
                categoryPrefix: '',
                allowMultiSelect: false,
                defaultText: '',
                selectedIds: null,
                expandedIds: null,
                expandedCategoryIds: null,
                showSelectChildren: false
            },
            controls: {},
            initialize: function (options) {
                var settings,
                    itemPicker;

                if (!options.controlId) throw 'controlId must be set';
                if (!options.restUrl) throw 'restUrl must be set';

                settings = $.extend({}, exports.defaults, options);

                if (!settings.defaultText) {
                    settings.defaultText = exports.defaults.defaultText;
                }

                itemPicker = new ItemPicker(settings);
                exports.controls[settings.controlId] = itemPicker;
                itemPicker.initialize();
            }
        };

        return exports;
    }());
}(jQuery));
;
; (function () {
    function updateKeyValues(e) {
        var $span = e.closest('span.key-value-list');
        var newValue = '';

        $span.children('span.key-value-rows:first').children('div.controls-row').each(function (index) {
            if (newValue !== '') {
                newValue += '|';
            }

            var keyValue = $(this).children('.key-value-key').first().val();
            var valueValue = $(this).children('.key-value-value').first().val();

            // if the key or value have any magic chars ('^' or '|' or ','' ), URI encode each magic char using encodeURIComponent so that commas also get encoded
            var keyValueDelimiters = ['^', '|', ','];
            keyValueDelimiters.forEach(function (v, i, a) {
                var re = new RegExp('\\' + v, 'g');
                if (keyValue.indexOf(v) > -1) {
                    keyValue = keyValue.replace(re, encodeURIComponent(v));
                }
                if (valueValue.indexOf(v) > -1) {
                    valueValue = valueValue.replace(re, encodeURIComponent(v));
                }
            });

            newValue += keyValue + '^' + valueValue;
        });
        $span.children('input').first().val(newValue);

        raiseKeyValueList();
    }

    Sys.Application.add_load(function () {

        $('a.key-value-add').on('click', function (e) {
            e.preventDefault();
            var $keyValueList = $(this).closest('.key-value-list');
            $keyValueList.find('.key-value-rows').append($keyValueList.find('.js-value-html').val());
            updateKeyValues($(this));
            Rock.controls.modal.updateSize($(this));
        });

        $(document).on('click', 'a.key-value-remove', function (e) {
            e.preventDefault();
            var $rows = $(this).closest('span.key-value-rows');
            $(this).closest('div.controls-row').remove();
            updateKeyValues($rows);
            Rock.controls.modal.updateSize($(this));
        });

        $(document).on('change', '.js-key-value-input', function (e) {
            updateKeyValues($(this));
        });
    });
})();
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.listItems = (function () {
        var exports = {

            initialize: function (options) {
                if (!options.id) {
                    throw 'id is required';
                }

                if (!options.clientId) {
                    throw 'clientId is required';
                }

                const $listItemsControl = $('#' + options.clientId);

                function updateListItemValues($e) {
                    var $span = $e.closest('span.list-items');
                    var keyValuePairs = [];
                    $span.children('span.list-items-rows').first().children('div.controls-row').each(function (index) {
                        keyValuePairs.push({
                            'Key': $(this).children('.input-group').find('.js-list-items-input').first().data('id'),
                            'Value': $(this).children('.input-group').find('.js-list-items-input').first().val()
                        });
                    });
                    $span.children('input').first().val(JSON.stringify(keyValuePairs));
                    if (options.valueChangedScript) {
                        window.location = "javascript:" + options.valueChangedScript;
                    }

                }

                var fixHelper = function (e, ui) {
                    ui.children().each(function () {
                        $(this).width($(this).width());
                    });
                    return ui;
                };

                $listItemsControl.find('a.list-items-add').on('click', function (e) {
                    e.preventDefault();
                    $listItemsControl.find('.list-items-rows').append($listItemsControl.find('.js-list-items-html').val());
                    updateListItemValues($(this));
                    Rock.controls.modal.updateSize($(this));
                });

                // Use add_load to fire with page load and postbacks
                Sys.Application.add_load(function () {
                    function onRemoveClick(event) {
                        event.preventDefault();
                        var $rows = $(this).closest('span.list-items-rows');
                        $(this).closest('div.controls-row').remove();
                        updateListItemValues($rows);
                        Rock.controls.modal.updateSize($(this));
                    }

                    function onFocus(event) {
                        var element = event.target;
                        var $element = $(element);
                        var valueOnFocus = element.value;

                        function onBlur() {
                            var valueOnBlur = element.value;

                            if (valueOnFocus != valueOnBlur) {
                                updateListItemValues($element);
                            }
                        }

                        // Only handle the focus out event once and remove the handler.
                        $element.one('blur', onBlur);
                    }

                    $listItemsControl.find('a.list-items-remove').off('click', onRemoveClick).on('click', onRemoveClick);
                    $listItemsControl.find('.js-list-items-input').off('focus', onFocus).on('focus', onFocus);
                });

                $listItemsControl.find('.list-items-rows').sortable({
                    helper: fixHelper,
                    handle: '.fa-bars',
                    start: function (event, ui) {
                        {
                            var start_pos = ui.item.index();
                            ui.item.data('start_pos', start_pos);
                        }
                    },
                    update: function (event, ui) {
                        updateListItemValues($(this));
                    }

                }).disableSelection();

            }
        };

        return exports;
    }());
}(jQuery));
;
(function($) {
    window.Rock = window.Rock || {};
    window.Rock.controls = window.Rock.controls || {};

    window.Rock.controls.mediaElementPicker = (function () {
        var exports = {
            initialize: function(options) {
                var $picker = $('#' + options.controlId);

                $picker.data('required', options.required === true);

                // Setup click handlers for any refresh buttons.
                $picker.find('.js-media-element-picker-refresh').on('click', function () {
                    if ($(this).hasClass('disabled')) {
                        return false;
                    }

                    $picker.find('.js-media-element-picker-refresh').addClass('disabled');
                    $(this).find('i.fa').addClass('fa-spin');

                    window.location = 'javascript:' + options.refreshScript;

                    return false;
                });

                // Re-validate after they change the selection of the media.
                $picker.find('.js-media-element-value select').on('change', function () {
                    ValidatorValidate(window[$picker.find('.js-media-element-validator').prop('id')]);
                });
            },

            clientValidate: function (validator, args) {
                // After a postback, the validator is not actually in the DOM
                // anymore. So find the fake ancestor's Id and then lookup the
                // real media element picker.
                var mediaElementPickerId = $(validator).closest('.js-media-element-picker').attr('id');
                var $mediaElementPicker = $('#' + mediaElementPickerId);

                var required = $mediaElementPicker.data('required') === true;
                var value = $mediaElementPicker.find('.js-media-element-value select').val() || '';
                var isValid = !required || (value !== '' && value !== '0');

                if (isValid) {
                    $mediaElementPicker.closest('.form-group').removeClass('has-error');
                    args.IsValid = true;
                }
                else {
                    $mediaElementPicker.closest('.form-group').addClass('has-error');
                    args.IsValid = false;
                }
            }
        };

        return exports;
    })();
})(jQuery);
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.mediaPlayer = (function () {
        var exports = {
            initialize: function () {

                Sys.Application.add_load(function () {
                    var cssFile = Rock.settings.get('baseUrl') + 'Scripts/mediaelementjs/mediaelementplayer.min.css';
                    var jsFile = Rock.settings.get('baseUrl') + 'Scripts/mediaelementjs/mediaelement-and-player.js';

                    // ensure that css for mediaelementplayers is added to page
                    if (!$('#mediaElementCss').length) {
                        $('head').append("<link id='mediaElementCss' href='" + cssFile + "' type='text/css' rel='stylesheet' />");
                    }

                    // ensure that js for mediaelementplayers is added to page
                    if (!$('#mediaElementJs').length) {
                        // by default, jquery adds a cache-busting parameter on dynamically added script tags. set the ajaxSetup cache:true to prevent this
                        $.ajaxSetup({ cache: true });
                        $('head').prepend("<script id='mediaElementJs' src='" + jsFile + "' />");
                    }

                    // ensure that mediaelementplayer is applied to all the the rock audio/video that was generated from a Rock Video/Audio FieldType (js-media-audio,js-media-video)
                    $('audio.js-media-audio,video.js-media-video').mediaelementplayer({ enableAutosize: true });
                });

            }
        };

        return exports;
    }());

    /** This is the new Rock media player that uses Plyr. */
    Rock.controls.mediaplayer = (function () {
        var exports = {
            initialize: function (options) {
                if (!options.id) {
                    throw "id is required";
                }

                if (!options.playerId) {
                    throw "playerId is required";
                }

                if (!options.progressId) {
                    throw "progressId is required";
                }

                var $playerElement = $("#" + options.id);
                var playerOptions = $playerElement.data("player-options");

                var player = new Rock.UI.MediaPlayer("#" + options.playerId, playerOptions);

                player.on("ready", function () {
                    $("#" + options.progressId).val(player.percentWatched);
                });

                player.on("progress", function () {
                    console.log("progress", player.percentWatched, options.progressId);
                    $("#" + options.progressId).val(player.percentWatched);
                });

                $playerElement
                    .data("required", options.required || false)
                    .data("progress-id", options.progressId)
                    .data("required-percentage", options.requiredPercentage || 0)
                    .data("required-error-message", options.requiredErrorMessage || "You must watch the video.");
            },

            clientValidate: function (validator, args) {
                var $playerElement = $(validator).closest(".js-media-player");
                var $formGroup = $playerElement.parent().hasClass("control-wrapper") ? $playerElement.closest(".form-group") : null;
                var progressId = $playerElement.data("progress-id");
                var required = $playerElement.data('required');
                var requiredProgress = $playerElement.data("required-percentage");
                var progress = parseFloat($("#" + progressId).val() || "0");

                var isValid = !required || progress >= requiredProgress;

                if (isValid) {
                    if ($formGroup) {
                        $formGroup.removeClass("has-error");
                    }

                    args.IsValid = true;
                }
                else {
                    if ($formGroup) {
                        $formGroup.addClass("has-error");
                    }

                    args.IsValid = false;
                    validator.errormessage = $playerElement.data('required-error-message');
                }
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.mediaSelector = (function () {

        var exports = {
            clientValidate: function (validator, args) {
                var $checkBoxList = $(validator).closest('.js-mediaselector');

                var checkboxes = $checkBoxList.find('input');
                var isValid = false;
                for (var i = 0; i < checkboxes.length; i++) {
                    if (checkboxes[i].checked) {
                        isValid = true;
                        break;
                    }
                }

                if (isValid) {
                    $checkBoxList.removeClass('has-error');
                } else {
                    $checkBoxList.addClass('has-error');
                }

                args.IsValid = isValid;
            }
        };

        return exports;
    }());
}(jQuery));


;
/*
 * BJW  7-7-2020
 * Previous Issues and Fixes
 *
 * April 15, 2020
 * Problem:
 *      Open a modal dialog and close it. Next, try to open a modal alert. An invisible backdrop is added to the
 *      page such that the page cannot be interacted with. The alert does not appear.
 * Solution:
 *      Every postback causes the C# modal control code to register JS that checks a hidden field to determine
 *      if a modal should be open or closed. If many modals exist, then they are all checked and all call
 *      the JS close dialog method, which removed backdrops. If each modal removes any backdrop without
 *      discretion, then they interfere with each other. Therefore, only remove backdrops that belong to the modal
 *      in current context being closed.
 *      https://github.com/SparkDevNetwork/Rock/commit/8a1c5653c534c61548de34d0ca1a4483304437a1
 *
 * June 1, 2020
 * Problem:
 *      When a modal is opened, sometimes the body is set to .modal-open and sometimes a manager (usually the
 *      update panel of the block) is set to .modal-open. In the event of the manager, cleanup was not occurring
 *      and the class remained even after the modal was gone.
 * Solution:
 *      A param was added to the close dialog JS method that allowed proper cleanup of classes (.modal-open) added
 *      to the manager.
 *      https://github.com/SparkDevNetwork/Rock/commit/26cf101ae8e8923008ae42f3d833cab5a35df8cb
 *
 * July 1, 2020
 * Problem:
 *      Open a dialog. Close the dialog with a postback that calls C# Hide with no manager param. Open the dialog again
 *      and there is no backdrop.
 * Solution:
 *      This seems to be an issue with bootstrap (seems unlikely) or the combination of bootstrap with the postback
 *      UI re-rendering. The modal is removed from the DOM before our JS close method is reached. Therefore proper
 *      cleanup of the first modal open is never done. This seems to impact the second opening and bootstrap fails
 *      to add the backdrop. Thus our code adds a backdrop if one is not visible.
 *      https://github.com/SparkDevNetwork/Rock/commit/f08bb6fc1e17b79d0772152707acd4a3876a68d6
 *
 *  July 7, 2020
 *  Problem:
 *      Open a dialog. Close the dialog with a postback that calls C# Hide with no manager param. Open the dialog again
 *      and there is a backdrop but no modal.
 *  Solution:
 *      This is because the postback calls the C# Hide method, which does not provide the manager to the JS close modal
 *      method. Because of this, proper cleanup doesn't occur. We can calculate the manger as we did when we opened the
 *      modal, which fixes the issue.
 *      Also, there was a bug in hiding the backdrop where if the owner no longer existed
 *      in the DOM, then the backdrop was not removed.
 */

(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.modal = (function () {
        // shows the IFrame #modal-popup modal
        var _showModalPopup = function (sender, popupUrl) {
            var $modalPopup = $('#modal-popup');
            var $modalPopupIFrame = $modalPopup.find('iframe');

            // Use the anchor tag's title attribute as the title of the dialog box
            if (sender && sender.attr('title') != undefined) {
                $('#modal-popup_panel h3').html(sender.attr('title') + ' <small></small>');
            }

            $modalPopupIFrame.height('auto');

            $modalPopupIFrame.one('load', function () {

                // now that the iframe is loaded, show it, set it's initial height and do a modal layout
                $('#modal-popup').fadeTo(0, 1);

                var newHeight = $(this.contentWindow.document).height();
                if ($(this).height() != newHeight) {
                    $(this).height(newHeight);
                }

                $('body').addClass('modal-open');
                if (document.body.clientHeight > window.innerHeight) {
                    $('body').css('padding-right', Rock.controls.util.getScrollbarWidth());
                }
                $('#modal-popup').modal('layout');
            });

            // Use the anchor tag's href attribute as the source for the iframe
            // this will trigger the load event (above) which will show the popup
            $('#modal-popup').fadeTo(0, 0);

            // Indicate to Rock that this content is being served in an iFrame modal.
            var separator = popupUrl.indexOf("?") === -1 ? "?" : "&";
            popupUrl = popupUrl + separator + "IsIFrameModal=true";

            $modalPopupIFrame.attr('src', popupUrl);
            $('#modal-popup').modal({
                show: true,
                backdrop: 'static',
                keyboard: false,
                attentionAnimation: '',
                modalOverflow: true
            });
        }

        // Closes the non-iframe modal dialog control
        var _closeModalControl = function ($modalDialog, $manager) {
            if ($modalDialog && $modalDialog.length && $modalDialog.modal) {
                $modalDialog.modal('hide');
            }

            // remove the modal-open class from this modal's manager
            if ($manager && $manager.length) {
                $manager.each(function () {
                    $(this).removeClass('modal-open');
                });
            }

            // if all modals are closed, remove the modal-open class from the body
            if ($('.modal:visible').length === 0) {
                $('body').removeClass('modal-open').css('padding-right', '');
            }

            // Ensure any modalBackdrops are removed if its owner is no longer visible. Note that some
            // backdrops do not have an owner
            $('.modal-backdrop').each(function () {
                var $modalBackdrop = $(this);
                var ownerId = $modalBackdrop.data('modalId');
                var $owner = $('#' + ownerId);
                var isOwnerInDom = $owner.length > 0;

                if (ownerId && (!isOwnerInDom || !$owner.is(':visible'))) {
                    $modalBackdrop.remove();
                }
            });
        };

        // shows a non-IFrame modal dialog control
        var _showModalControl = function ($modalDialog, managerId, clickBackdropToClose, $hfModalVisible) {
            $('body').addClass('modal-open');
            if (document.body.clientHeight > window.innerHeight) {
                $('body').css('padding-right', Rock.controls.util.getScrollbarWidth());
            }

            $modalDialog.modal({
                show: true,
                manager: managerId,
                backdrop: 'static',
                keyboard: false,
                attentionAnimation: '',
                modalOverflow: true,
                replace: true
            });

            if ($('.modal-backdrop').filter(':visible').length === 0) {
                // ensure that there is a modal-backdrop and include its owner as an attribute so that we can remove it when this modal is closed
                $('<div class="modal-backdrop" data-modal-id="' + $modalDialog.prop('id') + '" />').appendTo('body');
            }

            if (clickBackdropToClose) {
                $(".modal-scrollable").click(function (event) {
                    if (event.target === event.currentTarget) {
                        $(".modal-scrollable").off("click");
                        _closeModalControl($modalDialog, $(managerId));

                        if ($hfModalVisible && $hfModalVisible.length) {
                            $hfModalVisible.val(0);
                        }
                    }
                });
            }
        }

        var exports = {
            // updates the side of the modal that the control is in.
            // this function works for both the IFrame modal and ModalDialog control
            updateSize: function (controlId) {
                var $control = typeof (controlId) == 'string' ? $('#' + controlId) : $(controlId);
                if ($control && $control.length) {
                    var $modalBody = $control.closest('.modal-body');
                    if ($modalBody.is(':visible')) {
                        $modalBody[0].style.minHeight = "0";
                        var scrollHeight = $modalBody.prop('scrollHeight');
                        if ($modalBody.outerHeight() != scrollHeight) {
                            // if modalbody didn't already grow to fit (maybe because of a bootstrap dropdown) make modal-body big enough to fit.
                            $modalBody[0].style.minHeight = scrollHeight + "px";

                            // force the resizeDetector to fire
                            if ($('#dialog').length && $('#dialog')[0].resizedAttached) {
                                $('#dialog')[0].resizedAttached.call();
                            }
                        }
                    }
                }

            },
            // closes the #modal-popup modal (IFrame Modal)
            close: function (msg) {
                // do a setTimeout so this fires after the postback
                $('#modal-popup').hide();
                setTimeout(function () {
                    $('#modal-popup iframe').attr('src', '');
                    $('#modal-popup').modal('hide');

                }, 0);

                $('body').removeClass('modal-open').css('padding-right', '');

                if (msg && msg != '') {

                    if (msg == 'PAGE_UPDATED') {
                        location.reload(true);
                    }
                    else {
                        $('#rock-config-trigger-data').val(msg);
                        $('#rock-config-trigger').trigger('click');
                    }
                }
            },
            // closes a ModalDialog control (non-IFrame Modal)
            closeModalDialog: function ($modalDialog, $manager) {
                _closeModalControl($modalDialog, $manager);
            },
            // shows the #modal-popup modal (IFrame Modal)
            show: function (sender, popupUrl, detailsId, postbackUrl) {
                _showModalPopup(sender, popupUrl);
            },
            // shows a ModalDialog control (non-IFrame Modal)
            showModalDialog: function ($modalDialog, managerId, clickBackdropToClose, $hfModalVisible) {
                _showModalControl($modalDialog, managerId, clickBackdropToClose, $hfModalVisible);
            },

            // gets the IFrame element of the global the Modal Popup (for the IFrame Modal)
            getModalPopupIFrame: function () {
                var $modalPopup = $('#modal-popup');
                var $modalPopupIFrame = $modalPopup.find('iframe');

                return $modalPopupIFrame;
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    /** JS helper for the NoteEditor control */
    Rock.controls.noteEditor = (function () {
        var exports = {
            /** initializes the JavasSript for the noteEditor control */
            initialize: function (options) {

                if (!options.id) {
                    throw 'id is required';
                }

                var self = this;

                var $control = $('#' + options.id);

                if ($control.length == 0) {
                    return;
                }

                self.$noteEditor = $control;

                this.initializeEventHandlers();

                if (options.isEditing) {
                    var $noteContainer = self.$noteEditor.closest('.js-notecontainer');

                    if (options.currentNoteId) {
                        // editing an existing note
                        var $displayedNote = $noteContainer.find("[data-note-id='" + options.currentNoteId + "']");

                        // move note editor and display in place of the readonly version of note
                        self.$noteEditor.detach();
                        $displayedNote.parent('.js-note').prepend(self.$noteEditor);
                        self.$noteEditor.fadeIn();

                        $displayedNote.hide();
                    }
                    else if (options.parentNoteId) {
                        // new reply to a note
                        var $replyToNote = $noteContainer.find("[data-note-id='" + options.parentNoteId + "']");

                        // move note editor as a child note of the note we are replying to
                        self.$noteEditor.detach();
                        $replyToNote.append(self.$noteEditor)
                        self.$noteEditor.slideDown().find('textarea').trigger("focus");
                    }
                    else {
                        // new note, so just show it 
                        self.$noteEditor.fadeIn();
                    }
                }
            },

            /**  */
            initializeEventHandlers: function () {
                // Initialize NoteEditor and NoteContainer events
                var self = this;
                var $noteContainer = self.$noteEditor.closest('.js-notecontainer');

                $('.js-addnote,.js-editnote,.js-replynote', $noteContainer).on('click', function (e) {
                    var addNote = $(this).hasClass('js-addnote');
                    var editNote = $(this).hasClass('js-editnote');
                    var replyNote = $(this).hasClass('js-replynote');
                    var cancelNote = $(this).hasClass('js-editnote-cancel');
                    var deleteNote = $(this).hasClass('js-removenote');

                    var sortDirection = $noteContainer.data('sortdirection');
                    var $noteEditor = $noteContainer.find('.js-note-editor');
                    var $currentNote = $(false);
                    $noteEditor.detach();

                    // clear out any previously entered stuff
                    $noteEditor.find('.js-parentnoteid').val('');
                    $noteEditor.find('textarea').val('');
                    $noteEditor.find('input:checkbox').prop('checked', false);
                    $noteEditor.find('.js-notesecurity').hide();

                    var $noteprivateInput = $noteEditor.find('.js-noteprivate');
                    $noteprivateInput.parent().show();

                    if (addNote) {
                        e.preventDefault();
                        e.stopImmediatePropagation();
                        var postbackJs = $noteContainer.find(".js-add-postback").attr('href');
                        window.location = postbackJs;
                        return;
                    }
                    else {
                        $currentNote = $(this).closest('.js-noteviewitem');
                        var currentNoteId = $currentNote.data('note-id');

                        if (replyNote) {
                            $noteContainer.find('.js-currentnoteid').val(currentNoteId);
                            $noteEditor.find('.js-parentnoteid').val(currentNoteId);

                            e.preventDefault();
                            e.stopImmediatePropagation();
                            var postbackJs = $noteContainer.find(".js-reply-to-postback").attr('href');
                            window.location = postbackJs;
                            return;
                        }
                        else if (editNote) {
                            $noteContainer.find('.js-currentnoteid').val(currentNoteId);

                            e.preventDefault();
                            e.stopImmediatePropagation();
                            var postbackJs = $noteContainer.find(".js-edit-postback").attr('href');
                            window.location = postbackJs;
                            return;
                        }
                    }
                });

                $('.js-notesecurity', $noteContainer).on('click', function (e) {
                    var $securityBtn = $(this);
                    var entityTypeId = $securityBtn.data('entitytype-id');
                    var title = $securityBtn.data('title');
                    var currentNoteId = $securityBtn.data('entity-id');
                    var securityUrl = Rock.settings.get('baseUrl') + "Secure/" + entityTypeId + "/" + currentNoteId + "?t=" + title + "&pb=&sb=Done";
                    Rock.controls.modal.show($securityBtn, securityUrl);
                });

                $('.js-editnote-cancel', $noteContainer).on('click', function (e) {
                    var $noteEditor = $noteContainer.find('.js-note-editor');
                    $noteEditor.slideUp();

                    // show any notedetails that might have been hidden when doing the editing
                    $noteEditor.parent().find('.js-noteviewitem').slideDown();
                });

                $('.js-removenote', $noteContainer).on('click', function (e) {
                    var $currentNote = $(this).closest('.js-noteviewitem');
                    var currentNoteId = $currentNote.attr('data-note-id');
                    $noteContainer.find('.js-currentnoteid').val(currentNoteId);

                    e.preventDefault();
                    e.stopImmediatePropagation();
                    var postbackJs = $noteContainer.find(".js-delete-postback").attr('href');
                    return Rock.dialogs.confirm('Are you sure you want to delete this note?', function (result) {
                        if (result) {
                            window.location = postbackJs;
                        }
                    });
                });

                $('.js-expandreply', $noteContainer).on('click', function (e) {
                    var $currentNote = $(this).closest('.js-note');
                    var $childNotesContainer = $currentNote.find('.js-childnotes').first();
                    $childNotesContainer.slideToggle(function (x) {

                        // get a list of noteIds that have their child items visible, so that we can maintain that expansion after a postback
                        var expandedNoteIds = $(this).closest('.js-notecontainer').find('.js-noteviewitem:visible').map(function () {
                            var $noteItem = $(this).closest('.js-note');
                            var $childNotesExpanded = $noteItem.find('.js-childnotes:first').is(':visible');
                            if ($childNotesExpanded) {
                                return $(this).attr('data-note-id');
                            }
                            else {
                                return null;
                            }
                        }).get().join();

                        var $expandedNoteIdsHiddenField = $noteContainer.find('.js-expandednoteids');
                        $expandedNoteIdsHiddenField.val(expandedNoteIds);
                    });
                });
            }
        }

        return exports
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.numberBox = (function () {
        var exports = {
            clientValidate: function (validator, args) {
                var validationControl = $(validator);
                var numberType = validationControl.attr('type');

                // Only validate numeric types - currency and other formats require server validation.
                if (!(numberType === 'double' || numberType === 'integer')) {
                    return;
                }

                var $numberBox = $(validator).closest('.js-number-box');
                var isValid = true;
                var validationMessage = '';

                var label = $numberBox.find('label').text();
                if (!label) {
                    label = "Value";
                }

                var value = args.Value;

                // Check for a valid number - allow a signed or unsigned decimal with or without digit separators.
                var floatRegex = /(?=.*?\d)^[-,+]?(([1-9]\d{0,2}(,\d{3})*)|\d+)?(\.\d{1,9})?$/s;
                validationMessage = label + " must be a valid number.";
                isValid = floatRegex.test(value);

                // Get the numeric value.
                value = parseFloat(value);

                // Check for integer.
                if (isValid) {
                    if (numberType === "integer") {
                        validationMessage = label + " must be an integer value.";
                        isValid = Number.isInteger(value);
                    }
                }

                // Check range.
                if (isValid) {
                    var max = parseFloat(validationControl.attr("max"));
                    var min = parseFloat(validationControl.attr("min"));
                    var checkMaxValue = !Number.isNaN( max );
                    var checkMinValue = !Number.isNaN( min );

                    if ( checkMinValue && checkMaxValue) {
                        validationMessage = label + ' must have a value between ' + min + ' and ' + max + '.';
                        isValid = (value >= min && value <= max);
                    }
                    else if (checkMinValue) {
                        validationMessage = label + ' must have a value of ' + min + ' or more.';
                        isValid = (value >= min);
                    }
                    else if (checkMaxValue) {
                        validationMessage = label + ' must have a value of ' + max + ' or less.';
                        isValid = (value <= max);
                    }
                }

                args.IsValid = isValid;

                // Set the visual feedback.
                if (isValid) {
                    $numberBox.removeClass('has-error');
                    $numberBox.attr('title', '');
                }
                else {
                    $numberBox.addClass('has-error');
                    $numberBox.attr('title', validationMessage);

                    validator.errormessage = validationMessage;
                    validator.isvalid = false;
                }
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.numberRangeEditor = (function () {
        var exports = {
            clientValidate: function (validator, args) {
                var $numberRangeEditor = $(validator).closest('.js-numberrangeeditor');
                var lowerValue = $numberRangeEditor.find('input.js-number-range-lower ').val();
                var upperValue = $numberRangeEditor.find('input.js-number-range-upper').val();
                var required = $numberRangeEditor.attr('data-required') == 'true';
                var itemLabelText = $numberRangeEditor.attr('data-itemlabel');

                var isValid = true;

                if (required) {
                    // if required, then make sure that the number range has a lower and/or upper value (can't both be blank)
                    if (lowerValue.length === 0 && upperValue.length === 0) {
                        isValid = false;
                        validator.errormessage = itemLabelText + ' is required';
                    }
                }

                var control = $numberRangeEditor;
                var labelForSelector = "label[for='" + $numberRangeEditor.prop('id') + "']";
                var $labelFor = $(labelForSelector)
                if (isValid) {
                    control.removeClass('has-error');
                    $labelFor.parent().removeClass('has-error');
                }
                else {
                    control.addClass('has-error');
                    $labelFor.parent().addClass('has-error');
                }

                args.IsValid = isValid;
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.numberUpDown = (function () {

        var exports = {
            adjust: function (btn, adjustment, postbackScript) {

                var $parent = $(btn).closest('div.numberincrement');
                var $min = $parent.find('.js-number-up-down-min').first();
                var $max = $parent.find('.js-number-up-down-max').first();
                var $value = $parent.find('.js-number-up-down-value').first();
                var $lbl = $parent.find('.js-number-up-down-lbl').first();
                var $upBtn = $parent.find('.js-number-up').first();
                var $downBtn = $parent.find('.js-number-down').first();

                // Get the min, max, and new value
                var minValue = parseInt($min.val(), 10);
                var maxValue = parseInt($max.val(), 10);
                var numValue = parseInt($value.val(), 10) + adjustment;

                // If new value is valid, set the hf and lbl
                if (numValue >= minValue && numValue <= maxValue) {
                    $value.val(numValue);
                    $lbl.html(numValue);
                }

                // enable/disable the 'up' button
                if (numValue >= maxValue) {
                    $upBtn.addClass('disabled');
                } else {
                    $upBtn.removeClass('disabled');
                }

                // enable/disable the 'down' button
                if (numValue <= minValue) {
                    $downBtn.addClass('disabled');
                } else {
                    $downBtn.removeClass('disabled');
                }

                if (postbackScript && postbackScript != "" ) {
                    window.location = "javascript:" + postbackScript;
                }

            }
        };

        return exports;
    }());
}(jQuery));;
(function ($)
{
  'use strict';
  window.Rock = window.Rock || {};
  Rock.controls = Rock.controls || {};

  Rock.controls.numberUpDownGroup = (function ()
  {
    var exports = {
      clientValidate: function (validator, args)
      {
        var $numberUpDownGroup = $(validator).closest('.js-number-up-down-group');
        var isValid = true;

        if ($numberUpDownGroup.hasClass('required') === true) {
          isValid = false;

          $numberUpDownGroup.find('.js-number-up-down-lbl').each(function (i) {
            if (parseInt(this.innerText, 10) > 0) {
              isValid = true;
            }
          });
        }

        if (isValid === false) {
          validator.errormessage = $numberUpDownGroup.find('label').text() + " is required";
        }

        var control = $numberUpDownGroup;
        if (isValid)
        {
          control.removeClass('has-error');
        }
        else
        {
          control.addClass('has-error');
        }

        args.IsValid = isValid;
      }
    };

    return exports;
  }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.pbx = (function () {

        var exports = {
            // implements the click to call feature
            originate: function (sourcePersonGuid, destinationNumber, callerId, destinationPersonName, destinationNumberFormatted) {
                var originateUrl = Rock.settings.get('baseUrl') + 'api/Pbx/Originate?sourcePersonGuid=' + sourcePersonGuid + '&destinationPhone=' + destinationNumber + '&callerId=' + callerId;

                console.log('From Rock.controls.originateCall: ' + originateUrl);
                $.get(originateUrl, function (r) {
                    if (!r.Success) {
                        Rock.dialogs.alert("An error occurred while attempting to place call.  " + r.Message);
                    }
                });
            }
        };

        return exports;
    }());
}(jQuery));;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.personPicker = (function () {
        var PersonPicker = function (options) {
            this.controlId = options.controlId;
            this.restUrl = options.restUrl;
            this.restDetailUrl = options.restDetailUrl;
            this.defaultText = options.defaultText || '';
            this.iScroll = null;
            this.$pickerControl = $('#' + this.controlId);
            this.$pickerScrollContainer = this.$pickerControl.find('.js-personpicker-scroll-container');
        };

        PersonPicker.prototype.initializeEventHandlers = function () {
            var controlId = this.controlId,
                restUrl = this.restUrl,
                restDetailUrl = this.restDetailUrl || (Rock.settings.get('baseUrl') + 'api/People/GetSearchDetails'),
                defaultText = this.defaultText;

            var $pickerControl = this.$pickerControl;
            var $searchFields = $pickerControl.find('.js-personpicker-search-field input');
            var $searchFieldName = $pickerControl.find('.js-personpicker-search-name input');
            var $searchFieldAddress = $pickerControl.find('.js-personpicker-search-address input');
            var $searchFieldPhone = $pickerControl.find('.js-personpicker-search-phone input');
            var $searchFieldEmail = $pickerControl.find('.js-personpicker-search-email input');
            var $searchResults = $pickerControl.find('.js-personpicker-searchresults');
            var $pickerToggle = $pickerControl.find('.js-personpicker-toggle');
            var $pickerMenu = $pickerControl.find('.js-personpicker-menu');
            var $pickerSelect = $pickerControl.find('.js-personpicker-select');
            var $pickerSelectNone = $pickerControl.find('.js-picker-select-none');
            var $pickerPersonId = $pickerControl.find('.js-person-id');
            var $pickerPersonName = $pickerControl.find('.js-person-name');
            var $pickerCancel = $pickerControl.find('.js-personpicker-cancel');
            var $pickerToggleAdditionalSearchFields = $pickerControl.find('.js-toggle-additional-search-fields');
            var $pickerAdditionalSearchFields = $pickerControl.find('.js-personpicker-additional-search-fields');
            var $pickerExpandSearchFields = $pickerControl.find('.js-expand-search-fields');

            var includeBusinesses = $pickerControl.find('.js-include-businesses').val() == '1' ? 'true' : 'false';
            var includeDeceased = $pickerControl.find('.js-include-deceased').val() == '1' ? 'true' : 'false';
            var includeDetails = 'true';

            var promise = null;
            var lastSelectedPersonId = null;

            var autoCompletes = $searchFields.autocomplete({
                source: function (request, response) {

                    var search = {
                        name: $searchFieldName.val(),
                        address: $searchFieldAddress.val(),
                        phone: $searchFieldPhone.val(),
                        email: $searchFieldEmail.val()
                    };

                    // make sure that at least one of the search fields has 3 chars in it
                    if ((search.name.length < 3) && (search.address.length < 3) && (search.phone.length < 3) && (search.email.length < 3)) {
                        return;
                    }

                    // abort any searches that haven't returned yet, so that we don't get a pile of results in random order
                    if (promise && promise.state() === 'pending') {
                        promise.abort();
                    }

                    var searchParams = [];
                    if (search.name) {
                        searchParams.push("name=" + encodeURIComponent(search.name));
                    }

                    // also search additional search fields if they are visible
                    if (search.address && $searchFieldAddress.is(':visible')) {
                        searchParams.push("address=" + encodeURIComponent(search.address));
                    }

                    if (search.phone && $searchFieldPhone.is(':visible')) {
                        searchParams.push("phone=" + encodeURIComponent(search.phone));
                    }

                    if (search.email && $searchFieldEmail.is(':visible')) {
                        searchParams.push("email=" + encodeURIComponent(search.email));
                    }

                    var searchQuery = "?" + searchParams.join("&");

                    // set the timeout to 20 seconds, just in case it takes a long time to search
                    promise = $.ajax({
                        url: restUrl
                            + searchQuery
                            + "&includeDetails=" + includeDetails
                            + "&includeBusinesses=" + includeBusinesses
                            + "&includeDeceased=" + includeDeceased,
                        timeout: 20000,
                        dataType: 'json'
                    });

                    // Display a wait indicator to show that the search is now running.
                    if ($('.js-searching-notification').length == 0) {
                        $searchResults.prepend('<i class="fa fa-refresh fa-spin margin-l-md js-searching-notification" style="display: none; opacity: .4;"></i>');
                    }
                    $('.js-searching-notification').fadeIn(800);

                    promise.done(function (data) {
                        $searchResults.html('');
                        response($.map(data, function (item) {
                            return item;
                        }));

                        exports.personPickers[controlId].updateScrollbar();
                    });

                    promise.fail(function (xhr, status, error) {
                        console.log(status + ' [' + error + ']: ' + xhr.responseText);
                        var errorCode = xhr.status;
                        if (errorCode == 401) {
                            $searchResults.html("<li class='text-danger'>Sorry, you're not authorized to search.</li>");
                        }

                        $('.js-searching-notification').remove();
                    });
                },
                // Set minLength to 0, but check that at least one field has 3 chars before fetching from REST.
                // To minimize load on the server, don't trigger the search until a reasonable delay after the last keypress.
                minLength: 0,
                delay: 750,
                html: true,
                appendTo: $searchResults,
                pickerControlId: controlId,
                messages: {
                    noResults: function () { },
                    results: function () { }
                }
            });

            var autoCompleteCustomRenderItem = function ($ul, item) {
                if (this.options.html) {
                    // override jQueryUI autocomplete's _renderItem so that we can do HTML for the ListItems
                    // derived from http://github.com/scottgonzalez/jquery-ui-extensions

                    var inactiveWarning = "";

                    if (!item.IsActive && item.RecordStatus) {
                        inactiveWarning = " <small>(" + item.RecordStatus + ")</small>";
                    }
                    if (item.IsDeceased) {
                        inactiveWarning = " <small class=\"text-danger\">(Deceased)</small>";
                    }

                    var quickSummaryInfo = "";
                    if (item.FormattedAge || item.SpouseNickName) {
                        quickSummaryInfo = " <small class='rollover-item text-muted'>";
                        if (item.FormattedAge) {
                            quickSummaryInfo += "Age: " + item.FormattedAge;
                        }

                        if (item.SpouseNickName) {
                            if (item.FormattedAge) {
                                quickSummaryInfo += "; ";
                            }

                            quickSummaryInfo += "Spouse: " + item.SpouseNickName;
                        }

                        quickSummaryInfo += "</small>";
                    }

                    var $div = $('<div/>').attr('class', 'radio'),

                        $label = $('<label/>')
                            .html('<span class="label-text">' + item.Name + inactiveWarning + quickSummaryInfo + '</span><i class="fa fa-refresh fa-spin margin-l-md loading-notification" style="display: none; opacity: .4;"></i>')
                            .prependTo($div),

                        $radio = $('<input type="radio" name="person-id" />')
                            .attr('id', item.Id)
                            .attr('value', item.Id)
                            .prependTo($label),

                        $li = $('<li/>')
                            .addClass('picker-select-item js-picker-select-item')
                            .attr('data-person-id', item.Id)
                            .attr('data-person-name', item.Name)
                            .html($div),

                        $resultSection = $(this.options.appendTo);

                    var $itemDetailsDiv = $('<div/>')
                        .addClass('picker-select-item-details js-picker-select-item-details clearfix');

                    if (item.SearchDetailsHtml) {
                        $itemDetailsDiv.attr('data-has-details', true).html(item.SearchDetailsHtml);
                    }
                    else {
                        $itemDetailsDiv.attr('data-has-details', false);
                    }

                    if (includeDetails === 'false') {
                        $itemDetailsDiv.hide();
                    }

                    $itemDetailsDiv.appendTo($li);

                    if (!item.IsActive) {
                        $li.addClass('is-inactive');
                    }

                    return $resultSection.append($li);
                }
                else {
                    return $('<li></li>')
                        .data('item.autocomplete', item)
                        .append($('<a></a>').text(item.label))
                        .appendTo($ul);
                }
            }

            // each search field has its own autocomplete object, so we'll need override with our custom _renderItem to each
            $.each(autoCompletes, function (a, b, c) {
                var autoComplete = $(autoCompletes[a]).data('ui-autocomplete');

                // Debugging: override close to prevent it canceling when loosing focus when debugging
                // autoComplete.close = function () { };

                autoComplete._renderItem = autoCompleteCustomRenderItem;
            });

            $pickerToggle.on('click', function (e) {
                e.preventDefault();
                $(this).toggleClass("active");
                $pickerMenu.toggle(0, function () {
                    exports.personPickers[controlId].updateScrollbar();
                    $searchFieldName.trigger('focus');
                });
            });

            $pickerControl.on('click', '.js-picker-select-item', function (e) {
                if (e.type == 'click' && $(e.target).is(':input') == false) {
                    // only process the click event if it has bubbled up to the input tag
                    return;
                }

                e.stopPropagation();

                var $selectedItem = $(this).closest('.js-picker-select-item');
                var $itemDetails = $selectedItem.find('.js-picker-select-item-details');

                var selectedPersonId = $selectedItem.attr('data-person-id');

                if ($itemDetails.is(':visible')) {

                    if (selectedPersonId == lastSelectedPersonId && e.type == 'click') {
                        // if they are clicking the same person twice in a row (and the details are done expanding), assume that's the one they want to pick
                        var selectedText = $selectedItem.attr('data-person-name');

                        setSelectedPerson(selectedPersonId, selectedText);

                        $pickerSelect.trigger('onclick');
                        // Fire the postBack for the Select button.
                        var postBackUrl = $pickerSelect.prop('href');
                        if (postBackUrl) {
                            window.location = postBackUrl;
                        }
                    } else {

                        // if it is already visible but isn't the same one twice, just leave it open
                    }
                }

                if (includeDetails === 'false') {
                    // hide other open details
                    $('.js-picker-select-item-details', $pickerControl).filter(':visible').each(function () {
                        var $el = $(this),
                            currentPersonId = $el.closest('.js-picker-select-item').attr('data-person-id');

                        if (currentPersonId != selectedPersonId) {
                            $el.slideUp();
                            exports.personPickers[controlId].updateScrollbar();
                        }
                    });
                }

                lastSelectedPersonId = selectedPersonId;

                if ($itemDetails.attr('data-has-details') == 'false') {
                    // add a spinner in case we have to wait on the server for a little bit
                    var $spinner = $selectedItem.find('.loading-notification');
                    $spinner.fadeIn(800);

                    // fetch the search details from the server
                    $.get(restDetailUrl + '?Id=' + selectedPersonId, function (responseText, textStatus, jqXHR) {
                        $itemDetails.attr('data-has-details', true);

                        // hide then set the HTML so that we can get the slideDown effect
                        $itemDetails.stop().hide().html(responseText);
                        showItemDetails($itemDetails);

                        $spinner.stop().fadeOut(200);
                    });
                } else {
                    showItemDetails($selectedItem.find('.picker-select-item-details:hidden'));
                }
            });

            var showItemDetails = function ($itemDetails) {
                if ($itemDetails.length) {
                    $itemDetails.slideDown(function () {
                        exports.personPickers[controlId].updateScrollbar();
                    });
                }
            }

            $pickerControl.on('mouseenter',
                function () {
                    // only show the X if there is something picked
                    if (($pickerPersonId.val() || '0') !== '0') {
                        $pickerSelectNone.addClass('show-hover');
                    }
                });

            $pickerCancel.on('click', function () {

                clearSearchFields();
                $pickerMenu.slideUp(function () {
                    exports.personPickers[controlId].updateScrollbar();
                });
            });

            $pickerSelectNone.on('click', function (e) {
                // prevent the click from bubbling up to the pickerControl click event
                e.preventDefault();
                e.stopPropagation();

                var selectedValue = '0',
                    selectedText = defaultText;

                $pickerPersonId.val(selectedValue);
                $pickerPersonName.val(selectedText);
                // run onclick event from the button
                $(this).trigger('onclick');
            });

            // disable the enter key : this will prevent the enter key from clearing the loaded search query.
            $pickerControl.on('keypress', function (e) {
                if (e.which == 13) {
                    return false;
                }
            });

            var setSelectedPerson = function (selectedValue, selectedText) {
                var selectedPersonLabel = $pickerControl.find('.js-personpicker-selectedperson-label');

                $pickerPersonId.val(selectedValue);
                $pickerPersonName.val(selectedText);

                selectedPersonLabel.val(selectedValue);
                selectedPersonLabel.text(selectedText);

                $pickerMenu.slideUp();
            }

            var clearSearchFields = function () {
                $searchFieldName.val('');
                $searchFieldAddress.val('');
                $searchFieldPhone.val('');
                $searchFieldEmail.val('');
            }

            $pickerSelect.on('click', function () {
                var $radInput = $pickerControl.find('input:checked'),
                    selectedValue = $radInput.val(),
                    selectedText = $radInput.closest('.js-picker-select-item').attr('data-person-name');

                setSelectedPerson(selectedValue, selectedText);
                clearSearchFields();
            });

            var toggleSearchFields = function () {
                var expanded = $pickerExpandSearchFields.val();
                if (expanded == 1) {
                    $pickerAdditionalSearchFields.slideDown();
                }
                else {
                    $pickerAdditionalSearchFields.slideUp();
                }

                $pickerToggleAdditionalSearchFields.toggleClass('active', expanded == 1);
            };

            toggleSearchFields();

            $pickerToggleAdditionalSearchFields.on('click', function () {
                var expanded = $pickerExpandSearchFields.val();
                if (expanded == 1) {
                    expanded = 0;
                }
                else {
                    expanded = 1;
                }

                $pickerExpandSearchFields.val(expanded);

                toggleSearchFields();
            });

            $('.js-select-self', $pickerControl).on('click', function () {
                var selectedValue = $('.js-self-person-id', $pickerControl).val(),
                    selectedText = $('.js-self-person-name', $pickerControl).val();

                setSelectedPerson(selectedValue, selectedText);

                // fire the postBack of the btnSelect if there is one
                $pickerSelect.trigger('onclick');
                var postBackUrl = $pickerSelect.prop('href');
                if (postBackUrl) {
                    window.location = postBackUrl;
                }
            });
        };

        PersonPicker.prototype.updateScrollbar = function () {
            var self = this;

            // first, update this control's scrollbar, then the modal's

            if (self.$pickerScrollContainer.is(':visible')) {
                if (self.iScroll) {
                    self.iScroll.refresh();
                }
            }

            // update the outer modal scrollbar
            Rock.dialogs.updateModalScrollBar(this.controlId);
        }

        PersonPicker.prototype.initialize = function () {

            this.iScroll = new IScroll($('.viewport', this.$pickerControl)[0], {
                mouseWheel: true,
                indicators: {
                    el: $('.track', this.$pickerScrollContainer)[0],
                    interactive: true,
                    resize: false,
                    listenY: true,
                    listenX: false,
                },
                click: false,
                preventDefaultException: { tagName: /.*/ }
            });

            this.initializeEventHandlers();
        };

        var exports = {
            personPickers: {},
            findControl: function (controlId) {
                return exports.personPickers[controlId];
            },
            initialize: function (options) {
                if (!options.controlId) throw '`controlId` is required.';
                if (!options.restUrl) throw '`restUrl` is required.';

                var personPicker = new PersonPicker(options);
                exports.personPickers[options.controlId] = personPicker;
                personPicker.initialize();
            }
        };
        return exports;
    }());
}(jQuery));
;
(function($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.priorityNav = (function() {
        var RESIZE_DURATION = 500;
        var TAB_KEYCODE = 9;
        var ClassName = {
            PRIORITY: 'priority',
            HIDE: 'sr-only',
            RESIZING: 'resizing'
        };
        var Selector = {
            NAV_ELEMENTS: "li:not('.overflow-nav')",
            FIRST_ELEMENT: 'li:first',
            PRIORITY_ELEMENT: '.priority'
        };
        var MenuLabelDefault = 'More';
        var MenuLabelAllHiddenDefault = 'Menu';
        var MenuLabelCarat = '<i class="ml-1 fa fa-angle-down" />'
        var MenuTemplate = function(MenuLabel) {
            return '<li class="overflow-nav dropdown d-none"><a href="#" class="dropdown-toggle nav-link overflow-nav-link" data-toggle="dropdown" role="button" aria-haspopup="true">' + MenuLabel + '</a><ul class="overflow-nav-list dropdown-menu dropdown-menu-right"></ul></li>';
        };
        var PriorityNav = function (options) {
            var element = $('.nav-tabs');

            if (options && options.controlId) {
                element = $('#' + options.controlId);
            } else {
                options = '';
            }

            this._element = element;
            this._config = options;

            if ($(element).is('ul')) {
                this._$menu = $(element);
            } else {
                this._$menu = $(element)
                    .find('ul')
                    .first();
            }
            this._initMenu();
            this._$allNavElements = this._$menu.find(Selector.NAV_ELEMENTS);
            this._bindUIActions();
            this._setupMenu();
        };

        PriorityNav.prototype = {
            constructor: PriorityNav,
            initialize: function() {},
            _initMenu: function() {
                var MenuLabel = this._config.MenuLabel

                if (typeof MenuLabel === 'undefined') {
                  MenuLabel = MenuLabelDefault
                }

                // add menu template
                this._$menu.append(MenuTemplate(MenuLabel));
            },

            _setupMenu: function() {
                var $allNavElements = this._$allNavElements;
                // Checking position of the menu
                var menuPosition = this._$menu.position();
                // Get position of right
                var menuRight = menuPosition.left + this._$menu.outerWidth();
                // Checking top position of first item (sometimes changes)
                var firstPos = this._$menu.find(Selector.FIRST_ELEMENT).position();

                // Empty collection in which to put menu items to move
                var $wrappedElements = $();

                // Used to snag the previous menu item in addition to ones that have wrapped
                var first = true;

                // Loop through all the nav items...
                this._$allNavElements.each(function(i) {
                    var $elm = $(this);

                    // ...in which to find wrapped elements
                    var pos = $elm.position();
                    // Get position of right
                    var right = pos.left + $elm.outerWidth();

                    if (pos.top !== firstPos.top || right > menuRight) {
                        // If element is wrapped, add it to set
                        $wrappedElements = $wrappedElements.add($elm);

                        // Add the previous one too, if first
                        if (first) {
                            $wrappedElements = $wrappedElements.add($allNavElements.eq(i - 1));
                            first = false;
                        }
                    }
                });

                if ($wrappedElements.length) {
                    // Clone set before altering
                    var newSet = $wrappedElements.clone();

                    // Hide ones that we're moving
                    $wrappedElements.addClass(ClassName.HIDE);
                    $wrappedElements.find('a').attr('tabindex', -1);

                    // Add wrapped elements to dropdown
                    this._$menu.find('.overflow-nav-list').append(newSet);

                    // Show new menu
                    this._$menu.find('.overflow-nav').removeClass('d-none');

                    // Check if menu doesn't overflow after process
                    if (this._$menu.find('.overflow-nav').position().top !== firstPos.top) {
                        var $item = $(this._element)
                            .find('.' + ClassName.HIDE)
                            .first()
                            .prev();
                        var $itemDuplicate = $item.clone();

                        $item.addClass(ClassName.HIDE);
                        $item.find('a').attr('tabindex', -1);

                        this._$menu.find('.overflow-nav-list').prepend($itemDuplicate);
                    }

                    if ($allNavElements.length == $wrappedElements.length) {
                        this._$menu.find('.overflow-nav-link').html(MenuLabelAllHiddenDefault + ' ' + MenuLabelCarat);
                        this._$menu.find('.overflow-nav-list').removeClass('dropdown-menu-right');
                    } else {
                        this._$menu.find('.overflow-nav-link').html(MenuLabelDefault + ' ' + MenuLabelCarat);
                        this._$menu.find('.overflow-nav-list').addClass('dropdown-menu-right');
                    }
                }

                // hide menu from AT
                this._$menu.find('.overflow-nav').attr('aria-hidden', true);
            },

            _tearDown: function() {
                this._$menu.find('.overflow-nav-list').empty();
                this._$menu.find('.overflow-nav').addClass('d-none');
                this._$allNavElements.removeClass(ClassName.HIDE);
                this._$allNavElements.find('a').attr('tabindex', 0);
            },

            _bindUIActions: function() {
                var self = this;
                $(window).on('resize', function() {
                    self._$menu.addClass(ClassName.RESIZING);

                    setTimeout( function() {
                        self._tearDown();
                        self._setupMenu();
                        self._$menu.removeClass(ClassName.RESIZING);
                    }, RESIZE_DURATION);
                });

                this._$menu.find('.overflow-nav .dropdown-toggle').on('keyup', function(e) {
                    if (e.which === TAB_KEYCODE) {
                        $(e.target).dropdown('toggle');
                    }
                });
            }
        }

        var exports = {
            defaults: {
                controlId: null,
                name: 'prioritynav'
            },
            controls: {},
            initialize: function (options) {
                var settings = $.extend({}, exports.defaults, options);

                if (!settings.controlId) throw 'controlId is required';

                var priorityNav = new PriorityNav(settings);

                // Delay initialization until after the DOM is ready
                $(function () {
                    priorityNav.initialize();
                });
            }
        };

        return exports;
    })();
})(jQuery);
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.rangeSlider = (function () {
        var exports = {
            initialize: function (options) {
                if (!options.controlId) {
                    throw 'controlId is required';
                }

                Sys.Application.add_load(function () {
                    var cssFile = Rock.settings.get('baseUrl') + 'Scripts/ion.rangeSlider/css/ion.rangeSlider.Rock.css';

                    // ensure that css for rangeSlider is added to page
                    if (!$('#rangeSliderCss').length) {
                        $('head').append("<link id='rangeSliderCss' href='" + cssFile + "' type='text/css' rel='stylesheet' />");
                    }
                });

                // use https://github.com/IonDen/ion.rangeSlider/ to make a slider
                // see https://github.com/IonDen/ion.rangeSlider/#settings for settings
                $('#' + options.controlId).ionRangeSlider({
                    type: options.type || 'single', // Choose slider type, could be 'single' for one handle, or 'double' for two handles
                    min: options.min || 0,
                    max: options.max || 100,
                    step: options.step || 1,
                    from: options.from || null, // for 'single' the position of the slider. for 'double' the lower position of the selected range
                    to: options.to || null, // if 'double' the upper position of the selected range
                    disable: options.disable || false // if 'true', the control will render as read-only
                });

            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.rockCheckBoxList = (function () {

        var exports = {
            clientValidate: function (validator, args) {
                var $checkBoxList = $(validator).closest('.js-rockcheckboxlist');

                var checkboxes = $checkBoxList.find('input');
                var isValid = false;
                for (var i = 0; i < checkboxes.length; i++) {
                    if (checkboxes[i].checked) {
                        isValid = true;
                        break;
                    }
                }

                if (isValid) {
                        $checkBoxList.removeClass('has-error');
                } else {
                        $checkBoxList.addClass('has-error');
                }

                args.IsValid = isValid;
            }
        };

        return exports;
    }());
}(jQuery));


;
(function (Sys) {
    'use strict';
    Sys.Application.add_load(function () {

        var $chosenDropDowns = $('.chosen-select');
        if ($chosenDropDowns.length) {
            // IE fix for 'chosen' causing activeElement to be a plain generic object (not a real node) after postback
            if (document.activeElement && !document.activeElement.nodeType) {
                $('body').trigger('focus');
            }

            $chosenDropDowns.chosen({
                width: '100%',
                allow_single_deselect: true,
                placeholder_text_multiple: ' ',
                placeholder_text_single: ' '
            });

            $chosenDropDowns.on('chosen:showing_dropdown chosen:hiding_dropdown', function (evt, params) {
                // update the outer modal
                Rock.dialogs.updateModalScrollBar(this);
            });

            var $chosenDropDownsAbsolute = $chosenDropDowns.filter('.chosen-select-absolute');
            if ($chosenDropDownsAbsolute.length) {
                $chosenDropDownsAbsolute.on('chosen:showing_dropdown', function (evt, params) {
                    $(this).next('.chosen-container').find('.chosen-drop').css('position', 'relative');
                });
                $chosenDropDownsAbsolute.on('chosen:hiding_dropdown', function (evt, params) {
                    $(this).next('.chosen-container').find('.chosen-drop').css('position', 'absolute');
                });
            }
        }
    });
}(Sys));
;
(function ($, Sys) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.scheduleBuilder = (function () {
        var exports,
            ScheduleBuilder = function (options) {
                this.id = options.id;
            };

        ScheduleBuilder.prototype.initializeEventHandlers = function () {

            var id = this.id,
                $modal = $('#' + id).closest('.rock-modal');


            $modal.find('.schedule-type').off('click').on('click',function () {
                var recurrenceState = $('input[class=schedule-type]:checked').data('schedule-type');

                if (recurrenceState === 'schedule-onetime') {
                    $modal.find('.js-schedule-recurrence-panel').slideUp();
                } else {
                    $modal.find('.js-schedule-recurrence-panel').slideDown();
                }
            });

            $modal.find('.recurrence-pattern-radio').off('click').on('click', function () {

                var recurrencePattern = '.' + $('input[class=recurrence-pattern-radio]:checked').data('recurrence-pattern');

                if ($modal.find(recurrencePattern)) {

                    $modal.find('.recurrence-pattern-type').not(recurrencePattern).hide();

                    if (recurrencePattern == ".recurrence-pattern-specific-date") {
                        /* don't show continue-until or exclusion dates for specific date mode (since it doesn't make sense, plus ical ends up deleting them) */
                        $modal.find('.js-continue-until').hide();
                        $modal.find('.js-exclusion-dates').hide();
                    }
                    else {
                        $modal.find('.js-continue-until').show();
                        $modal.find('.js-exclusion-dates').show();
                    }

                    $(recurrencePattern).fadeIn();
                }
            });

            /** Specific Dates Scripts**/

            // show datepicker, ok, cancel so that new date can be added to the list
            $modal.find('.add-specific-date').off('click').on('click', function () {
                $(this).hide();
                $modal.find('.js-add-specific-date-group').show();
            });

            // add new date to list when ok is clicked
            $modal.find('.add-specific-date-ok').off('click').on('click', function () {

                // get date list from hidden field
                var dateListValues = $modal.find('.recurrence-pattern-type > input:hidden').val().split(',');

                if (!dateListValues[0]) {
                    // if dateList is blank, initialize as a new empty array
                    dateListValues = [];
                }

                // validate

                // set colors back to default just in case previously marked invalid
                var $datepicker = $modal.find('.specific-date input');
                $datepicker.css('color', '');

                // var checkDate = Date.parse($datepicker.val());
                if ($datepicker.val() == null || $datepicker.val() == "") {
                    // blank, don't color, just return
                    return;
                }

                var locale = window.navigator.userLanguage || window.navigator.language;
                moment.locale(locale);
                var checkDate = moment($datepicker.val(), 'l');

                if (!checkDate.isValid) {
                    // invalid date entered, color red and return
                    $datepicker.css('color', 'red');
                    return;
                }

                var newDate = $datepicker.val();

                // delete newDate from list in case it is already there
                var index = dateListValues.indexOf(newDate);
                if (index >= 0) {
                    dateListValues.splice(index, 1);
                }

                // add new date to list
                dateListValues.push(newDate);

                // save list back to hidden field
                $modal.find('.js-specific-datelist-values').val(dateListValues);

                // rebuild the UL
                var dateList = $modal.find(".lstSpecificDates");
                dateList.children().remove();
                $.each(dateListValues, function (i, value) {
                    // add to ul
                    var newLi = "<li><span>" + value + "</span> <a href='#' style='display: none'> <i class='fa fa-times'></i></a></li>";
                    dateList.append(newLi);
                });

                $modal.find('.js-add-specific-date-group').hide();
                $modal.find('.add-specific-date').show();
            });

            // cancel out of adding a new date
            $modal.find('.add-specific-date-cancel').off('click').on('click', function () {
                $modal.find('.js-add-specific-date-group').hide();
                $modal.find('.add-specific-date').show();
            });

            // fadeIn/fadeOut the X buttons to delete dates
            $modal.find('.lstSpecificDates').on('mouseenter',
                function () {
                    $(this).find('li a').stop(true, true).show();
                }).on('mouseleave',
                function () {
                    $(this).find('li a').stop(true, true).fadeOut(500);
                });

            // delete specific date from list
            $modal.find('.lstSpecificDates').off('click').on('click', 'li a', function () {
                var selectedDate = $(this).siblings().text();

                // get date list from hidden field
                var $hiddenField = $modal.find('.js-specific-datelist-values');
                var dateList = $hiddenField.val().split(',');

                // delete selectedDate
                var index = dateList.indexOf(selectedDate);
                if (index >= 0) {
                    dateList.splice(index, 1);
                }

                // save list back to hidden field
                $hiddenField.val(dateList);

                // remove date from ul list
                var liItem = $(this).parent();
                liItem.remove();
            });

            /** Exclusion DateRanges Scripts **/

            // show dateRangepicker, ok, cancel so that new dateRange can be added to the list
            $modal.find('.add-exclusion-daterange').off('click').on('click', function () {

                $(this).hide();
                $modal.find('.js-add-exclusion-daterange-group').show(function () {
                    Rock.controls.modal.updateSize();
                });
            });

            // add new date to list when ok is clicked
            $modal.find('.add-exclusion-daterange-ok').off('click').on('click', function () {

                // get daterange list from hidden field
                var dateRangeListValues = $modal.find('.js-exclusion-daterange-list-values').val().split(',');
                if (!dateRangeListValues[0]) {
                    // if blank, initialize as a new empty array
                    dateRangeListValues = [];
                }

                var $exclusionDateRange = $modal.find('.js-exclusion-date-range-picker input');
                var newDateRange = $exclusionDateRange.first().val() + ' - ' + $exclusionDateRange.last().val();

                // delete newDateRange from list in case it is already there
                var index = dateRangeListValues.indexOf(newDateRange);
                if (index >= 0) {
                    dateRangeListValues.splice(index, 1);
                }

                // add new dateRange to list
                dateRangeListValues.push(newDateRange);

                // save list back to hidden field
                $modal.find('.js-exclusion-daterange-list-values').val(dateRangeListValues);

                // rebuild the UL
                var dateRangeList = $modal.find('.lstExclusionDateRanges');
                dateRangeList.children().remove();
                $.each(dateRangeListValues, function (i, value) {
                    // add to ul
                    var newLi = "<li><span>" + value + "</span><a href='#' style='display: none'><i class='fa fa-times'></i></a></li>";
                    dateRangeList.append(newLi);
                });

                $modal.find('.js-add-exclusion-daterange-group').hide();
                $modal.find('.add-exclusion-daterange').show();
            });

            // cancel out of adding a new dateRange
            $modal.find('.add-exclusion-daterange-cancel').off('click').on('click', function () {
                $modal.find('.js-add-exclusion-daterange-group').hide();
                $modal.find('.add-exclusion-daterange').show();
            });

            // fadeIn/fadeOut the X buttons to delete dateRanges
            $modal.find('.lstExclusionDateRanges').on('mouseenter',
                function () {
                    $(this).find('li a').stop(true, true).show();
                }).on('mouseleave',
                function () {
                    $(this).find('li a').stop(true, true).fadeOut(500);
                });

            // delete dateRange from list
            $modal.find('.lstExclusionDateRanges').off('click').on('click', 'li a', function () {
                var selectedDateRange = $(this).siblings('span').text();

                // get dateRange list from hidden field
                var $hiddenField = $modal.find('.js-exclusion-daterange-list-values');
                var dateRangeList = $hiddenField.val().split(',');

                // delete selectedDateRange
                var index = dateRangeList.indexOf(selectedDateRange);
                if (index >= 0) {
                    dateRangeList.splice(index, 1);
                }

                // save list back to hidden field
                $hiddenField.val(dateRangeList);

                // remove dateRange from ul list
                var liItem = $(this).parent();
                liItem.remove();
            });

            // validate on Save.  Make sure they have at least a StartDate and Time set
            $modal.find('.js-modaldialog-save-link').off('click').on('click', function (event) {

                var locale = window.navigator.userLanguage || window.navigator.language;
                moment.locale(locale);

                var $datetimepicker = $modal.find('[id*="dpStartDateTime"]').find('input'),
                    startDateValue = moment($datetimepicker.first().val(), 'l'),
                    startTimeValue = $datetimepicker.last().val();

                if (!startDateValue.isValid) {
                    $datetimepicker.parents('.form-group').first().toggleClass('has-error', 1);
                    event.preventDefault();
                    return;
                }
                else {
                    $datetimepicker.parents('.form-group').first().toggleClass('has-error', 0);
                }
            });
        };

        exports = {
            cache: {},
            initialize: function (options) {
                if (!options.id) {
                    throw 'id is required';
                }

                var sb = new ScheduleBuilder(options);
                exports.cache[options.id] = sb;

                Sys.Application.add_load(function () {
                    sb.initializeEventHandlers();
                });
            }
        };

        return exports;
    }());
}(jQuery, Sys));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.screenKeyboard = (function () {
        //
        // Handle toggling the Shift key states.
        //
        var toggleShift = function ($keyboard) {
            $keyboard.find('[data-command="shift"]').toggleClass('active');

            setAltMode($keyboard, $keyboard.find('[data-command="shift"].active').length > 0);
        };

        //
        // Handle setting digits to either normal value or alternate value.
        //
        var setAltMode = function ($keyboard, altMode) {
            $keyboard.find('.digit').each(function () {
                $(this).text(altMode === true ? $(this).data('alt-value') : $(this).data('value'));
            });
        };

        //
        // Process normal digit presses.
        //
        var digitClick = function () {
            var $keyboard = $(this).closest('.screen-keyboard');
            var $target = $('#' + $keyboard.data('target'));

            $target.val($target.val() + $(this).text());
            $target.trigger('input');

            if ($keyboard.find('[data-command="shift"].active').length > 0) {
                toggleShift($keyboard);
            }
        };

        //
        // Process special command keys.
        //
        var commandClick = function (options) {
            var $keyboard = $(this).closest('.screen-keyboard');
            var $target = $('#' + $keyboard.data('target'));
            var command = $(this).data('command');

            if (command === 'backspace') {
                var val = $target.val();
                if (val.length > 0) {
                    val = val.substr(0, val.length - 1);
                }
                $target.val(val);
                $target.trigger('input');
            }
            else if (command === 'clear') {
                $target.val('');
                $target.trigger('input');
            }
            else if (command === 'shift') {
                $keyboard.find('[data-command="caps"]').removeClass('active');
                toggleShift($keyboard);
            }
            else if (command === 'caps') {
                $keyboard.find('[data-command="shift"]').removeClass('active');
                $(this).toggleClass('active');

                setAltMode($keyboard, $(this).hasClass('active'));
            }
            else if (command === 'tab') {
                /* TODO: What do we do with this action? */
            }
            else if (command === 'enter') {
                window.location = "javascript:__doPostBack('" + options.postback + "', 'enter')";
            }
        };

        var exports = {
            initialize: function (options) {
                if (!options.id) {
                    throw 'id is required';
                }

                var $keyboard = $('#' + options.id);

                $keyboard.find('.digit').on('click', digitClick);
                $keyboard.find('.command').on('click', function () { commandClick.apply(this, [options]); });
            }
        };

        return exports;
    }());
}(jQuery));
;
(function () {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.searchField = (function () {
        var exports,
            SearchField = function (options) {
                this.controlId = options.controlId;
                this.$el = $('#' + this.controlId);
                this.name = options.name;
            };

        SearchField.prototype = {
            constructor: SearchField,
            initialize: function () {
                var self = this;

                var key = sessionStorage.getItem("com.rockrms.search");
                if (key && key != '') {
                    var $search = self.$el.parents('.smartsearch');
                    $search.find('input:hidden').val(key);
                    $search.find('a.dropdown-toggle > span').html($search.find('li[data-key="' + key + '"] > a').html());
                }

                this.$el.typeahead({
                    name: this.name,
                    limit: 15,
                    remote: {
                        url: Rock.settings.get('baseUrl') + 'api/search?type=%TYPE&term=%QUERY&$top=15',
                        replace: function (url, uriEncodedQuery) {
                            var query = url;
                            query = query.replace('%TYPE', self.$el.parents('.smartsearch').find('input:hidden').val());
                            query = query.replace('%QUERY', uriEncodedQuery);
                            return query;
                        }
                    }
                });

                this.initializeEventHandlers();
            },
            initializeEventHandlers: function () {
                var self = this,
                    search = function (term) {

                        // clear the selected item from the input as it might have HTML which would show while the search page loads
                        var searchControl = document.getElementById(self.controlId);
                        searchControl.value = '';

                        // search for data elements in the search term
                        var $dataEl = $("<p>" + term + "</p>").find("data").first();

                        // see if this is a universal search by looking for return-type and return-id data params
                        var returnType = $dataEl.attr("return-type");
                        var returnId = $dataEl.attr("return-id");

                        if (returnType == null || returnId == null) {
                            var otherParams = '';

                            // take any data attributes and add them as query parameters to the url
                            $dataEl.each(function () {
                                $.each(this.attributes, function () {
                                    if (this) {
                                        otherParams += '&' + this.name + '=' + encodeURIComponent(this.value);
                                    }
                                });
                            });

                            // remove any search accessories. These are html elements with the class .search-accessory
                            var resultElement = document.createElement('div');
                            resultElement.innerHTML = term;
                            //var resultElement = $("<div/>").html(term);
                            var accessories = resultElement.getElementsByClassName('search-accessory');

                            // remove all accessory elements
                            while (accessories[0]) { 
                                accessories[0].parentNode.removeChild(accessories[0]);
                            }

                            // remove any html from the search term before putting it in the url
                            var targetTerm = resultElement.textContent.trim();

                            var keyVal = self.$el.parents('.smartsearch').find('input:hidden').val(),
                                $li = self.$el.parents('.smartsearch').find('li[data-key="' + keyVal + '"]'),
                                targetUrl = $li.attr('data-target'),
                                url = Rock.settings.get('baseUrl') + targetUrl.replace('{0}', encodeURIComponent(targetTerm));

                            if (otherParams != '') {
                                if (url.indexOf('?') > -1) {
                                    url += otherParams
                                } else {
                                    url += otherParams.replace(/^&/, '?')
                                }
                            }

                            window.location = url;
                        } else {
                            // universal search uses returnType and returnId, so build the url for that
                            var keyVal = self.$el.parents('.smartsearch').find('input:hidden').val(),
                                $li = self.$el.parents('.smartsearch').find('li[data-key="' + keyVal + '"]'),
                                targetUrl = $li.attr('data-target'),
                                url = Rock.settings.get('baseUrl') + targetUrl.replace('{0}', encodeURIComponent(returnType) + "/" + encodeURIComponent(returnId));

                            window.location = url;
                        }
                    };

                // Listen for typeahead's custom events and trigger search when hit
                this.$el.on('typeahead:selected typeahead:autocompleted', function (e, obj, name) {
                    search(obj.value);
                });

                // Listen for the ENTER key being pressed while in the search box and trigger search when hit
                this.$el.on('keydown', function (e) {
                    if (e.keyCode === 13) {
                        e.preventDefault();
                        return false;
                    }
                });
                this.$el.on('keyup', function (e) {
                    if (e.keyCode === 13 && "" !== $(this).val().trim() ) {
                        search($(this).val());
                    }
                });

                // Wire up "change" handler for search type "dropdown menu"
                this.$el.parents('.smartsearch').find('.dropdown-menu a').on('click', function () {
                    var $this = $(this),
                        text = $this.html();

                    var key = $this.parent().attr('data-key');
                    sessionStorage.setItem("com.rockrms.search", key);

                    $this.parents('.dropdown-menu').siblings('.navbar-link').find('span').html(text);
                    self.$el.parents('.smartsearch').find('input:hidden').val(key)
                });
            }
        };

        exports = {
            defaults: {
                controlId: null,
                name: 'search'
            },
            controls: {},
            initialize: function (options) {
                var searchField,
                    settings = $.extend({}, exports.defaults, options);

                if (!settings.controlId) throw 'controlId is required';

                if (!exports.controls[settings.controlId]) {
                    searchField = new SearchField(settings);
                    exports.controls[settings.controlId] = searchField;
                } else {
                    searchField = exports.controls[settings.controlId];
                }

                // Delay initialization until after the DOM is ready
                $(function () {
                    searchField.initialize();
                });
            }
        };

        return exports;
    }());
}());
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.slidingDateRangePicker = (function () {
        var exports = {
            initialize: function (options) {
                if (!options.id) {
                    throw 'id is required';
                }

                var $picker = $('#' + options.id);

                // the dropdown with current, last, previous, daterange in it
                var $select = $picker.find('.js-slidingdaterange-select');

                showHideControls($picker, $select);
                updateDateRangeInfo($picker);

                $select.on('change', function () {
                    showHideControls($picker, $select);
                    updateDateRangeInfo($picker);
                });

                $('.js-number, .js-time-units-singular, .js-time-units-plural, .js-slidingdaterange-select, .js-lower, .js-upper', $picker).on('change', function () {
                    updateDateRangeInfo($picker);
                });

                $('.js-number', $picker).on('keyup', function () {
                    updateDateRangeInfo($picker);
                });
            }
        },
        showHideControls = function ($picker, $select) {
            var selectedValue = $select.val();
            var isLast = selectedValue == '0';
            var isCurrent = selectedValue == '1';
            var isDateRange = selectedValue == '2';
            var isPrevious = selectedValue == '4';
            var isNext = selectedValue == '8';
            var isUpcoming = selectedValue == '16';
            $picker.find('.js-number').toggle(isLast || isPrevious || isNext || isUpcoming);
            $picker.find('.js-time-units-singular').toggle(isCurrent);
            $picker.find('.js-time-units-plural').toggle(isLast || isPrevious || isNext || isUpcoming);
            $picker.find('.js-time-units-date-range').toggle(isDateRange);
            var $pickerContainer = $picker.closest('.js-slidingdaterange-container');
            if (isLast || isPrevious || isNext || isUpcoming || isCurrent) {
                $pickerContainer.find('.js-slidingdaterange-info').css("display", "inline");
            } else {
                $pickerContainer.find('.js-slidingdaterange-info').hide();
            }
        },
        updateDateRangeInfo = function ($picker) {
            var timeUnitType = 0;
            if ($picker.find('.js-time-units-singular').is(':visible')) {
                timeUnitType = $picker.find('.js-time-units-singular').val();
            } else {
                timeUnitType = $picker.find('.js-time-units-plural').val();
            }

            var numberOf = $picker.find('.js-number').val();

            var $pickerContainer = $picker.closest('.js-slidingdaterange-container');

            var dateRangeString = '';
            var $select = $picker.find('.js-slidingdaterange-select');
            var selectedValue = $select.val();
            if (selectedValue == '2') {
                var startPicker = $pickerContainer.find('.js-lower');
                var startDate = startPicker.find('.form-control').val();

                var endPicker = $pickerContainer.find('.js-upper');
                var endDate = endPicker.find('.form-control').val();

                if (startDate != '' && endDate != '') {
                    dateRangeString = '&startDate=' + startDate + '&endDate=' + endDate;
                }
            }

            if (selectedValue != '2' || dateRangeString != '') {
                var getDateRangeUrl = Rock.settings.get('baseUrl') + 'api/Utility/CalculateSlidingDateRange?slidingDateRangeType=' + $select.val() + '&timeUnitType=' + timeUnitType + '&number=' + numberOf + dateRangeString;
                $.get(getDateRangeUrl, function (r) {
                    $pickerContainer.find('.js-slidingdaterange-info').text(r);
                });

                var getTextValueUrl = Rock.settings.get('baseUrl') + 'api/Utility/GetSlidingDateRangeTextValue?slidingDateRangeType=' + $select.val() + '&timeUnitType=' + timeUnitType + '&number=' + numberOf + dateRangeString;
                $.get(getTextValueUrl, function (r) {
                    $pickerContainer.find('.js-slidingdaterange-text-value').val(r).change();
                });
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.tagList = (function () {
        var TagList = function (options) {
                this.controlId = options.controlId;
                this.entityTypeId = options.entityTypeId;
                this.currentPersonId = options.currentPersonId;
                this.entityGuid = options.entityGuid;
                this.entityQualifierColumn = options.entityQualifierColumn;
                this.entityQualifierValue = options.entityQualifierValue;
                this.preventTagCreation = options.preventTagCreation;
                this.delaySave = options.delaySave;
                this.categoryGuid = options.categoryGuid;
                this.includeInactive = options.includeInactive;
            },
            exports;

        TagList.prototype.verifyTag = function (tagName) {
            // Since `verifyTag` is being called by the jQuery lib, `this`
            // is the current TagList DOM element. Get the HTML ID and fetch
            // from the cache.
            var tagList = exports.tagLists[$(this).attr('id')];
            if (! /^((?!<)(?!>)(?!%)(?!&).)*$/.test(tagName)) {
                Rock.dialogs.alert("Invalid characters have been entered for the tag name. Angle brackets, percent, and ampersand are not allowed.");
                $('#' + tagList.controlId).removeTag(tagName);
                return;
            }

            var restUrl = Rock.settings.get('baseUrl') + 'api/tags';
            restUrl += '?entityTypeId=' + tagList.entityTypeId;
            restUrl += '&ownerId=' + tagList.currentPersonId;
            restUrl += '&name=' + encodeURIComponent(tagName);
            restUrl += '&includeInactive' + tagList.includeInactive.toString();

            if (tagList.entityQualifierColumn) {
                restUrl += '&entityQualifier=' + tagList.entityQualifierColumn;
            }

            if (tagList.entityQualifierValue) {
                restUrl += '&entityQualifierValue=' + tagList.entityQualifierValue;
            }

            if (tagList.categoryGuid && tagList.categoryGuid != '') {
                restUrl += '&categoryGuid=' + tagList.categoryGuid;
            }

            $.ajax({
                url: restUrl,
                statusCode: {
                    404: function () {
                        if (tagList.preventTagCreation) {
                            $('#' + tagList.controlId).removeTag(tagName);
                        }
                        else {
                            Rock.dialogs.confirm('A tag called "' + $('<div/>').text(tagName).html() + '" does not exist. Do you want to create a new personal tag?', function (result) {
                                if (result) {
                                    tagList.addTag(tagName);
                                } else {
                                    $('#' + tagList.controlId).removeTag(tagName);
                                }
                            });
                        }
                    },
                    403: function () {
                        Rock.dialogs.alert("Invalid characters have been entered for the tag name. Angle brackets, percent, and ampersand are not allowed.");
                        $('#' + tagList.controlId).removeTag(tagName);
                    },
                    200: function () {
                        tagList.addTag(tagName);
                    }
                }
            });
        };

        TagList.prototype.addTag = function (tagName) {
            // `addTag` is invoked by `verifyTag` on an instance of a `TagList` object.
            // `this` is the current TagList instance in scope.

            var tagList = this,
                restUrl = Rock.settings.get('baseUrl') + 'api/taggeditems'
            restUrl += '?entityTypeId=' + tagList.entityTypeId;
            restUrl += '&ownerId=' + tagList.currentPersonId;
            restUrl += '&entityGuid=' + tagList.entityGuid;
            restUrl += '&name=' + encodeURIComponent(tagName);
            restUrl += '&includeInactive' + tagList.includeInactive.toString();

            if (tagList.categoryGuid && tagList.categoryGuid != '') {
                restUrl += '&categoryGuid=' + this.categoryGuid;
            }

            // only attempt to add tag if an entityGuid exists
            if (!tagList.delaySave) {
                if (tagList.entityQualifierColumn) {
                    restUrl += '/' + tagList.entityQualifierColumn;
                }

                if (tagList.entityQualifierValue) {
                    restUrl += '/' + tagList.entityQualifierValue;
                }

                $.ajax({
                    type: 'POST',
                    url: restUrl,
                    error: function (xhr, status, error) {
                        console.log('AddTag() status: ' + status + ' [' + error + ']: ' + xhr.responseText);
                    }
                });
            }
        };

        TagList.prototype.removeTag = function (tagName) {

            // Since `removeTag` is being called by the jQuery lib, `this`
            // is the current TagList DOM element. Get the HTML ID and fetch
            // from the cache.
            var tagList = exports.tagLists[$(this).attr('id')],
                restUrl = Rock.settings.get('baseUrl') + 'api/taggeditems';

            // only attempt to remove tag if an entityGuid exists
            if (!tagList.delaySave) {

                restUrl += '?entityTypeId=' + tagList.entityTypeId;
                restUrl += '&ownerId=' + tagList.currentPersonId;
                restUrl += '&entityGuid=' + tagList.entityGuid;
                restUrl += '&name=' + encodeURIComponent(tagName);
                restUrl += '&includeInactive' + tagList.includeInactive.toString();

                if (tagList.entityQualifierColumn) {
                    restUrl += '&entityQualifier=' + tagList.entityQualifierColumn;
                }

                if (tagList.entityQualifierValue) {
                    restUrl += '&entityQualifierValue=' + tagList.entityQualifierValue;
                }

                if (tagList.categoryGuid && tagList.categoryGuid != '') {
                    restUrl += '&categoryGuid=' + tagList.categoryGuid;
                }

                $.ajax({
                    type: 'DELETE',
                    url: restUrl,
                    context: {
                        tagName: tagName,
                        tagsInput: this
                    },
                    error: function (xhr, status, error) {
                        if (xhr && xhr.status == 404) {
                            // already deleted
                            return;
                        }

                        Rock.dialogs.alert('Unable to remove tag: ' + error);

                        // put the tag back in (in alpha order, case-insensitive)
                        var tagsCommaList = $(this.tagsInput).val() + ',' + this.tagName

                        tagsCommaList = tagsCommaList.split(',').sort(function (a, b) {
                            return a.toLowerCase().localeCompare(b.toLowerCase());
                        }).join(',');

                        $(this.tagsInput).importTags(tagsCommaList);
                        return false;
                    }
                });
            }

        };

        TagList.prototype.initialize = function () {

            var restUrl = Rock.settings.get('baseUrl') + 'api/Tags/AvailableNames';
            restUrl += '?entityTypeId=' + this.entityTypeId;
            restUrl += '&ownerId=' + this.currentPersonId;
            restUrl += '&entityGuid=' + this.entityGuid;
            restUrl += '&includeInactive' + this.includeInactive.toString();

            if (this.entityQualifierColumn) {
                restUrl += '&entityQualifier=' + this.entityQualifierColumn;
            }
            if (this.entityQualifierValue) {
                restUrl += '&entityQualifierValue=' + this.entityQualifierValue;
            }
            if (this.categoryGuid && this.categoryGuid != '') {
                restUrl += '&categoryGuid=' + this.categoryGuid;
            }

            $('ul.ui-autocomplete').css({ 'width': '300px' });

            $('#' + this.controlId).tagsInput({
                autocomplete_url: function (request, response) {
                    $.ajax({
                        url: restUrl + '&name=' + request.term,
                        dataType: 'json',
                        success: function (data, status, xhr) {
                            response($.map(data, function (item) {
                                return {
                                    value: item.Name,
                                    class: !item.OwnerId ? 'system' : 'personal'
                                };
                            }));
                        },
                        error: function (xhr, status, error) {
                            console.log('availablenames status: ' + status + ' [' + error + ']: ' + xhr.reponseText);
                        }
                    });
                },
                autoCompleteAppendTo: 'div.tag-wrap',
                autoCompleteMessages: {
                    noResults: function () {},
                    results: function () {}
                },
                height: 'auto',
                width: '100%',
                interactive: true,
                defaultText: 'add tag',
                removeWithBackspace: false,
                onAddTag: this.verifyTag,
                onRemoveTag: this.removeTag
            });
        };

        exports = {
            tagLists: {},
            initialize: function (options) {
                if (!options.controlId) throw 'controlId must be set.';
                if (!options.entityTypeId) throw 'entityTypeId must be set.';
                if (!options.currentPersonId) throw 'currentPersonId must be set';
                if (!options.entityGuid) throw 'entityGuid must be set';

                var tagList = new TagList(options);
                tagList.options = options;
                exports.tagLists[options.controlId] = tagList;
                tagList.initialize();
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.taskActivityProgressReporter = (function () {
        const TaskActivityProgressReporter = function (options) {
            if (!Rock.RealTime) {
                throw new Error("realtime.js must be included first.");
            }

            if (!options.controlId) {
                throw new Error("controlId is required.");
            }

            this.controlId = options.controlId;
            this.connectionId = undefined;

            this.connect();
        }

        TaskActivityProgressReporter.prototype.getTaskId = function () {
            const control = document.getElementById(this.controlId);

            if (!control) {
                return null;
            }

            return control.getAttribute("data-task-id");
        };

        TaskActivityProgressReporter.prototype.connect = async function () {
            this.topic = await Rock.RealTime.getTopic("Rock.RealTime.Topics.TaskActivityProgressTopic");

            this.topic.on("taskStarted", this.onTaskStarted.bind(this));
            this.topic.on("taskCompleted", this.onTaskCompleted.bind(this));
            this.topic.on("updateTaskProgress", this.onUpdateTaskProgress.bind(this));

            const $connectionId = $(`#${this.controlId} [id$='_hfConnectionId']`);

            $connectionId.val(this.topic.connectionId || "");
        };

        TaskActivityProgressReporter.prototype.onTaskStarted = function (status) {
            // Intentionally left blank for now.
        };

        TaskActivityProgressReporter.prototype.onTaskCompleted = function (status) {
            const taskId = this.getTaskId();

            if (status.taskId !== taskId) {
                return;
            }

            const $preparing = $(`#${this.controlId} .js-preparing`);
            const $progress = $(`#${this.controlId} .js-progress-div`);
            const $results = $(`#${this.controlId} .js-results`);

            $results.removeClass("alert-danger").removeClass("alert-warning").removeClass("alert-success");

            if (status.errors && status.errors.length > 0) {
                $results.addClass("alert-danger");
            }
            else if (status.warnings && status.warnings.length > 0) {
                $results.addClass("alert-warning");
            }
            else {
                $results.addClass("alert-success");
            }

            $results.text(status.message).slideDown();
            $preparing.slideUp();
            $progress.slideUp();
        };

        TaskActivityProgressReporter.prototype.onUpdateTaskProgress = function (progress) {
            const taskId = this.getTaskId();

            if (progress.taskId !== taskId) {
                return;
            }

            const $preparing = $(`#${this.controlId} .js-preparing`);
            const $progress = $(`#${this.controlId} .js-progress-div`);
            const $bar = $(`#${this.controlId} .js-progress-bar`);

            $bar.prop("aria-valuenow", progress.completionPercentage);
            $bar.prop("aria-valuemax", "100");
            $bar.css("width", `${progress.completionPercentage}%`);

            if (progress.message) {
                $bar.text(progress.message);
            }
            else {
                $bar.text(`${progress.completionPercentage}%`);
            }

            $progress.slideDown();
            $preparing.slideUp();
        };

        const reporters = {};

        const exports = {
            initialize(options) {
                if (!options.controlId) {
                    throw new Error("id is required.");
                }

                if (reporters[options.controlId]) {
                    return;
                }

                reporters[options.controlId] = new TaskActivityProgressReporter(options);
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.timePicker = (function () {
        var exports = {
            initialize: function (options) {
                if (!options.id) {
                    throw 'id is required';
                }

                var $tp = $('#' + options.id);

                // bootstrap-timepicker requires that the parent div have bootstrap-timepicker, input-append classes
                $tp.closest('div').addClass('bootstrap-timepicker').addClass('input-append');

                // uses https://github.com/jdewit/bootstrap-timepicker
                $tp.timepicker({
                    defaultTime: false
                });

                $tp.closest('.js-timepicker-input').find('.js-timepicker-clear').on('click', function () {
                    $tp.timepicker('clear');
                });
            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.toggleButton = (function () {
        var exports = {
            initialize: function (options) {
                if (!options.id) {
                    throw 'id is required';
                }

                // uses pattern from http://www.bootply.com/92189

                $('#' + options.id + ' .btn-toggle').on('click', function (e) {

                    e.stopImmediatePropagation();

                    $(this).find('.btn').toggleClass('active');

                    if (options.activeButtonCssClass && $(this).find('.' + options.activeButtonCssClass).length > 0) {
                        $(this).find('.btn').toggleClass(options.activeButtonCssClass);
                    }

                    if (options.onButtonCssClass) {
                        $(this).find('.js-toggle-on').toggleClass(options.onButtonCssClass);
                    }

                    if (options.offButtonCssClass) {
                        $(this).find('.js-toggle-off').toggleClass(options.offButtonCssClass);
                    }

                    $(this).parent().find('.js-toggle-checked').val($(this).find('.js-toggle-on').hasClass('active'));

                });

            }
        };

        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.universalItemSearchPicker = (function () {
        var UniversalItemSearchPicker = function (options) {
            this.controlId = options.controlId;
            this.restUrl = options.restUrl;
            this.areDetailsAlwaysVisible = options.areDetailsAlwaysVisible;
            this.iScroll = null;
            this.$pickerControl = $('#' + this.controlId);
            this.$pickerScrollContainer = this.$pickerControl.find('.js-universalitemsearchpicker-scroll-container');
        };

        UniversalItemSearchPicker.prototype.initializeEventHandlers = function () {
            var controlId = this.controlId;
            var restUrl = this.restUrl;
            var areDetailsAlwaysVisible = this.areDetailsAlwaysVisible;

            var $pickerControl = this.$pickerControl;
            var $searchFields = $pickerControl.find('input.js-universalitemsearchpicker-search-field');
            var $searchFieldName = $pickerControl.find('input.js-universalitemsearchpicker-search-name');
            var $searchFieldIncludeInactive = $pickerControl.find("input.js-include-inactive");
            var $searchResults = $pickerControl.find('.js-universalitemsearchpicker-searchresults');
            var $pickerToggle = $pickerControl.find('.js-universalitemsearchpicker-toggle');
            var $pickerMenu = $pickerControl.find('.js-universalitemsearchpicker-menu');
            var $pickerSelect = $pickerControl.find('.js-universalitemsearchpicker-select');
            var $pickerSelectNone = $pickerControl.find('.js-picker-select-none');
            var $pickerItemValue = $pickerControl.find('.js-item-value');
            var $pickerItemName = $pickerControl.find('.js-item-name');
            var $pickerCancel = $pickerControl.find('.js-universalitemsearchpicker-cancel');
            var promise = null;
            var lastSelectedItemValue = null;

            var autoCompletes = $searchFields.autocomplete({
                source: function (request, response) {

                    var search = {
                        value: $searchFieldName.val()
                    };

                    // make sure that at least one of the search fields has 3 chars in it
                    if (search.value.length < 3) {
                        return;
                    }

                    // abort any searches that haven't returned yet, so that we don't get a pile of results in random order
                    if (promise && promise.state() === 'pending') {
                        promise.abort();
                    }

                    var searchBag = {
                        value: search.value,
                        isInactiveIncluded: $searchFieldIncludeInactive && $searchFieldIncludeInactive.is(":checked") == true
                    };

                    // set the timeout to 20 seconds, just in case it takes a long time to search
                    promise = $.ajax({
                        url: restUrl,
                        method: "POST",
                        data: JSON.stringify(searchBag),
                        contentType: "application/json",
                        timeout: 20000,
                        dataType: 'json'
                    });

                    // Display a wait indicator to show that the search is now running.
                    if ($('.js-searching-notification').length == 0) {
                        $searchResults.prepend('<i class="fa fa-refresh fa-spin margin-l-md js-searching-notification" style="display: none; opacity: .4;"></i>');
                    }
                    $('.js-searching-notification').fadeIn(800);

                    promise.done(function (data) {
                        $searchResults.html('');

                        response(data);

                        exports.universalItemSearchPickers[controlId].updateScrollbar();
                    });

                    promise.fail(function (xhr, status, error) {
                        console.log(status + ' [' + error + ']: ' + xhr.responseText);
                        var errorCode = xhr.status;
                        if (errorCode == 401) {
                            $searchResults.html("<li class='text-danger'>Sorry, you're not authorized to search.</li>");
                        }

                        $('.js-searching-notification').remove();
                    });
                },
                // Set minLength to 0, but check that at least one field has 3 chars before fetching from REST.
                // To minimize load on the server, don't trigger the search until a reasonable delay after the last keypress.
                minLength: 0,
                delay: 750,
                html: true,
                appendTo: $searchResults,
                pickerControlId: controlId,
                messages: {
                    noResults: function () { },
                    results: function () { }
                }
            });

            var autoCompleteCustomRenderItem = function ($ul, item) {
                // override jQueryUI autocomplete's _renderItem so that we can do HTML for the ListItems
                // derived from http://github.com/scottgonzalez/jquery-ui-extensions

                var $div = $('<div/>').attr('class', 'radio');

                var $labelText = $('<span/>')
                    .addClass('label-text d-flex gap')
                    .append($('<span/>').addClass('flex-grow-1').text(item.title));

                if (item.labels) {
                    var $labels = $('<span/>').addClass('d-flex gap');

                    for (let labelIndex = 0; labelIndex < item.labels.length; labelIndex++) {
                        $('<span/>')
                            .text(item.labels[labelIndex].text)
                            .addClass('label label-' + item.labels[labelIndex].value)
                            .appendTo($labels);
                    }

                    $labelText.append($labels);
                }

                var $label = $('<label/>')
                    .append($labelText)
                    .prependTo($div);

                $('<input type="radio" name="item-id" />')
                    .attr('value', item.Id)
                    .prependTo($label);

                var $li = $('<li/>')
                    .addClass('picker-select-item js-picker-select-item')
                    .css('list-style', 'none')
                    .attr('data-item-value', item.value)
                    .attr('data-item-name', item.title)
                    .html($div);

                var $resultSection = $(this.options.appendTo);

                var $itemDetailsDiv = $('<div/>')
                    .addClass('picker-select-item-details js-picker-select-item-details clearfix');

                if (item.description) {
                    var $descriptionDiv = $('<div/>');

                    $descriptionDiv.addClass("picker-select-item-description mb-2");
                    $descriptionDiv.text(item.description);

                    $itemDetailsDiv.append($descriptionDiv);
                }

                if (item.details) {
                    for (let detailIndex = 0; detailIndex < item.details.length; detailIndex++) {
                        var detail = item.details[detailIndex];
                        var $detailDl = $('<dl/>').addClass("d-flex");
                        $detailDl.append($("<dt/>").addClass("mr-2 text-nowrap").text(detail.value));
                        $detailDl.append($("<dd/>").text(detail.text));

                        $itemDetailsDiv.append($detailDl);
                    }
                }

                if (!areDetailsAlwaysVisible) {
                    $itemDetailsDiv.hide();
                }

                $itemDetailsDiv.appendTo($li);

                if (item.isInactive) {
                    $li.addClass('is-inactive');
                }

                return $resultSection.append($li);
            }

            // each search field has its own autocomplete object, so we'll need override with our custom _renderItem to each
            $.each(autoCompletes, function (a, b, c) {
                var autoComplete = $(autoCompletes[a]).data('ui-autocomplete');

                // Debugging: override close to prevent it canceling when loosing focus when debugging
                // autoComplete.close = function () { };

                autoComplete._renderItem = autoCompleteCustomRenderItem;
            });

            $pickerToggle.on('click', function (e) {
                e.preventDefault();
                $(this).toggleClass("active");
                $pickerMenu.toggle(0, function () {
                    exports.universalItemSearchPickers[controlId].updateScrollbar();
                    $searchFieldName.trigger('focus');
                });
            });

            $pickerControl.on('click', '.js-picker-select-item', function (e) {
                if (e.type == 'click' && $(e.target).is(':input') == false) {
                    // only process the click event if it has bubbled up to the input tag
                    return;
                }

                e.stopPropagation();

                var $selectedItem = $(this).closest('.js-picker-select-item');
                var $itemDetails = $selectedItem.find('.js-picker-select-item-details');

                var selectedItemValue = $selectedItem.attr('data-item-value');

                if ($itemDetails.is(':visible')) {
                    if (selectedItemValue == lastSelectedItemValue && e.type == 'click') {
                        // if they are clicking the same item twice in a row
                        // (and the details are done expanding), assume that's
                        // the one they want to pick
                        $pickerSelect.trigger('click');
                    } else {
                        // if it is already visible but isn't the same one twice, just leave it open
                    }
                }

                lastSelectedItemValue = selectedItemValue;

                showItemDetails($selectedItem.find('.picker-select-item-details:hidden'));
            });

            var showItemDetails = function ($itemDetails) {
                if ($itemDetails.length) {
                    $itemDetails.slideDown(function () {
                        exports.universalItemSearchPickers[controlId].updateScrollbar();
                    });
                }
            }

            $pickerControl.on('mouseenter',
                function () {
                    // only show the X if there is something picked
                    if (($pickerItemValue.val() || '') !== '') {
                        $pickerSelectNone.addClass('show-hover');
                    }
                });

            $pickerCancel.on('click', function () {
                clearSearchFields();
                $pickerMenu.slideUp(function () {
                    exports.universalItemSearchPickers[controlId].updateScrollbar();
                });
            });

            $pickerSelectNone.on('click', function (e) {
                // prevent the click from bubbling up to the pickerControl click event
                e.preventDefault();
                e.stopPropagation();

                $pickerItemValue.val('');
                $pickerItemName.val('');
                // run onclick event from the button
                $(this).trigger('onclick');
            });

            // disable the enter key : this will prevent the enter key from clearing the loaded search query.
            $pickerControl.on('keypress', function (e) {
                if (e.which == 13) {
                    return false;
                }
            });

            var setSelectedItem = function (selectedValue, selectedText) {
                var selectedItemLabel = $pickerControl.find('.js-universalitemsearchpicker-selecteditem-label');

                $pickerItemValue.val(selectedValue);
                $pickerItemName.val(selectedText);

                selectedItemLabel.val(selectedValue);
                selectedItemLabel.text(selectedText);

                $pickerMenu.slideUp();
            }

            var clearSearchFields = function () {
                $searchFieldName.val('');
            }

            $pickerSelect.on('click', function () {
                var $selectedItem = $pickerControl.find('input:checked').closest('.js-picker-select-item');
                var selectedItemValue = $selectedItem.attr('data-item-value');
                var selectedText = $selectedItem.attr('data-item-name');

                setSelectedItem(selectedItemValue, selectedText);
                clearSearchFields();

                // Fire the postBack for the Select button.
                var postBackUrl = $pickerSelect.prop('href');
                if (postBackUrl) {
                    window.location = postBackUrl;
                }
            });
        };

        UniversalItemSearchPicker.prototype.updateScrollbar = function () {
            var self = this;

            // first, update this control's scrollbar, then the modal's

            if (self.$pickerScrollContainer.is(':visible')) {
                if (self.iScroll) {
                    self.iScroll.refresh();
                }
            }

            // update the outer modal scrollbar
            Rock.dialogs.updateModalScrollBar(this.controlId);
        }

        UniversalItemSearchPicker.prototype.initialize = function () {
            this.iScroll = new IScroll($('.viewport', this.$pickerControl)[0], {
                mouseWheel: true,
                indicators: {
                    el: $('.track', this.$pickerScrollContainer)[0],
                    interactive: true,
                    resize: false,
                    listenY: true,
                    listenX: false,
                },
                click: false,
                preventDefaultException: { tagName: /.*/ }
            });

            this.initializeEventHandlers();
        };

        var exports = {
            universalItemSearchPickers: {},
            findControl: function (controlId) {
                return exports.universalItemSearchPickers[controlId];
            },
            initialize: function (options) {
                if (!options.controlId) throw '`controlId` is required.';
                if (!options.restUrl) throw '`restUrl` is required.';

                var picker = new UniversalItemSearchPicker(options);
                exports.universalItemSearchPickers[options.controlId] = picker;
                picker.initialize();
            }
        };
        return exports;
    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.util = (function () {
        var exports = {
            googleMapsIsLoaded: function () {
                // trigger googleMapsIsLoaded so that controls (like the geoPicker) know about it
                $(window).trigger('googleMapsIsLoaded');
            },
            loadGoogleMapsApi: function (apiSrc) {
                // ensure that js for googleMapsApi is added to page
                if (!$('#googleMapsApi').length) {
                    // by default, jquery adds a cache-busting parameter on dynamically added script tags. set the ajaxSetup cache:true to prevent this
                    $.ajaxSetup({ cache: true });
                    var src = apiSrc + '&callback=Rock.controls.util.googleMapsIsLoaded';
                    $('head').prepend("<script id='googleMapsApi' src='" + src + "' />");
                } else if (typeof google == 'object' && typeof google.maps == 'object') {
                    this.googleMapsIsLoaded();
                }
            },
          // parses the value as a float and adds a 'px' suffix if successful. If the value can't be parsed, this will return an empty string
          // note: this uses javascript's parseFloat which will convert the the numeric portion value to a float as long as it starts with a numeric value
            getValueAsPixels: function (a)
            {
              var floatValue = parseFloat(a);
              if (floatValue) {
                return floatValue + 'px';
              }
              else {
                return '';
              }
            },
            getScrollbarWidth: function () {
                // thx d.walsh
                var scrollDiv = document.createElement('div');
                scrollDiv.className = 'modal-scrollbar-measure';
                document.body.appendChild(scrollDiv);
                var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
                document.body.removeChild(scrollDiv);
                return scrollbarWidth;
            } // Static
        };

        return exports;

    }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};
    const DefaultComparisonTypes = [
        { Value: 1, Text: 'Equal To' },
        { Value: 2, Text: 'Not Equal To' },
        { Value: 4, Text: 'Starts With' },
        { Value: 8, Text: 'Contains' },
        { Value: 16, Text: 'Does Not Contain' },
        { Value: 32, Text: 'Is Blank' },
        { Value: 64, Text: 'Is Not Blank' },
        { Value: 2048, Text: 'Ends With' }
    ];
    const GroupAll = 1;
    const GroupAny = 2;
    const GroupAllFalse = 3;
    const GroupAnyFalse = 4;

    Rock.controls.valueFilter = (function () {
        //
        // Setup the UI so it reflects the initial data configured on the filter.
        //
        function loadInitialData($filter) {
            var data;

            //
            // Try to parse the initial data.
            //
            try {
                data = JSON.parse($filter.find('input[type="hidden"]').val());
            }
            catch (ex) {
                data = null;
            }

            //
            // Initialize a new filter if we have no current settings.
            //
            if (data === null) {
                data = { ExpressionType: GroupAny, Filters: [] };
                $filter.find('input[type="hidden"]').val(JSON.stringify(data));
            }

            //
            // Grab the objects we need from the filter control.
            //
            var $filterTypeAll = $filter.data('filterTypeAll');
            var $filterTypeAny = $filter.data('filterTypeAny');
            var options = $filter.data('options');

            //
            // Set the initial state of the Any/All toggle buttons.
            //
            if (data.ExpressionType === GroupAll) {
                $filterTypeAll.addClass(options.btnToggleOnClass).removeClass(options.btnToggleOffClass);
                $filterTypeAny.addClass(options.btnToggleOffClass).removeClass(options.btnToggleOnClass);
            }
            else {
                $filterTypeAny.addClass(options.btnToggleOnClass).removeClass(options.btnToggleOffClass);
                $filterTypeAll.addClass(options.btnToggleOffClass).removeClass(options.btnToggleOnClass);
            }

            //
            // Add the initial field rows.
            //
            for (var index in data.Filters) {
                var expression = data.Filters[index];

                addFilterRow($filter, expression.Value, expression.Comparison);
            }

            if (data.Filters.length === 0) {
                addFilterRow($filter, '', 8);
                updateData($filter);
            }
        }

        //
        // Setup the initial event handlers for the controls in filter.
        //
        function addControlEvents($filter) {
            //
            // Load all the objects we need from the filter.
            //
            var $filterTypeAll = $filter.data('filterTypeAll');
            var $filterTypeAny = $filter.data('filterTypeAny');
            var options = $filter.data('options');

            //
            // Add a click handler on the "Any" toggle button that will turn off the
            // "All" option and turn on the "Any" option instead.
            //
            $filterTypeAny.on('click', function (e) {
                e.preventDefault();

                $filterTypeAny.addClass(options.btnToggleOnClass).removeClass(options.btnToggleOffClass);
                $filterTypeAll.addClass(options.btnToggleOffClass).removeClass(options.btnToggleOnClass);

                updateData($filter);
            });

            //
            // Add a click handler on the "All" toggle button that will turn off the
            // "Any" option and turn on the "All" option instead.
            //
            $filterTypeAll.on('click', function (e) {
                e.preventDefault();

                $filterTypeAll.addClass(options.btnToggleOnClass).removeClass(options.btnToggleOffClass);
                $filterTypeAny.addClass(options.btnToggleOffClass).removeClass(options.btnToggleOnClass);

                updateData($filter);
            });

            //
            // Add a click handler to the "Add" button that will add a new filter row.
            //
            $filter.data('addButton').on('click', function (e) {
                e.preventDefault();
                addFilterRow($filter, '', 8);
                updateData($filter);
            });
        }

        //
        // Add a new field row.
        //
        function addFilterRow($filter, value, type) {
            var options = $filter.data('options');
            var $comparisonButton = null;

            //
            // Create the row container as an input-group so things look pretty.
            //
            var $row = $('<div class="input-group margin-b-sm"></div>');

            //
            // Create the text field the user can type into.
            //
            var $text = $('<input type="text" class="form-control">').appendTo($row);

            //
            // Create the drop down that indicates what type of filter row this is.
            //
            var $inputGroupBtns = $('<span class="input-group-btn" />').appendTo($row);
            var $fieldTypeBtn = $('<button type="button" class="btn btn-default dropdown-toggle filter-dropdown-toggle" data-toggle="dropdown"><span /> <span class="caret" /></button>')
                .appendTo($inputGroupBtns);
            var $fieldTypeOptions = $('<ul class="dropdown-menu dropdown-menu-right"></ul>')
                .appendTo($inputGroupBtns);
            for (var i in options.comparisonTypes) {
                var $btn = $('<li><a href="#" data-value="' + options.comparisonTypes[i].Value + '">' + options.comparisonTypes[i].Text + '</a></li>').appendTo($fieldTypeOptions);
                if (type === options.comparisonTypes[i].Value) {
                    $comparisonButton = $btn;
                }
            }
            $comparisonButton = $comparisonButton || $fieldTypeOptions.find('li:first-child');

            //
            // Create the delete button.
            //
            $('<button type="button" class="btn btn-danger btn-square"><i class="fa fa-times"></i></button>')
                .appendTo($inputGroupBtns);

            //
            // Set default values.
            //
            $text.val(value);
            changeFilterRowType($filter, $comparisonButton, true);

            //
            // Setup event handlers.
            //
            addFilterRowEvents($filter, $row);

            //
            // Append this new filter row to the DOM.
            //
            $filter.data('rowContainer').append($row);
        }

        //
        // Setup the event handlers for a newly created filter row.
        //
        function addFilterRowEvents($filter, $field) {
            //
            // When the text has changed, update the stored data.
            //
            $field.find('input[type="text"]').on('change', function () {
                updateData($filter);
            });

            //
            // When the user clicks on one of the filter type drop down options, update
            // the filter row type.
            //
            $field.find('ul > li > a').on('click', function (e) {
                e.preventDefault();
                changeFilterRowType($filter, $(this));
            });

            //
            // Remove a single row from the filter.
            //
            $field.find('button.btn-danger').on('click', function (e) {
                e.preventDefault();
                $(this).closest('.input-group').remove();
                updateData($filter);
            });
        }

        //
        // Update the filter row type based on what the user clicked.
        //
        function changeFilterRowType($filter, $btn, skipUpdate) {
            var $btnGroup = $btn.closest('.input-group-btn');
            $btnGroup.find('.dropdown-toggle span:first-child').text($btn.text());

            if ($btn.data('value') === 32 || $btn.data('value') === 64) {
                $filter.find('input[type="text"]').val('').prop('disabled', true);
            }
            else {
                $filter.find('input[type="text"]').prop('disabled', false);
            }

            if (skipUpdate !== true) {
                updateData($filter);
            }
        }

        //
        // Update the hidden field to match what is in the UI.
        //
        function updateData($filter) {
            var options = $filter.data('options');
            var filters = [];

            //
            // Walk each row of the filter and build up the filters collection.
            //
            $filter.data('rowContainer').find('.input-group').each(function (index, element) {
                var $field = $(element);
                var text = $field.find('input[type="text"]').val();
                var typeText = $field.find('button.dropdown-toggle span:first-child').text();
                var type = $field.find('ul li a').filter(function () { return $(this).text() === typeText; }).data('value');

                filters.push({ Value: text, Comparison: type });
            });

            //
            // If there is only one filter, it is a Contains filter and has no value, then get rid of it.
            //
            if (filters.length === 1 && filters[0].Value === '' && filters[0].Comparison === 8) {
                filters = [];
            }

            //
            // Build the final filtered text value.
            //
            var data = {
                ExpressionType: $filter.data('filterTypeAny').hasClass(options.btnToggleOnClass) ? GroupAny : GroupAll,
                Filters: filters
            };

            $filter.find('input[type="hidden"]').val(JSON.stringify(data));
        }

        var exports = {
            initialize: function (options) {
                if (!options.controlId) {
                    throw 'controlId is required';
                }

                //
                // Make sure we have minimum default options
                //
                options = $.extend({
                    btnToggleOnClass: 'btn-info',
                    btnToggleOffClass: 'btn-default',
                    required: true,
                    requiredMessage: 'The field is required',
                    hideFilterMode: false,
                    comparisonTypes: DefaultComparisonTypes,
                    defaultComparison: 8
                }, options);

                //
                // Setup the comparsionTypesByValue object.
                //
                options.comparisonTypeByValue = {};
                for (var i = 0; i < options.comparisonTypes.length; i++) {
                    options.comparisonTypeByValue[options.comparisonTypes[i].Value] = options.comparisonTypes[i].Text;
                }

                //
                // Find the filter control placeholder.
                //
                var $filter = $('#' + options.controlId);

                //
                // Setup the "Any/All" buttons for the filter.
                //
                var $filterTypeContainer = $('<div class="text-right" />').appendTo($filter);
                var $filterTypeGroup = $('<div class="btn-group" role="group" />').appendTo($filterTypeContainer);
                var $filterTypeAny = $('<button type="button" class="btn btn-default btn-xs">Any</button>').appendTo($filterTypeGroup);
                var $filterTypeAll = $('<button type="button" class="btn btn-default btn-xs">All</button>').appendTo($filterTypeGroup);
                if (options.hideFilterMode === true) {
                    $filterTypeContainer.addClass('hidden');
                }

                //
                // Setup the placeholder div that will contain all the filter  rows.
                //
                var $rowContainer = $('<div />').appendTo($filter);

                //
                // Setup the add button.
                //
                var $addButtonContainer = $('<div class="text-right" />').appendTo($filter);
                var $addButton = $('<a href="#" class="btn btn-default btn-square btn-sm"><i class="fa fa-plus"></i></a>').appendTo($addButtonContainer);

                //
                // Store all the objects we need access to later as jQuery data objects
                // on the filter control.
                //
                $filter.data('options', options);
                $filter.data('filterTypeAny', $filterTypeAny);
                $filter.data('filterTypeAll', $filterTypeAll);
                $filter.data('rowContainer', $rowContainer);
                $filter.data('addButton', $addButton);

                //
                // Setup initial control events.
                //
                addControlEvents($filter);

                //
                // Setup UI to reflect initial data.
                //
                loadInitialData($filter);
            },
            clientValidate: function (validator, args) {
                var $filter = $(validator).prev();
                var required = $filter.data('options').required;
                var data = JSON.parse($filter.find('input[type="hidden"]').val());

                var isValid = !required || data.Filters.length > 0;

                if (isValid) {
                    $filter.closest('.form-group').removeClass('has-error');
                    args.IsValid = true;
                }
                else {
                    $filter.closest('.form-group').addClass('has-error');
                    args.IsValid = false;
                    validator.errormessage = $filter.data('options').requiredMessage;
                }
            }
        };

        return exports;
    }());
}(jQuery));
;
; (function () {
  function updateKeyValues(e) {
    var $span = e.closest('span.value-list');
    var newValue = '';

    var valueDelimiters = ['|', ','];
    $span.children('span.value-list-rows').first().children('div.controls-row').each(function (index) {
      var value = $(this).children('.js-value-list-input').first().val();

      valueDelimiters.forEach(function (v, i, a) {
        var re = new RegExp('\\' + v, 'g');
        if (value.indexOf(v) > -1) {
          value = value.replace(re, encodeURIComponent(v));
        }
      });
      newValue += value + '|'
    });

    $span.children('input').first().val(newValue);
  }

  Sys.Application.add_load(function () {

    $('a.value-list-add').on('click', function (e) {
      e.preventDefault();
      var $ValueList = $(this).closest('.value-list');
      var newValuePickerHtml = $ValueList.find('.js-value-list-html').val()
      $ValueList.find('.value-list-rows').append(newValuePickerHtml);
      updateKeyValues($(this));
      Rock.controls.modal.updateSize($(this));
    });

    $(document).on('click', 'a.value-list-remove', function (e) {
      e.preventDefault();
      var $rows = $(this).closest('span.value-list-rows');
      $(this).closest('div.controls-row').remove();
      updateKeyValues($rows);
      Rock.controls.modal.updateSize($(this));
    });

    $(document).on('change', '.js-value-list-input', function (e) {
      updateKeyValues($(this));
    });
  });
})();
;
(function (Sys) {
    'use strict';
    Sys.Application.add_load(function () {
        $('a.warning').on('click', function (e) {
            e.preventDefault();
            $(this).siblings('div.alert-warning').slideToggle(function () {
                Rock.controls.modal.updateSize(this);
            });
            $(this).siblings('div.alert-info').slideUp();
        });
    });
}(Sys));
;
/* NOTE: Requires this on the init of the block

RockPage.AddScriptLink("~/Scripts/d3-cloud/d3.layout.cloud.js");
RockPage.AddScriptLink("~/Scripts/d3-cloud/d3.min.js");

*/

(function ($)
{
  'use strict';
  window.Rock = window.Rock || {};
  Rock.controls = Rock.controls || {};

  Rock.controls.wordcloud = (function ()
  {
    
    var exports = {
      initialize: function (options)
      {
        if (!options.inputTextId) {
          throw 'inputTextId is required';
        }

        if (!options.visId) {
          throw 'visId is required';
        }

        // if the visId element isn't found, jump out
        if (d3.select("#" + options.visId).empty()) {
          return;
        }

        var $inputText = $('#' + options.inputTextId);
        var visId = options.visId;
        var statusId = options.statusId;
        var svgWidth = options.width || 800;
        var svgHeight = options.height || 300;
        var fontName = options.fontName || 'Impact';
        var maxWords = options.maxWords || 255;
        var scaleName = options.scaleName || 'log';
        var spiralName = options.spiralName || 'archimedean';
        var colors = options.colors || ['#0193B9', '#F2C852', '#1DB82B', '#2B515D', '#ED3223'];

        var angleCount = options.angleCount == undefined ? 6 : options.angleCount;
        if (angleCount > 180) {
          angleCount = 180;
        }

        // a good max range is -90 to 90 so that words don't go upside down
        var angleMin = options.angleMin == undefined ? -90 : options.angleMin;
        var angleMax = options.angleMax == undefined ? 90 : options.angleMax;
        var angles = [];
        if (angleCount == 1) {
          // one angle, so choose the midpoint of the angleMin and angleMax
          angles.push((angleMax + angleMin) / 2);
        }
        else if (angleCount == 2) {
          // two angles, so choose the angleMin and angleMax
          angles.push(angleMin);
          angles.push(angleMax);
        }
        else if (angleCount > 2) {
          // three or more, so choose the angleMin and angleMax, and the the remaining ones between
          var angleSize = (angleMax - angleMin) / (angleCount - 1)
          var angle = angleMin;
          while (angle <= angleMax) {
            angles.push(angle);
            angle += angleSize;
          }
        }

        /* from cloud.js */
        var unicodePunctuationRe = "!-#%-*,-/:;?@\\[-\\]_{}\xa1\xa7\xab\xb6\xb7\xbb\xbf\u037e\u0387\u055a-\u055f\u0589\u058a\u05be\u05c0\u05c3\u05c6\u05f3\u05f4\u0609\u060a\u060c\u060d\u061b\u061e\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0830-\u083e\u085e\u0964\u0965\u0970\u0af0\u0df4\u0e4f\u0e5a\u0e5b\u0f04-\u0f12\u0f14\u0f3a-\u0f3d\u0f85\u0fd0-\u0fd4\u0fd9\u0fda\u104a-\u104f\u10fb\u1360-\u1368\u1400\u166d\u166e\u169b\u169c\u16eb-\u16ed\u1735\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u180a\u1944\u1945\u1a1e\u1a1f\u1aa0-\u1aa6\u1aa8-\u1aad\u1b5a-\u1b60\u1bfc-\u1bff\u1c3b-\u1c3f\u1c7e\u1c7f\u1cc0-\u1cc7\u1cd3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205e\u207d\u207e\u208d\u208e\u2329\u232a\u2768-\u2775\u27c5\u27c6\u27e6-\u27ef\u2983-\u2998\u29d8-\u29db\u29fc\u29fd\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00-\u2e2e\u2e30-\u2e3b\u3001-\u3003\u3008-\u3011\u3014-\u301f\u3030\u303d\u30a0\u30fb\ua4fe\ua4ff\ua60d-\ua60f\ua673\ua67e\ua6f2-\ua6f7\ua874-\ua877\ua8ce\ua8cf\ua8f8-\ua8fa\ua92e\ua92f\ua95f\ua9c1-\ua9cd\ua9de\ua9df\uaa5c-\uaa5f\uaade\uaadf\uaaf0\uaaf1\uabeb\ufd3e\ufd3f\ufe10-\ufe19\ufe30-\ufe52\ufe54-\ufe61\ufe63\ufe68\ufe6a\ufe6b\uff01-\uff03\uff05-\uff0a\uff0c-\uff0f\uff1a\uff1b\uff1f\uff20\uff3b-\uff3d\uff3f\uff5b\uff5d\uff5f-\uff65",
            fill = d3.scaleLinear()
            .domain([0, 1, 2, 3, 4, 5, 6, 10, 15, 20, 100])
            .range(d3.schemeCategory20b),
            w = svgWidth,
            h = svgHeight,
            words = [],
            max, scale = 1,
            complete = 0,
            keyword = "",
            tags, fontSize, maxLength = 30,
            fetcher, statusText = d3.select("#" + statusId),
            layout = d3.layout.cloud().timeInterval(10).size([w, h]).fontSize(function (t)
            {
              return fontSize(+t.value)
            }).text(function (t)
            {
              return t.key
            }).on("word", progress).on("end", draw),
            svg = d3.select("#" + visId).append("svg").attr("width", w).attr("height", h),
            background = svg.append("g"),
            vis = svg.append("g").attr("transform", "translate(" + [w >> 1, h >> 1] + ")");

        var stopWords = /^(i|me|my|myself|we|us|our|ours|ourselves|you|your|yours|yourself|yourselves|he|him|his|himself|she|her|hers|herself|it|its|itself|they|them|their|theirs|themselves|what|which|who|whom|whose|this|that|these|those|am|is|are|was|were|be|been|being|have|has|had|having|do|does|did|doing|will|would|should|can|could|ought|i'm|you're|he's|she's|it's|we're|they're|i've|you've|we've|they've|i'd|you'd|he'd|she'd|we'd|they'd|i'll|you'll|he'll|she'll|we'll|they'll|isn't|aren't|wasn't|weren't|hasn't|haven't|hadn't|doesn't|don't|didn't|won't|wouldn't|shan't|shouldn't|can't|cannot|couldn't|mustn't|let's|that's|who's|what's|here's|there's|when's|where's|why's|how's|a|an|the|and|but|if|or|because|as|until|while|of|at|by|for|with|about|against|between|into|through|during|before|after|above|below|to|from|up|upon|down|in|out|on|off|over|under|again|further|then|once|here|there|when|where|why|how|all|any|both|each|few|more|most|other|some|such|no|nor|not|only|own|same|so|than|too|very|say|says|said|shall)$/,
          punctuation = new RegExp("[" + unicodePunctuationRe + "]", "g"),
          wordSeparators = /[ \f\n\r\t\v\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\u3031-\u3035\u309b\u309c\u30a0\u30fc\uff70]+/g,
          discard = /^(@|https?:|\/\/)/,
          htmlTags = /(<[^>]*?>|<script.*?<\/script>|<style.*?<\/style>|<head.*?><\/head>)/g,
          matchTwitter = /^https?:\/\/([^\.]*\.)?twitter\.com/;

        var inputText = $inputText.val();
        if (inputText) {
          parseText(inputText);
        }

        function flatten(t, e)
        {
          if ("string" == typeof t) return t;
          var n = [];
          for (e in t) {
            var a = flatten(t[e], e);
            a && n.push(a)
          }
          return n.join(" ")
        }

        function parseText(t)
        {
          tags = {};
          var e = {},
              n = false//d3.select("#per-line").property("checked");
          t.split(n ? /\n/g : wordSeparators).forEach(function (t)
          {
            discard.test(t) || (n || (t = t.replace(punctuation, "")), stopWords.test(t.toLowerCase()) || (t = t.substr(0, maxLength), e[t.toLowerCase()] = t, tags[t = t.toLowerCase()] = (tags[t] || 0) + 1))
          }), tags = d3.entries(tags).sort(function (t, e)
          {
            return e.value - t.value
          }), tags.forEach(function (t)
          {
            t.key = e[t.key]
          }), generate()
        }

        function generate()
        {
          layout
            .font(fontName)
            .spiral(spiralName)
            .rotate(function ()
            {
              return angles[Math.floor(Math.random() * angles.length)];
            }),
          fontSize = d3.scaleLog().range([10, 100]), tags.length && fontSize.domain([+tags[tags.length - 1].value || 1, +tags[0].value]),
          complete = 0, statusText.style("display", null),
          words = [],
          layout.stop().words(tags.slice(0, max = Math.min(tags.length, +maxWords))).start()
        }

        function progress(t)
        {
          statusText.text(++complete + "/" + max)
        }

        function draw(t, e)
        {
          statusText.style("display", "none"),
          scale = e ? Math.min(w / Math.abs(e[1].x - w / 2), w / Math.abs(e[0].x - w / 2), h / Math.abs(e[1].y - h / 2), h / Math.abs(e[0].y - h / 2)) / 2 : 1, words = t;
          var n = vis.selectAll("text").data(words, function (t)
          {
            return t.text.toLowerCase()
          });
          n.transition().duration(1e3).attr("transform", function (t)
          {
            return "translate(" + [t.x, t.y] + ")rotate(" + t.rotate + ")"
          }).style("font-size", function (t)
          {
            return t.size + "px"
          }),

          n.enter().append("text").attr("text-anchor", "middle").attr("transform", function (t)
          {
            return "translate(" + [t.x, t.y] + ")rotate(" + t.rotate + ")"
          }).style("font-size", "1px").transition().duration(1e3).style("font-size", function (t)
          {
            return t.size + "px"
          }).style("font-family", function (t)
          {
            return t.font
          }).text(function (t)
          {
            return t.text
          }).style("fill", function (t)
          {
              return colors[Math.floor(Math.random() * colors.length)];
          });

          var a = background.append("g").attr("transform", vis.attr("transform")),
              r = a.node();
          n.exit().each(function ()
          {
            r.appendChild(this)
          }), a.transition().duration(1e3).style("opacity", 1e-6).remove(), vis.transition().delay(1e3).duration(750).attr("transform", "translate(" + [w >> 1, h >> 1] + ")scale(" + scale + ")")
        }

      }
    }

    return exports;
  }());
}(jQuery));
;
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};
    Rock.controls = Rock.controls || {};

    Rock.controls.yearPicker = (function () {
        var exports = {
            initialize: function (options) {
                if (!options.id) {
                    throw 'id is required';
                }

                var earliestDate = new Date(1000, 1, 1);

                // uses https://github.com/eternicode/bootstrap-datepicker
                $('#' + options.id).datepicker({
                    format: 'yyyy',
                    assumeNearbyYear: 10,
                    autoclose: true,
                    startView: 2,
                    minViewMode: 2,
                    startDate: earliestDate,
                    zIndexOffset: 1050
                });

            }
        };

        return exports;
    }());
}(jQuery));
;
var loadFunction = function () {
    // handle what should happen when a different compare type is selected
    function updateFilterControls(filterCompareControl) {
        var $fieldCriteriaRow = $(filterCompareControl).closest('.field-criteria');
        var compareValue = $(filterCompareControl).val();
        var isNullCompare = (compareValue == 32 || compareValue == 64);
        var isBetweenCompare = (compareValue == 4096);
        if (isNullCompare) {
            $fieldCriteriaRow.find('.js-filter-control').hide();
            $fieldCriteriaRow.find('.js-filter-control-between').hide();
        }
        else if (isBetweenCompare) {
            $fieldCriteriaRow.find('.js-filter-control').hide();
            $fieldCriteriaRow.find('.js-filter-control-between').show();
        }
        else {
            $fieldCriteriaRow.find('.js-filter-control').show();
            $fieldCriteriaRow.find('.js-filter-control-between').hide();
        }
    }

    $('.js-filter-compare').each(function (e) {
        updateFilterControls(this);
    });

    $('.js-filter-compare').on("change", function () {
        updateFilterControls(this);
    });

    // handle property selection changes from the EntityFieldFilter
    $('select.entity-property-selection').on("change", function () {
        var $parentRow = $(this).closest('.js-filter-row');
        $parentRow.find('div.field-criteria').hide();
        $parentRow.find('div.field-criteria').eq($(this).find(':selected').index()).show();
    });

    // activity animation on filter field cootrol
    $('.filter-item > header').on("click", function () {
        $(this).siblings('.panel-body').slideToggle();
        $(this).children('div.pull-left').children('div').slideToggle();

        $expanded = $(this).children('input.filter-expanded');
        $expanded.val($expanded.val() == 'True' ? 'False' : 'True');

        $('a.filter-view-state > i', this).toggleClass('fa-chevron-down');
        $('a.filter-view-state > i', this).toggleClass('fa-chevron-up');
    });

    // fix so that the Remove button will fire its event, but not the parent event
    $('.filter-item a.btn-danger').on("click", function (event) {
        event.stopImmediatePropagation();
    });

    $('.filter-item-select').on("click", function (event) {
        event.stopImmediatePropagation();
    });
}

$(document).ready(function () {
    loadFunction();
    Sys.Application.add_load(loadFunction);
});

//
(function ($) {
    'use strict';
    window.Rock = window.Rock || {};

    Rock.reporting = (function () {
        var _reporting = {},
            exports = {
                //
                formatFilterForDateField: function (title, $selectedContent) {
                    var betweenMode = $('.js-filter-control-between', $selectedContent).is(':visible');
                    var dateValue = '';
                    if (betweenMode) {
                        return title + ' during ' + $('.js-slidingdaterange-text-value', $selectedContent).val();
                    } else {
                        var useCurrentDateOffset = $('.js-current-date-checkbox', $selectedContent).is(':checked');

                        if (useCurrentDateOffset) {
                            var daysOffset = $('.js-current-date-offset', $selectedContent).val();
                            if (daysOffset > 0) {
                                dateValue = 'Current Date plus ' + daysOffset + ' days';
                            }
                            else if (daysOffset < 0) {
                                dateValue = 'Current Date minus ' + -daysOffset + ' days';
                            }
                            else {
                                dateValue = 'Current Date';
                            }
                        }
                        else {
                            dateValue = $('.js-date-picker input', $selectedContent).filter(':visible').val();
                        }
                        return title + ' ' + $('.js-filter-compare', $selectedContent).find(':selected').text() + ' \'' + dateValue + '\''
                    }
                },

                //
                formatFilterForDateTimeField: function (title, $selectedContent) {
                    var betweenMode = $('.js-filter-control-between', $selectedContent).is(':visible');
                    var dateValue = '';
                    var timeValue = '';
                    if (betweenMode) {
                        return title + ' during ' + $('.js-slidingdaterange-text-value', $selectedContent).val();
                    } else {
                        var useCurrentDateOffset = $('.js-current-datetime-checkbox', $selectedContent).is(':checked');

                        if (useCurrentDateOffset) {
                            var minutesOffset = Number($('.js-current-datetime-offset', $selectedContent).val());
                            if (minutesOffset > 0) {
                                dateValue = 'Current Time plus ' + minutesOffset + ' minutes';
                            }
                            else if (minutesOffset < 0) {
                                dateValue = 'Current Time minus ' + -minutesOffset + ' minutes';
                            }
                            else {
                                dateValue = 'Current Time';
                            }
                        }
                        else {
                            dateValue = $('input.js-datetime-date', $selectedContent).filter(':visible').val() || '';
                            timeValue = $('input.js-datetime-time', $selectedContent).filter(':visible').val() || '';
                        }

                        var dateTimeValue = (dateValue + ' ' + timeValue).trim();
                        return title + ' ' + $('.js-filter-compare', $selectedContent).find(':selected').text() + ' \'' + dateTimeValue + '\''
                    }
                },

                //
                formatFilterForCheckBoxListFilterControl: function (title, $selectedContent) {
                    var selectedItems = '';
                    $('input:checked', $selectedContent).each(
                        function () {
                            selectedItems += selectedItems == '' ? '' : ' OR ';
                            selectedItems += ' \'' + $(this).parent().text() + '\'';
                        });

                    return title + ' is ' + selectedItems
                },

                //
                formatFilterForDefinedValueField: function (title, $selectedContent) {
                    return formatFilterForCheckBoxListFilterControl(title, $selectedContent);
                },

                // NOTE: this is specifically for the Rock.Reporting.DataFilter.OtherDataViewFilter (and similar) components
                formatFilterForOtherDataViewFilter: function (title, $selectedContent) {
                    var dataViewName = $('.js-dataview .js-item-name-value', $selectedContent).val();
                    return title + ' ' + dataViewName;
                },

                //
                formatFilterForSelectSingleField: function (title, $selectedContent) {
                    return formatFilterForCheckBoxListFilterControl(title, $selectedContent);
                },

                // NOTE: this is specifically for the Rock.Reporting.DataFilter.Person.InGroupFilter component
                formatFilterForGroupFilterField: function (title, $selectedContent) {
                    var groupNames = $('.js-group-picker', $selectedContent).find('.selected-names').text();
                    var checkedRoles = $('.js-roles', $selectedContent).find(':checked').closest('label');
                    var result = title + ' ' + groupNames;
                    var includeChildGroups = $('.js-include-child-groups', $selectedContent).is(':checked');
                    if (includeChildGroups) {

                        var includeDescendantGroups = $('.js-include-child-groups-descendants', $selectedContent).is(':checked');
                        var includeSelectedGroups = $('.js-include-selected-groups', $selectedContent).is(':checked');
                        var includeInactiveGroups = $('.js-include-inactive-groups', $selectedContent).is(':checked');
                        if (includeDescendantGroups) {
                            result = result + ' OR descendant groups';
                        } else {
                            result = result + ' OR child groups';
                        }

                        if (includeInactiveGroups) {
                            result += ", including inactive groups";
                        }

                        if (!includeSelectedGroups) {
                            result = result + ', NOT including selected groups';
                        }
                    }

                    if (checkedRoles.length > 0) {
                        var roleCommaList = checkedRoles.map(function () { { return $(this).text() } }).get().join(',');
                        result = result + ', with role(s): ' + roleCommaList;
                    }

                    var groupMemberStatus = $('.js-group-member-status option:selected', $selectedContent).text();
                    if (groupMemberStatus) {
                        result = result + ', with member status:' + groupMemberStatus;
                    }

                    var dateAddedDateRangeText = $('.js-dateadded-sliding-date-range .js-slidingdaterange-text-value', $selectedContent).val()
                    if (dateAddedDateRangeText) {
                      result = result + ', added to group in Date Range: ' + dateAddedDateRangeText;
                    }

                    var firstAttendanceDateRangeText = $('.js-firstattendance-sliding-date-range .js-slidingdaterange-text-value', $selectedContent).val()
                    if (firstAttendanceDateRangeText) {
                      result = result + ', first attendance to group in Date Range: ' + firstAttendanceDateRangeText;
                    }

                    var lastAttendanceDateRangeText = $('.js-lastattendance-sliding-date-range .js-slidingdaterange-text-value', $selectedContent).val()
                    if (lastAttendanceDateRangeText) {
                      result = result + ', last attendance to group in Date Range: ' + lastAttendanceDateRangeText;
                    }

                    return result;
                },

                // NOTE: this is specifically for the Rock.Reporting.DataFilter.Person.HasPhoneFilter component
                formatFilterForHasPhoneFilter: function ($content) {

                    var has;
                    if ($('.js-hasphoneoftype', $content).find(':selected').val() == "True") {
                        has = "Has ";
                    } else {
                        has = "Doesn't have ";
                    }

                    var phoneType = $('.js-phonetype', $content).find(':selected').text();
                    var sms = $('.js-hassms', $content).find(':selected').text();

                    if (sms == 'Yes') {
                        sms = ' AND has SMS Enabled';
                    }
                    else if (sms == 'No') {
                        sms = " AND doesn't have SMS Enabled";
                    }

                    var result = has + phoneType + sms;

                    return result;
                },

                //
                formatFilterDefault: function (title, $selectedContent) {
                    var compareTypeText = $('.js-filter-compare', $selectedContent).find(':selected').text();

                    var compareValueText = $('input[type=text].js-filter-control', $selectedContent).val(); // textbox value.
                    if (!compareValueText || compareValueText == "") {
                        compareValueText = $('.js-filter-control', $selectedContent).find(':selected').map(function () { return this.text; }).get().join("', '");
                    }
                    if (!compareValueText || compareValueText == "") {
                        compareValueText = $('.js-filter-control', $selectedContent).find(':checked').next().map(function () { return $(this).text(); }).get().join("', '");
                    }

                    var result = title;
                    if ($('.js-filter-control', $selectedContent).is(':visible')) {
                        result = title + ' ' + compareTypeText + " '" + compareValueText + "'";
                    } else {
                        result = title + ' ' + compareTypeText;
                    }

                    return result;
                }
            };

        return exports;
    }());
}(jQuery));
;
